设计模式更强调多个类/对象之间的关系和交互过程——比接口/类复用的粒度更大。设计模式有三种,创建型模式、结构型模式、行为类模式。
是一种虚拟构造器。
当client不知道要创建哪个具体类的实例,或者不想在client代码中指明要具体创建的实例时,用工厂方法。
定义一个用于创建对象的接口,让其子类来决定实例化哪一个类,从而使一个类的实例化延迟到其子类。例如,创建一个Factory
接口,具体的工厂去实现这个接口,可以创建多个工厂。
Client使用“工厂方法”来创建实例时,使用工厂Factory
接口去创建示例,由实现这个接口的具体工厂来决定实例化哪一个类。
有新的具体产品类加入时,可以在工厂类里修改或增加新的工厂函数(OCP),不会影响客户端代码。
静态工厂方法,既可以在ADT内部实现,也可以构造单独的工厂类。
将某个类/接口转换为client期望的其他形式。Adapter 模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。
通过增加一个接口,将已存在的子类封装起来,client面向接口编程,从而隐藏了具体子类。
适配器模式主要解决的问题是,在软件系统中,常常要将一些"现存的对象"放到新的环境中,而新环境要求的接口是现对象不能满足的。
适配器通俗点说就是一个转换器,你不是接口对不上吗,我给你加个转换器放中间就可以了。有些人可能会认为适配器没必要存在,的确不使用也能写出正确的程序,但是这是模块化编程的思想,是便于复用、维护的,使用它会变得更方便。
但是要注意,过多地使用适配器,会让系统非常零乱,不易整体进行把握。这是因为你把A接口的类偷偷适配成B接口的了,容易造成错误。
父类实现了共性操作,用每个子类实现不同的特性。在学习这一部分之前可能第一个想到的是继承重写的方式,但是继承会带来一个数学问题——组合爆炸,要实现的子类比较多就不行了,后续的开发很容易爆炸掉。
装饰器动态地给一个对象添加一些额外的职责。就增加功能来说,装饰器模式相比生成子类更为灵活。
例如,一个基础的stack如下。
现在想在它里面增加新功能。首先创造一个实现Stack
接口的抽象装饰类
然后写装饰类的具体实现
这样,就装饰完成了,使用时这样来实现。
Stack t = new SecureStack(new SynchronizedStack(new UndoStack(s));
有多种不同的算法来实现同一个任务,但需要client根据需要动态切换算法,而不是写死在代码里。
为不同的实现算法构造抽象接口,利用delegation,运行时动态传入client倾向的算法类实例。
选择不同算法时,一个很自然的想法就是使用条件判断if去选择,但是在有多种算法相似的情况下,使用 if-else 是复杂和难以维护的。
一个例子如下。为不同的算法(ConcreteStrategyA、B)创建接口Strategy,在Context
类中使用构造器Constructor
通过delegation与Strategy
接口进行绑定。
client在选择算法A时这样来使用。
Context context = new Context(new ConcreteStrategyA());
做事情的步骤一样,但具体方法不同,就像我用csdn的模板写这篇文章…
共性的步骤在抽象类内公共实现,差异化的步骤在各个子类中实现。
模板模式的实现依赖于继承和重写,定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
说白了就是把子类中通用的那些方法设置成模板方法,不让子类重写,比如方法前加个final
,而其他的方法在每个子类中不一样,需要重写。
客户端希望遍历被放入容器/集合类的一组ADT对象,无需关心容器的具体类型,也就是说,不管对象被放进哪里,都应该提供同样的遍历方式。
提供一种方法顺序访问一个聚合对象中各个元素, 而又无须暴露该对象的内部表示。
具体实现也比较简单,可以参考菜鸟教程。
在访问者模式(Visitor Pattern)中,使用一个访问者类,它改变了元素类的执行算法。通过这种方式,元素的执行算法可以随着访问者改变而改变。
本质上:将数据和作用于数据上的某种/些特定操作分离开来。
为ADT预留一个将来可扩展功能的“接入点”,外部实现的功能代码可以在不改变ADT本身的情况下通过delegation接入ADT。
一个例子如下:
本文总结了面向可复用性和可维护性的设计模式。