很早就接触了设计模式,今天在看一些文章的时候发现自己在学习理解设计模式的时候有些偏差。设计模式应该服务于特定的场景,并且是经过前人经验总结而来的代码解决思路,这种解决思路考虑的点在于如何能够让代码之间的耦合度更低并且减少冗余,使得代码耦合度更低的目的是为了程序更好的扩展,所谓的更好的扩展是指在功能的变动或者扩展的情况下尽可能触发较少较少的改动。所以在学习设计模式的时候,一个好的学习方式应该是模拟程序的扩展来比较在使用前人总结的设计模式和现有代码的不同之处,只有真正体会到其优点,才能尽得其精髓。
创建型模式
创建行模式抽象了对象的实例化过程,它帮助一个系统如独立于如何创建、组合和表示那些对象。
抽象工厂模式
抽象工厂模式用于创建具有不同产品族的工厂,所谓产品族是值得是多个不同对象的组合,不同对象值得是相对于实现同一接口的对象来说,所以同一产品族的对象肯定不是实现同一接口。
抽象工厂模式包括7个主要角色,A类抽象角色和A类具体角色(AbstractProductA,ProductA),B类抽象角色和B类具体角色(AbstractProductB,ProductB),抽象工厂(AbstractFactory)提供A类产品和B类产品的两个方法接口,具体工厂(ConcreteFactory)实现抽象工厂的两个方法接口,两个方法接口的实现依赖于A,B不同的产品(createProductA从A产品的实现类中挑选一个实现类,createProductB从B产品的实现类中挑选一个实现类)。客户短(Client)依赖于抽象工厂的具体实现类,生产一个具体工厂的产品族产品。
抽象工厂模式实现了多个产品的产品族工厂的提供,实现了多个产品的多维组合,一个典型的例子的产品族如UnixButton和WindowsButton。适用于不同产品组合的工厂提供。从扩展的角度来看,具体工厂的实现了抽象工厂,如果需要一个新的产品族,只要再实现一个产品族即可,实现了热插拔。对比工厂模式,具体工厂面临的是一个产品,而抽象工厂面临的是一系列产品。
建造者模式
建造者模式的作用是将一个对象的构建步骤抽象出来,使得易于重写一个对象的构建过程。
建造者模式包括4个主要角色,建造者接口(Builder),具体建造者(ConcreteBuilder),产品对象(Product)和导演类(Director)。导演类的construct方法负责调用抽象建造者的多个buildPart方法进行组装,Builder接口定义多个部分组建过程buildPart()方法和组建结果方法retrieveResult()方法供具体建造者实现,ConcreteBuilder实现部分组建方法和结果返回方法,具体的建造者完成对应的组装任务,并且使用retrieveResult获取对应的组装结果。
从目的上来看,建造者模式实现了对象的不同组装方式的最简实现方式,组建的顺序由导演类控制,而具体的组装任务分布在不同的构建者手里,导演类和抽象建造者的聚合关系使得产品构建的实际操作可以实现热插拔,使得构建的具体实现更容易扩展或替换。反观,如果使用导演类直接操作产品实现产品的组装过程,如果需要更改建造的过程,那么必须要修改导演类,这是我们不想看到的。
结构型模式
结构型模式侧重于解决如何组装现有的类,设计他们之间的交互,以达到一定的目的,比如扩展性,封装性。
适配器模式
适配器模式的目的是将一个类的方法接口转换成另一个客户端将要使用的另一个方法接口。
适配器模式含有三个主要角色,客户端需要的目标接口(Target)及其方法sampleOperation();被适配的类Adaptee,它是实际的执行者;适配器Adapter,Adapter继承了Target接口,并且继承了Adaptee,Adapter的sampleOperation方法调用Adptee的sampleOperation方法。实际上,对Adaptee类方法的调用可以使用继承的方式,也可以使用直接依赖的方式,前者被称为类适配器,后者被称为对象适配器。
适配器模式实现了一个接口到另一个接口之间的兼容性转换。在扩展性方面并无特别的地方。
装饰器模式
装饰器模式又称为包装模式,其目的是在不改变原有继承关系的情况下,动态的扩展类的功能。这里所说的扩展的类的功能可以是已有方法的改变,也可以是提供更多的方法。
装饰器模式含有4个主要角色,系统已经存在的继承关系,组件接口(Component)和其实现类(ConcreteComponent),装饰者(Decorator)实现了组件接口依赖于被装饰的具体实现类,其sampleOperateration()方法依赖于被装饰的具体组建类的sampleOperation()方法,然后具体的装饰者(ConcreateDecorator)继承自Decorator,可以实现增加其他方法或者修改对应的sampleOperation方法,从而实现了不改变原有类并且实现继承同样的动态方法扩充,并且对于客户端来说这种扩充是透明的。
从扩展的角度来看,Decorator代替的原来ConCreteComponent的位置,并且替代其完成继承的动态扩充,相比直接继承自ConcreteComponent,这种方法的依赖性更小,其装饰的对象也是可以动态插拔的,并且ConcreteComponent并需要知道装饰类的存在,免去复杂类关系的维护。
一个应用的具体例子就是BufferInputStream和FileInputStream之间的关系,InputStream是Component, FileInputStream、ByteArrayInputStream等是具体组件,FilterInputStream是装饰者,BufferInputStream等继承自FilterInputStream,是具体的装饰者。
代理模式
代理模式的目的是为一个对象提供一个代理,代理对象可以增强,修改甚至删除原有的功能。
因为代理模式应用十分广泛,所以代理模式已经在另一篇博客中详加叙述了,这里不再累赘。
结构型模式
结构型设计模式专注于类或对象之间的职责分配,研究的是多个类或对象如何高效的合作来完成一个任务。
观察者模式
观察者模式的目的是实现一种一对多的依赖关系,使得主题更新的时候能够将这种更新以特定的方式通知与主题绑定的观察者。
观察者模式包含4个参与者,主题接口和具体实现主题(Subject,ConcreteSubject),观察者和具体观察者(Observer,ConcreateObserver)。observer通过聚合方式注册到Subject中,由于依赖关系定义接口中,具体主题可以实现不同的通知策略,并且具体观察者和具体主题之间可以实现低耦合依赖。
两者之间的依赖关系可以通过attach和detach方法进行建立和解除,subject在状态改变时可以通过notifyObserver通知所有的observers,调用对应observer的update方法。
从扩展的角度来看,观察者模式的好处在于在新增observer的时候只需要实现observer接口,然后在想要监听的具体subject中attach就实现了添加;同样的,如果想要删除一个subject中的一个observer也仅仅需要调用attach方法,实现了热插拔。如果不采用这种设计方式,任何一方不适用接口都会导致一方的添加困难。
中介者模式
中介者模式的作用是使用一个中介对象封装一系列对象的交互,使得各个对象的交互不需要显示的相互依赖。简单来讲,就是让不同对象之间的交互由多对多的网状关联转换成一对多的星状关联。
中介者模式由四个主要角色来构建,中介者接口(Mediator)定义公用交互方法changed(); 具体中介者(ConcreteMediator)实现具体的交互方法changed(),并且获取需要持有需要通信的colleague的引用,changed()方法的具体实现依赖于持有的colleague引用; 同事抽象类(Colleague)定义同事接口对中介者接口的关联关系,并且提供获取中介者的接口方法; 具体的同事实现类(ConcreteColleague)可能有多个,主要是实现具体的交互操作,交互操作依赖于其关联的中介者的changed方法。至于为什么说是变成了一对多的关系,从上面四个角色的关系来看,每个colleague都持有一个mediator的引用,而每个mediator持有所有colleage的引用。
中介者模式优点在于解耦复杂系统对象的交互,使得再增加或者删除colleage时仅需要修改中间的中介者。中介者也可以替换成另一个具体中介者,替换相应的交互行为。缺点之处在于随着需要交互同事的不断增加,具体中介者也会不断膨胀。