观察者模式(Observer Pattern)是对象之间一对多的依赖关系,当一个对象改变时,其他依赖它的对象都会收到通知并自动更新。

怎么来理解这句话呢?用微信朋友圈来举个例子,假如你就是被依赖的对象,你的好友都依赖你,这样的关系就形成了一对多,当你发朋友圈的时候你的好友都能收到通知,并且自动更新。

观察者模式是一个比较简单的模式,它的核心很简单,只有两个角色,一个是被依赖的对象,在朋友圈例子里面就是你自己,这个叫主题或者被观察者,另一个对象是你的朋友叫观察者或者订阅者,为了统一认知,后面都叫主题(被观察者)和订阅者(观察者)。

类图

类图不是目的,只是方便理解

观察者模式

从类图上可以看到,主题不关心其他订阅者的实现,只关心Observer接口,所有实现了Observer接口并订阅了主题的对象都能在主题发生变化的时候得到通知。

实例

现在来实现一个汽车仪表盘,汽车在行驶过程中转速和速度都会一直处于变化中,我们现在通过观察者模式把转速和速度显示到仪表盘上。
首先,模拟一辆汽车从0-100加速的过程,这个过程中拿到汽车把实时数据:

/**
 * 汽车回调数据,这里会根据汽车的速度变化,持续的传递转速和速度
 */
fun carInfo(power:Float, speed: Float)
{

}
/**
 * 汽车引擎,模拟汽车从0-100加速
 */
for (speed in 0..1000)
{
    Thread.sleep(10)
    carInfo(speed/200f + Random().nextInt(2), speed.toFloat()/10)
}
// 部分汽车数据打印
power: 0.0   speed: 0.0
power: 0.005   speed: 0.1
power: 0.01   speed: 0.2
power: 0.015   speed: 0.3
power: 0.02   speed: 0.4
power: 1.025   speed: 0.5
power: 1.03   speed: 0.6
power: 1.035   speed: 0.7
power: 0.04   speed: 0.8
power: 0.045   speed: 0.9
power: 0.05   speed: 1.0
power: 1.055   speed: 1.1

既然要显示到仪表盘,现在还差一个仪表盘Display用于显示速度和转速:

/**
 * 仪表盘
 * Created by Carlton on 2016/11/9.
 */
class Display
{
    var power:Float = 0f
    var speed:Float = 0f

    fun display()
    {
        println("汽车当前的 转速:$power  速度:$speed")
    }
}

现在我们把速度变化数据通过仪表盘Display展示出来:

val display = Display()
/**
 * 汽车回调数据,这里会根据汽车的速度变化,持续的传递转速和速度
 */
fun carInfo(power:Float, speed: Float)
{
    display.power = power
    display.speed = speed
    display.display()
}
// 仪表盘数据
……
汽车当前的 转速:3.985  速度:79.7
汽车当前的 转速:4.99  速度:79.8
汽车当前的 转速:3.995  速度:79.9
汽车当前的 转速:4.0  速度:80.0
汽车当前的 转速:5.005  速度:80.1
汽车当前的 转速:5.01  速度:80.2
汽车当前的 转速:4.015  速度:80.3
汽车当前的 转速:4.02  速度:80.4
汽车当前的 转速:5.025  速度:80.5
汽车当前的 转速:5.03  速度:80.6
……

现在我们就实现了一个简易的汽车仪表盘展示数据,这样写有什么问题呢?如果我们给汽车扩展一个后视镜显示速度,中控台展示速度,我们又需要来修改carInfo()去设置和显示后视镜、中控台,显然不符合设计原则
现在我们知道整个系统中有两个角色:汽车变化的数据、仪表盘。按照观察者模式,把汽车变化的数据定义成主题(被观察者),仪表盘定义订阅者(观察者),然后用观察者模式重构整个系统。
首先,实现观察者接口:

/**
 * 主题,有的地方叫观察者Observable
 * @param T 更新的数据回调
 * Created by Carlton on 2016/11/9.
 */
interface Subject<T>
{
    /**
     * 注册成为观察者
     */
    fun registeObserver(observer: Observer<T>)

    /**
     * 删除观察者
     */
    fun removeObserver(observer: Observer<T>)

    /**
     * 通知观察者数据已经发生了变化
     */
    fun notifyObservers(value: T)
}

/**
 * 观察者
 * @param T 观察者回调的数据类型
 * Created by Carlton on 2016/11/9.
 */
interface Observer<in T>
{
    /**
     * 数据更新
     */
    fun update(value: T)
}

接下来把数据封装成一个主题(被观察者CarSubject:

/**
 * 具体的主题,被观察者
 * Created by Carlton on 2016/11/9.
 */
class CarSubject : Subject<Array<Float>>
{
    /**
     * 观察者
     */
    val observers = ArrayList<Observer<Array<Float>>>()
    override fun registerObserver(observer: Observer<Array<Float>>)
    {
        observers.add(observer)
    }

    override fun removeObserver(observer: Observer<Array<Float>>)
    {
        if(observers.contains(observer))
        {
            observers.remove(observer)
        }
    }

    override fun notifyObservers(value: Array<Float>)
    {
        for (observer in observers)
        {
            observer.update(value)
        }
    }
}

现在改造一下我们的系统,把数据用主题绑定起来:

val carSubject = CarSubject()

/**
 * 汽车回调数据,这里会根据汽车的速度变化,持续的传递转速和速度
 */
fun carInfo(power:Float, speed: Float)
{
    carSubject.notifyObservers(arrayOf(power, speed))
}

到这里我们实现了一个可扩展的观察者模式系统,观察者模式中的接口部分一般都是固定的,包括java里面都有支持观察者模式,后面会说道,所以如果我们要实现一个观察者模式,接口部分我们只需要实现一次,或者直接使用java api提供的接口,主要需要实现主题或者观察者接口。

在上面的例子中,我们自己提供了观察者接口SubjectObserver,接着我们实现了一个主题CarSubject用于封装汽车变化的数据,提供给其他对这个数据感兴趣的观察者们。那么,现在把仪表盘做为观察者,去订阅主题,修改一下之前的Display:

/**
 * 仪表盘,传递的数据是一个数组,0下标存的是转速,1下标存的是速度
 * Created by Carlton on 2016/11/9.
 */
class Display : Observer<Array<Float>>
{
    override fun update(value: Array<Float>)
    {
        power = value[0]
        speed = value[1]
        display()
    }

    var power:Float = 0f
    var speed:Float = 0f

    fun display()
    {
        println("汽车当前的 转速:$power  速度:$speed")
    }
}

运行系统:

val carSubject = CarSubject()
// 添加仪表盘观察者
carSubject.registerObserver(Display())

// 仪表盘显示
……
汽车当前的 转速:4.525  速度:90.5
汽车当前的 转速:4.53  速度:90.6
汽车当前的 转速:4.535  速度:90.7
汽车当前的 转速:5.54  速度:90.8
汽车当前的 转速:5.545  速度:90.9
汽车当前的 转速:4.55  速度:91.0
汽车当前的 转速:4.555  速度:91.1
汽车当前的 转速:4.56  速度:91.2
汽车当前的 转速:4.565  速度:91.3
……

如果现在仪表盘不需要监听主题的数据了,可以调用carSubject.removeObserver()移除对象,这样主题数据发生变化后,就不会通知到这个观察者对象。

接下来,添加中控台和后视镜的数据显示,把中控台和后视镜当成观察者去订阅CarSubject

这里解答一个疑惑,为什么观察者去订阅主题,反而要把订阅和移除订阅的方法放到主题里面而不是观察者里面,这样也很好理解啊?主要原因是现实世界和程序世界还是有区别,如果我们把这两个方法按照现实的理解放到观察者里面,代码会变得比较复杂,没有现在这种实现方式简单明确,有兴趣的可以自己去按照现实的理解方式实现一个。设计模式只是一种编程思想,不是编程的形式,理解到思想就行了。

新添加两个观察者,中控台(CenterConsoleDisplay)、后视镜(RearviewBack):

/**
 * 中控台
 * Created by Carlton on 2016/11/9.
 */
class CenterConsoleDisplay : Observer<Array<Float>>
{
    override fun update(value: Array<Float>)
    {
        println("中控台显示的速度:${value[1]}")
    }
}

/**
 * 后视镜
 * Created by Carlton on 2016/11/9.
 */
class RearviewBack : Observer<Array<Float>>
{
    override fun update(value: Array<Float>)
    {
        println("后视镜显示: 速度 - ${value[1]}  转速 - ${value[0]}")
    }
}

订阅这两个新的,启动:

val carSubject = CarSubject()
// 添加仪表盘观察者
carSubject.registerObserver(Display())
// 添加中控台
carSubject.registerObserver(CenterConsoleDisplay())
// 添加后视镜
carSubject.registerObserver(RearviewBack())

// 各个地方的数据展示
……
中控台显示的速度:98.9
后视镜显示: 速度 - 98.9  转速 - 4.945
汽车当前的 转速:4.95  速度:99.0
中控台显示的速度:99.0
后视镜显示: 速度 - 99.0  转速 - 4.95
汽车当前的 转速:4.955  速度:99.1
中控台显示的速度:99.1
后视镜显示: 速度 - 99.1  转速 - 4.955
汽车当前的 转速:5.96  速度:99.2
中控台显示的速度:99.2
后视镜显示: 速度 - 99.2  转速 - 5.96
……

数据的推和拉

观察者模式中获取数据的方式有两种,一种是推给观察者,一种是观察者根据需要自己拉,有什么区别呢?如果是推的方式不管观察者对这部分信息是否感兴趣都会推给观察者,有冗余数据比如我们的中控台(CenterConsoleDisplay)只对速度感兴趣。如果是用拉的方式呢?这样就能根据观察者自己的需要获取想要的数据,Java里面两种方式都支持,下面改造一下通过拉的方式实现数据传递,这样的话CarSubject也就是主题需要暴露一些获取数据的方法:

/**
 * 观察者
 * @param T 观察者回调的数据类型
 * Created by Carlton on 2016/11/9.
 */
interface Observer<T>
{
    /**
     * 数据更新
     */
    fun update(value: T)
    // ------------------   变化的部分   --------------------------
    /**
     * 重载一个方法,让观察者可以用拉的方式获取数据
     */
    fun update(subject: Subject<T>)
    // ------------------   变化的部分   --------------------------
}

/**
 * 主题,有的地方叫观察者Observable
 * @param T 更新的数据回调
 * Created by Carlton on 2016/11/9.
 */
interface Subject<T>
{
    /**
     * 注册成为观察者
     */
    fun registerObserver(observer: Observer<T>)

    /**
     * 删除观察者
     */
    fun removeObserver(observer: Observer<T>)

    /**
     * 通知观察者数据已经发生了变化
     */
    fun notifyObservers(value: T)
    // ------------------   变化的部分   --------------------------
    /**
     * 通知观察者数据已经发生了变化,可以拉数据了
     */
    fun notifyObservers()
    // ------------------   变化的部分   --------------------------
}

/**
 * 具体的主题,被观察者
 * Created by Carlton on 2016/11/9.
 */
class CarSubject : Subject<Array<Float>>
{
    // ------------------   变化的部分   --------------------------
    var speed:Float = 0f
    var power:Float = 0f
    override fun notifyObservers()
    {
        for (observer in observers)
        {
            observer.update(this)
        }
    }
    // ------------------   变化的部分   --------------------------

    /**
     * 观察者
     */
    val observers = ArrayList<Observer<Array<Float>>>()
    override fun registerObserver(observer: Observer<Array<Float>>)
    {
        observers.add(observer)
    }

    override fun removeObserver(observer: Observer<Array<Float>>)
    {
        if(observers.contains(observer))
        {
            observers.remove(observer)
        }
    }

    override fun notifyObservers(value: Array<Float>)
    {
        for (observer in observers)
        {
            observer.update(value)
        }
    }
}

/**
 * 中控台
 * Created by Carlton on 2016/11/9.
 */
class CenterConsoleDisplay : Observer<Array<Float>>
{
    // ------------------   变化的部分   --------------------------
    override fun update(subject: Subject<Array<Float>>)
    {
        val carSubject:CarSubject = subject as CarSubject
        println("中控台显示的速度:${carSubject.speed}")
    }
    // ------------------   变化的部分   --------------------------

    override fun update(value: Array<Float>)
    {
        println("中控台显示的速度:${value[1]}")
    }
}

/**
 * 汽车回调数据,这里会根据汽车的速度变化,持续的传递转速和速度
 */
fun carInfo(power:Float, speed: Float)
{
    // ------------------   变化的部分   --------------------------
    carSubject.power = power
    carSubject.speed = speed
    carSubject.notifyObservers()
    // ------------------   变化的部分   --------------------------
    carSubject.notifyObservers(arrayOf(power, speed))
}

更灵活的设计

不知道大家发现一个问题没有,打印出来的速度和转速都是有小数,正常情况下速度表变化都是整数每次变化为1,为了说明简单的用速度和转速大于2的时候才通知观察者来代替这个需求。如果我们要处理这个问题可以在观察者对象中拿到数据后处理,不过观察者模式有一个比较优雅的处理方式setChanged(),有什么用呢?用来标记数据是否发生了不变化如果没有发生变化则不通知观察者,这样我们就能控制何时通知观察者,现在改造一下CarSubject,并在Subject中新增接口setChanged()

/**
 * 具体的主题,被观察者
 * Created by Carlton on 2016/11/9.
 */
class CarSubject : Subject<Array<Float>>
{
    var speed:Float = 0f
    var power:Float = 0f
    override fun notifyObservers()
    {
        if (!isChanged)
        {
            return
        }
        for (observer in observers)
        {
            observer.update(this)
        }
        isChanged = false
    }

    /**
     * 数据是否发生变化
     */
    var isChanged: Boolean = false
    override fun setChanged()
    {
        isChanged = true
    }
    /**
     * 观察者
     */
    val observers = ArrayList<Observer<Array<Float>>>()
    override fun registerObserver(observer: Observer<Array<Float>>)
    {
        observers.add(observer)
    }

    override fun removeObserver(observer: Observer<Array<Float>>)
    {
        if(observers.contains(observer))
        {
            observers.remove(observer)
        }
    }

    override fun notifyObservers(value: Array<Float>)
    {
        if (!isChanged)
        {
            return
        }
        for (observer in observers)
        {
            observer.update(value)
        }
        isChanged = false
    }
}

现在在通知观察者之前我们必须设置数据更新标志调用setChanged(),这里我们让转速和速度都大于2的时候才通知观察者:

/**
 * 汽车回调数据,这里会根据汽车的速度变化,持续的传递转速和速度
 */
fun carInfo(power:Float, speed: Float)
{
    carSubject.power = power
    carSubject.speed = speed
    // 这里我们可以让速度和转速大于2的时候才通知观察者
    if (carSubject.power > 2 && carSubject.speed > 2)
    {
        carSubject.setChanged()
    }
    carSubject.notifyObservers()
    carSubject.notifyObservers(arrayOf(power, speed))
}

Java里面的观察者

Java里面提供了一个被观察者类:java.util.Observable这不是一个接口,里面有具体的实现,跟我们的CarSubject一样,已经做好了添加观察者、重置标志符等功能,需要的时候直接继承。还有一个java.util.Observer观察者接口,跟我们上面的是一样的。Java把java.util.Observable定义成一个类,主要是流程化了主题功能,不像用接口的时候需要自己实现添加观察者等功能,这样也有一个问题就是扩展性变差了,因为接口总是比类要灵活。如果用Java自带的观察者API来实现我们的系统,只需要用CarSubject来继承java.util.Observable就可以了,CarSubject中就不需要再去实现这些方法了,因为父类已经实现好了:

/**
 * 注册成为观察者
 */
fun registerObserver(observer: Observer<T>)

/**
 * 删除观察者
 */
fun removeObserver(observer: Observer<T>)

/**
 * 通知观察者数据已经发生了变化
 */
fun notifyObservers(value: T)

/**
 * 通知观察者数据已经发生了变化,可以拉数据了
 */
fun notifyObservers()

/**
 * 标记数据变化
 */
fun setChanged()

MVC说几句

MVC有很多实现方式,但是用观察者模式实现是我觉得最好用的方式,我们把V想成订阅者,把M实现成主题,这样的话,当我们的Model中有数据变化的时候就可以直接通知到View,让View用数据来更新界面,这样写出来的架构更容易理解和解耦。

总结

观察者模式主要有两个角色,一个是观察者,一个是被观察者,所有观察者都能够收到被观察者数据变化的通知,如果某个观察者不在关心主题的数据了,也可以从被观察者的列表中删除这个观察者,这样它就不会收到通知了。如果使用Java,没有特别的需求情况下,不建议自己实现观察者接口,而是直接使用Java API。观察者有很多可以应用的地方,非常有用的一种编程思路,比如RxJava里面等等很多框架都有观察者模式。观察者模式在项目中是经常使用的一种模式,当明白它的核心思想后,在项目中能帮助我们实现更好维护的代码。

设计模式笔记本 【传送门】

不登高山,不知天之高也;不临深溪,不知地之厚也
感谢指点、交流、喜欢

装饰者模式(Decorator Pattern)是在不必改变原类文件和使用继承的情况下,动态地扩展一个对象的功能,它是通过创建一个包装对象,也就是装饰来包裹真实的对象。
装饰者是代替继承扩展功能另外一种方式,装饰者模式遵循了多用组合少用继承和对扩展开放对修改关闭的设计原则。

这一次用两个例子来说明,一个关于车的,一个是项目实际中经常用到的验证问题。
假如你有一天醒了,起来发现有人送了一辆法拉利488,那么有个性的你怎么会只满足车辆本身的性能和外观,这时候你会想着按照自己的意愿修改也就是改造。改了颜色、花纹、排气管等等要再改就比较麻烦了,既然是做梦,索性就做一点智能的东西,随意修改改装配件,还不用破坏车子本来的样子,接下来就用装饰者模式来做做梦~

类图

类图不是目的,只是方便理解

装饰者模式

前面提到了装饰者模式是创建包装对象,所以装饰者模式里面的类不管是具体的组件还是装饰者组件都是实现同样的接口或者类。
Component是装饰的装饰接口,ConcreteComponent是具体的装饰组件,DecoratorComponent是装饰者组件,装饰者模式的核心就是用装饰者去装饰具体的组件,同样的一个装饰者组件也可以当成被装饰的具体组件。
在这个类图中,可以用DecorateComponentA或者DecorateComponentB去装饰ConcreteComponentDecorateComponentADecorateComponentB,你要是非常丧心病狂也可以用DecorateComponentA装饰自己。

接下来实现自己的汽车智能改装

实例

现在我们用之前的知识梳理一下如何实现一个汽车的智能改装。
首先,需要一个Component也就是组件接口,因为我们是定义的改装所以定义成IUpdate,里面有一个叫refit()改装的行为:

/**
 * 改装,装饰者的装饰接口
 * Created by Carlton on 2016/11/2.
 */
interface IUpdate
{
    /**
     * 改装
     */
    fun refit() : String
}

有了装饰者接口,现在还需要一个被装饰的具体装饰组件,要不然妆化那么好,穿那么漂亮给谁看呢?女为悦己者容,所以得先要有一个服务的对象ConcreteComponent,这里定义成Car,这是别人送给你的4s店原装无改动的法拉利 488:

/**
 * 汽车, 被装饰的组件
 * Created by Carlton on 2016/11/2.
 */
class Car(var name: String) : IUpdate
{
    override fun refit(): String
    {
        return ""
    }

    override fun toString(): String
    {
        return "Car(name='$name')"
    }
}

现在有了具体的目标,接下来就需要很多额外的配件来改装,我们想改变汽车的颜色、速度、是否需要喷火,所以先定义4个类:RedColor、BlueColor、Speed、Fire:

/**
 * 红色改装
 * Created by Carlton on 2016/11/2.
 */
class RedColor(update: IUpdate) : DecoratorUpdate(update)
{
    override fun refit(): String
    {
        return "${update.refit()} - 红色的"
    }
}

/**
 * 蓝色改装
 * Created by Carlton on 2016/11/2.
 */
class BlueColor(update: IUpdate) : DecoratorUpdate(update)
{
    override fun refit(): String
    {
        return "${update.refit()} - 蓝色的"
    }
}

/**
 * 速度装饰者
 * Created by Carlton on 2016/11/2.
 */
class Speed(update: IUpdate) : DecoratorUpdate(update)
{
    override fun refit(): String
    {
        return "${update.refit()} - 速度提升一倍"
    }
}

/**
 * 能喷火的装饰者
 * Created by Carlton on 2016/11/2.
 */
class Fire(update: IUpdate) : DecoratorUpdate(update)
{
    override fun refit(): String
    {
        return "${update.refit()} - 能喷火"
    }

    fun fire(): String
    {
        return "${refit()}:氮气"
    }
}

注意这里的Fire类,里面多了一个方法叫fire(),功能是喷氮气。这是装饰者模式扩展的汽车原来的行为,可以给每一个装饰者组件添加多余的功能。

现在,汽车和原材料都准备好了,那么:

  • 我想要一辆红色、能喷氮气、速度极快的车车
  • 我想要一辆蓝色、速度极快的车车
  • 我想要一辆蓝色、速度、速度的车车
//红色、氮气、速度提升一倍的Ferrari
val car:Car = Car("Ferrari 488")
val refit = Fire(Speed(RedColor(car))).fire()
println("$car $refit")
//  Car(name='Ferrari 488')  - 红色的 - 速度提升一倍 - 能喷火:氮气


// 蓝色、速度极快的
val car:Car = Car("Ferrari 488")
val refit = Speed(BlueColor(car)).refit()
println("$car $refit")
//  Car(name='Ferrari 488')  - 蓝色的 - 速度提升一倍

// 蓝色、速度、速度
val car:Car = Car("Ferrari 488")
val refit = Speed(Speed(BlueColor(car))).refit()
println("$car $refit")
//  Car(name='Ferrari 488')  - 蓝色的 - 速度提升一倍 - 速度提升一倍

既然如此,那我买一辆自行车也能改装嘛,所以我们现在新增一个具体的装饰者组件Bike自行车:

/**
 * 自行车, 具体的装饰者组件
 * Created by Carlton on 2016/11/2.
 */
class Bike(var name: String) : IUpdate
{
    override fun refit(): String
    {
        return ""
    }

    override fun toString(): String
    {
        return "Bike(name='$name')"
    }
}

一辆蓝色、能喷火、速度提升的自行车由此诞生:

val bike:Bike = Bike("凤凰牌自行车")
val refitBike = Speed(Fire(BlueColor(bike))).refit()
println("$bike $refitBike")
//  Bike(name='凤凰牌自行车')  - 蓝色的 - 能喷火 - 速度提升一倍

例子的类图:

类图不是目的,只是方便理解

装饰者模式-例子

在这个例子中CarBike都是具体的装饰者组件,而继承自DecoratorUpdate的都是装饰者组件,可以自由扩展其他功能,比如Fire中的fire(),装饰者组件本身也可以被装饰。

项目应用

以下代码为Java实现

我们经常会遇到一个需求就是对前端的字段验证,比如字段是不是为空,手机号格式是否正确等等,如果对字段的判断需要新增规则用装饰者模式就能愉快的解决这个问题,接下来实现一个验证的装饰者,对字段的验证通过装饰者来实现。我们实现一个正则表达式的装饰者和一个函数方法的装饰者(验证字段可以自定义规则)。
首先,我们需要一个装饰者的接口:

/**
 * 参数验证接口
 * Created by Carlton on 2016/10/31.
 */
public interface IParamValidate<T>
{
    /**
     * 验证是否通过,先验证自身的条件再验证装饰者。先验证被装饰者,在验证自身的条件。
     *
     * @param value 被验证的字段
     *
     * @return 如果是true验证通过,如果是false验证失败。如果value==null,返回false
     */
    public boolean validate(T value);
}

然后,实现一个具体的装饰对象EmptyValidate

/**
 * 字段为空的验证,也是装饰者中的被装饰的对象.
 * 如果数据类型是null 返回false,如果数据类型是CharSequence则使用{@link TextUtils#isEmpty(CharSequence)}判断字符是否为空。
 * 所有的true代表验证通过,false代表验证不通过。
 * Created by Carlton on 2016/11/1.
 */
public class EmptyValidate<T> implements IParamValidate<T>
{
    @Override
    public boolean validate(T value)
    {
        return value != null && (!(value instanceof CharSequence) || !TextUtils.isEmpty((CharSequence) value));
    }
}

有了具体的装饰对象,现在我们实现一个正则表达式的验证和一个函数验证的装饰者:

/**
 * 验证的接口,用于扩展自定义的验证方式。
 * 实现的返回必须遵循下面的规则:
 * <ul>
 * <li>
 * 1、如果value==null则返回false。
 * </li>
 * <li>
 * 2、如果返回true则代表验证通过,如果返回false则代表验证失败。
 * </li>
 * </ul>
 * Created by Carlton on 2016/11/1.
 */
public interface IValidator<T>
{
    /**
     * 自定义的验证接口方法
     *
     * @param value 被验证的值
     *
     * @return true是验证通过,false验证失败
     */
    public boolean validate(T value);
}

/**
 * 函数验证,通过一个自定义的函数来实现验证。如果Validator==null 返回自身验证返回true。
 * Created by Carlton on 2016/11/1.
 */
public class FunctionValidate<T> extends ValidateDecorator<T>
{
    private IValidator<T> mValidator;

    public FunctionValidate(IParamValidate<T> validate, IValidator<T> validator)
    {
        super(validate);
        mValidator = validator;
    }

    private boolean selfValidate(T value)
    {
        return mValidator == null || mValidator.validate(value);
    }

    @Override
    public boolean validate(T value)
    {
        return getValidate().validate(value) && selfValidate(value);
    }
}

/**
 * 正则表达式验证。使用一个正则表达是来匹配验证。
 * 如果value == null返回false。
 * 如果是true验证通过,如果是false验证不通过。
 * Created by Carlton on 2016/11/1.
 */
public class RexValidate extends ValidateDecorator<CharSequence>
{
    private String mPatternString = "\\.";
    public RexValidate(String patternString, IParamValidate<CharSequence> validate)
    {
        super(validate);
        mPatternString = patternString;
    }
    private boolean selfValidate(CharSequence value)
    {
        if(value == null)
        {
            return false;
        }
        Pattern pattern = Pattern.compile(mPatternString);
        Matcher matcher = pattern.matcher(value);
        return matcher.find();
    }

    @Override
    public boolean validate(CharSequence value)
    {
        return getValidate().validate(value) && selfValidate(value);
    }
}

IValidator是函数验证的接口,然后就是怎么使用。
– 验证一个字段是否是空、是否匹配正则“abc”、首字母是否是C

实现代码:

public static void main(String[] args)
{
    String validateValue = "这是被验证的字段";
    // 验证是否是空
    EmptyValidate<CharSequence> emptyValidate = new EmptyValidate<>();
    // 验证是否通过正则表达式'abc'
    RexValidate rxValidate = new RexValidate("abc", emptyValidate);
    // 验证自定义函数,首字母是否是C
    boolean result = new FunctionValidate<>(rxValidate, new IValidator<CharSequence>() {
        @Override
        public boolean validate(CharSequence value)
        {
            return !(value == null || value.length() > 0) && value.charAt(0) == 'C';
        }
    }).validate(validateValue);
    System.out.println(result);
}

同样的这些规则可以任意组合,自己也能实现其他的验证规则,比如可以把手机号验证直接实现成一个装饰者。

总结

装饰者模式解决的问题是动态的将责任附加到对象上,若要扩展功能,装饰者提供了比继承更有弹性的替代方案。万物都是平衡的,装饰者也有它自己的缺点,主要就是会产生很多类,在使用的时候不要无脑套用,需要自己权衡是否合适。另外java中的I/O Stream是用装饰者实现的,本来想扩展一个IO Stream对象,想来比较画蛇添足,有兴趣的可以去了解一下。

设计模式笔记本 【传送门】

不登高山,不知天之高也;不临深溪,不知地之厚也
感谢指点、交流、喜欢