漫谈设计模式–GOF23种设计模式

2014/03/25

本文原创作者:Cloud Chou. 欢迎转载,请注明出处和本文链接

GOF共23种设计模式,实际应用中还有一种非常简单的设计模式,并且经常被使用,它就是简单工厂。现将对这24种设计模式的体会记录如下:

  • 1)简单工厂

    创建型设计模式,本质是选择实现,在client端只需对接口编程,通过简单工厂可获得接口的实现对象,通常会在获得接口对象的方法上设置参数,根据不同的参数实例化不同的接口实现对象。这样把选择接口实现对象的工作交给了工厂,client端只需获取接口实现对象,并对接口编程即可,简化了client端的逻辑。

  • 2)外观模式

    结构型设计模式,本质是封装交互,简化调用。子系统内部有多个模块,其它系统与该系统交互时,直接与这个外观对象交互,再由这个外观对象去调用系统内部多个模块实现其它系统调用需要的功能。该模式很明显体现了最少知识原则。我们在封装动态链接库(dll或者lib.so)时,通常会为这个动态链接库设计一个外观类,并且这个外观类通常设计为单例,其它动态链接库或者可执行文件调用该动态链接库时,只需和这个外观对象交互即可。

  • 3)适配器模式

    结构型设计模式,本质是转换匹配,复用功能。使用适配器模式的场景主要是,当前系统接口与新要求的接口不一致,但是可以使用当前系统实现新要求的接口的功能,这样可以不用重新开发。于是设计一个适配器,组合一个现有系统的接口实现对象,并实现新接口,在新接口方法里转调现有系统接口实现对象完成功能。但是尽量不要使用该设计模式,如果有现有系统的源码,并对它很熟悉,时间也宽裕,可考虑重构现有系统,实现新接口。

  • 4)单例模式

    创建型设计模式,本质是控制实例数目。单例模式使用的场景非常广泛,当我们想要控制某个类在系统类只能有唯一的实例时,就会使用该模式。该模式会将构造器方法私有化,并声明一个静态的该类型的字段,另外提供一个静态方法获取静态实例,这样便可获得唯一实例。这是饿汉式的实现,还有懒汉式的实现,考虑到多线程,需要用双重检查加锁的方式来实现,还可以用静态内部类或者枚举类实现。在设计Android程序时,通常采用MVC模式,我们一般都会为model层设计一个单例,controller层的Activity会调用该单例来完成业务计算。

  • 5)工厂方法模式

    创建型设计模式,本质是延迟到子类选择实现。父类的某个方法需要某个接口对象,但是在父类里并不知道如何选择接口的实现对象,此时可设计一个抽象方法用于创建接口对象,在它的子类里实现选择接口实现对象。体现了依赖倒置原则,应用依赖于IOC容器注入他们需要的外部对象,这里父类依赖子类注入它需要的外部对象。我们一般会在不知道如何选择接口实现对象时采用工厂方法模式,它与模板方法模式有点类似,也可以很好地结合。

  • 6)抽象工厂模式

    创建型设计模式,本质是创建产品簇。抽象工厂需要创建一系列有关联的产品,于是为这些产品的创建各自设计了一个创建方法,在抽线工厂的子类里,在创建这些产品时会选择相关联的产品,比如创建电脑的Cpu和主板时,必须选择可兼容的Cpu和主板。建议创建一系列相关产品时使用该设计模式。

  • 7)生成器模式

    创建型设计模式,本质是分离整体构建算法和部件构造。该设计模式里构建整体的算法是一致的,放在Director对象里,但是每个部件的构造,可能千差万别,于是设计一个Builder接口,约束部件构造方法,实现Builder接口的类,就会实现构建部件的方法,不同的实现,会带来不同的产品。体现了里氏替换原则和开闭原则。通常Director会和client融为一体,由client直接操纵Builder来一步一步的构建整个产品。我们在使用生成器模式的时候常会在需要创建的产品类里实现一个Builder的静态内部类,用于构建该产品,比如Android的AlertDialog.Builder。

  • 8)原型模式

    创建型设计模式,本质是克隆生成对象。在系统中设计一个原型接口,里面有一个方法clone,实现该接口的类,都必须能正确克隆自己得到一个新对象并返回给调用者,要注意深拷贝和浅拷贝的区别。优点在于对client隐藏了实现细节。Java里的Cloneable其实只是个标志接口,Object类就已经实现了clone方法,但只是浅拷贝,如果要实现深拷贝,需要自己手动实现。

  • 9)中介者模式

    行为型型设计模式,本质是封装交互。使用场景,多个同事对象需要彼此交互,造成类之间的耦合非常紧密。因此设计了一个中介者类,所有同事对象都持有该中介者类,当某个同事对象需要和别的同事对象交互时,通知中介者,由中介者决定和其它哪个同事对象交互,故此中介者也会持有所有的同事对象。此时松散了同事对象之间的耦合关系,加强了集中控制。建议一组对象之间的通信方式比较复杂时采用中介者模式。

  • 10)代理模式

    结构型设计模式,本质是控制对象访问。代理类和被代理类实现同一个接口,在代理类里持有被代理类的对象,调用接口方法时,由代理对象控制是否调用被代理对象,或者在调用前后添加一些新功能。根据使用场景不同,有智能代理,保护代理,虚代理,远程代理,Cache代理,同步代理。象远程代理在Java里最典型的就是RMI技术,在Android里最典型的就是Binder技术。

  • 11)观察者模式

    行为型设计模式,本质是实现联动。也是非常常见的一种设计模式,观察者调用被观察对象的注册观察者接口,将观察者注册到被观察者的 观察者列表,当被观察者的状态发生变化后,它会通知观察者列表里的所有观察者对象,观察者对象再从被观察者里获取数据,实现了联动。我们编写应用程序时,常采用MVC模式,Controller层的对象常常会将自己注册到model层被观察业务对象的观察者列表里,当被观察业务对象状态发生改变后,就能及时通知controller对象,controller对象再去获取新的数据,然后更新界面。Android的列表界面,常常会采用局部更新,一般也是采用观察者模式来实现。

  • 12)命令模式

    行为型设计模式,本质是封装请求。命令对象封装了请求数据,并被Invoker所持有,client通过Invoker向系统发送命令,在标准的命令模式里,命令对象并不会直接完成命令所要求的任务,只是封装请求而已,它会转调Receiver去完成真正的业务逻辑。因为命令只是封装了请求,我们可以结合组合模式将命令对象组合成宏命令,即多个命令。我们还可以记录命令列表,以实现撤销和恢复操作。先前看过一个开源的Java实现的Ftp程序,收到客户端请求后,会将请求转化为一个命令对象,然后在命令对象里完成了客户要求的功能,这时候就是退化了的命令模式,没有了receiver,也称为智能命令。

  • 13)迭代器模式

    行为型设计模式,本质是控制访问聚合对象的元素。也是一种非常常见的设计模式,该模式里有一个迭代接口,迭代接口里有first,next,isdone,currentItem等操作,还有一个聚合对象的接口,具体的聚合对象负责创建迭代器接口的实现对象。在Java里有两个接口Iterable和Iterator,Iterable表示聚合对象的接口,Iterator是迭代接口,实现了Iterable接口的聚合对象,可以用for(.. in..)循环进行迭代,非常方便。我们在迭代器里去访问聚合对象的元素时,可以加上一定的策略,比如向前迭代,向后迭代,随机迭代,根据条件不访问某些元素的迭代,故此迭代器的本质就是控制访问聚合对象的元素。

  • 14)组合模式

    结构型设计模式,本质是统一叶子对象和组合对象。非常常见的设计模式,大多数界面库都会采用这种设计模式,界面库里一般会有普通view和布局view,普通view相当于组合模式里的叶子对象,而布局view相当于组合模式里的组合对象。在组合模式里为叶子对象和组合对象设计了一个统一的component接口,里面有普通的操作接口,还有用于管理孩子结点的操作,比如addChild,removeChild,getChildren等操作方法。组合模式可和装饰模式,享元模式,迭代器模式,访问者模式,职责链模式,命令模式等其它模式结合使用。

  • 15)模板方法模式

    行为型设计模式,本质是固定算法骨架。很常见的设计模式。我们在实现某个业务逻辑的时候,知道整体的流程,但是流程中某些步骤的实现在不同的具体业务逻辑里会有不同的实现,故此会把这些变化的部分设计成抽象接口,由子类决定如何实现,这样便分离了变化的部分,能够更好的扩展,体现了开闭原则和里氏替换原则。如果我们不知道如何选择某个接口的实现,还可以结合工厂方法模式,让子类选择需要的接口实现。我们还可以结合简单工厂,用于选择具体业务逻辑实现对象。

  • 16)策略模式

    行为型设计模式,本质是分离算法,选择实现。策略模式里有一个Context对象,用于持有策略,以及公共数据,并设计了一个策略接口,所有策略都实现了这个接口, client端使用时,先实例化具体的策略对象,并设置到Context对象中,client调用Context的接口,Context对象再转调策略对象的策略接口的方法,完成客户需要的功能。虽然策略模式和状态模式的类结构很相似,但是策略模式的策略是扁平式的结构,是可以被相互替换的,而状态模式的状态行为是不能被相互替换的,只能根据具体的状态选择相应的行为。先前也介绍到实现计算器时可以采用策略模式。

  • 17)状态模式

    行为型设计模式,本质是根据状态选择行为。状态模式里也有一个上下文,会持有具体的状态处理对象,状态切换时,上下文会切换状态处理对象。所有的状态处理对象都会实现同一个状态处理接口,不同的状态处理对象会根据表示的不同状态进行不同的业务处理。状态的维护可在上下文中,也可放在状态的处理类中,还可以放在数据库里维护。虽然状态模式和策略模式非常类似,但是状态模式里的状态是不可相互替换的,因为不同状态对应的行为也不同。

  • 18)备忘录模式

    行为型设计模式,本质是保存和恢复内部状态。对象需要保存内部状态,但又不愿意暴露内部实现时,可使用备忘录模式。备忘录模式里有一个备忘接口,该接口是一个标志接口,没有任何方法,需要保存内部状态的对象通常会设计一个私有的静态内部类,这个静态内部类会实现备忘接口,需要保存内部状态的对象还会提供两个接口,一个用于获取备忘接口,一个用于从备忘接口对象恢复内部状态。另外还会设计一个备忘保存对象,用于保管备忘录对象。对于外部来说,备忘录对象是一个窄接口,不知道如何使用,对于保存内部状态的对象来说,它就是一个宽接口,可以用它来恢复内部状态。备忘录模式可用于实现撤销恢复操作。

  • 19)享元模式

    结构型设计模式,本质是分离和共享。当系统中需要的重复对象数量比较多,或者占用内存比较大时,可考虑使用享元模式。在享元模式里,有一个享元工厂,用于缓存享元对象,享元对象存在享元工厂的map里,享元工厂一般设计为单例。享元对象有共享的,也有不共享的,不共享的享元对象一般由共享的享元对象组成。

  • 20)解释器模式

    行为型设计模式,本质是分离实现,解释执行。如果不用解释器模式解析一个句子,可能是直接解析句子的各个部分,而使用解释器模式,则为每一个语法规则(在句子里对应主语和谓语)创建相应的解释器对象,然后将各个解释器对象组成抽象语法树,最后才解释执行,只需调用抽象语法树的解释方法就可解析出来。分离实现是指将各个语法规则分离实现,针对每个语法规则,都有对应的解释器,每个解释器的解释行为都不一样。解释器模式里有终端解释器,也有非终端解释器,非终端解释器相当于组合模式里的组合对象。上下文在组合模式里非常重要,所有的公共数据都存于上下文,被各个解释器对象获取或者修改。建议当有一个语言需要解释执行,并且可以将该语言中的句子表示为一个抽象语法树的时候,可以考虑使用解释器模式,但是使用解释器模式还有两个要求,一是语法比较简单,一是效率要求不高。

  • 21)装饰模式

    行为型设计模式,本质是动态组合,透明添加功能。装饰模式里装饰类和非装饰类继承了同一个类,装饰类持有父类对象,具体装饰类实现祖父接口方法时,可以选择调用持有的祖父类对象的方法,也可以选择在调用前后添加一些功能,或者添加一些策略,对于被装饰的祖父类对象来说,它一无所知,故此实现了透明添加功能。Java里使用装饰模式的最典型的例子莫过于IO流库,装饰类有FilterInputStream和FilterOutputStream,而FilterInputStream的子类有BufferedInputStream,DataInputStream等子类,这些装饰器类可以用于修饰普通的流对象,也可用于修饰装饰对象。

  • 22)职责链模式

    行为型设计模式,本质是分离职责,动态组合。实现请求者和接收者解耦,将复杂的功能分离成一个个职责对象,运行时,将职责对象动态组合成一条链,请求者将请求发送给这条职责链,这条职责链里的某个对象会处理该请求,但是不确定是哪个对象,这是由运行时决定的。职责链模式里有一个Handler的抽象类,它含有一个Handler对象,也就是说实现了包含了Handler类的另一个对象,还有一个SetHandler方法,用于设置下一个处理请求的对象,另有一个handleRequest的方法,子类必须实现该方法,并在合适的时候调用父类存的Handler对象的handleRequest方法。在有大量if else语句时可考虑使用职责链模式。

  • 23)桥接模式

    行为型设计模式,本质是分离抽象和实现。当出现有两个维度的变化时可考虑使用桥接模式。如果抽线和实现耦合在一起,扩展起来非常困难,因为导致类变化的因素有两个,抽象部分增加功能导致实现也要跟着变,因此应对变化非常不灵活。比如消息(抽象)和消息发送者(实现),消息有多种,紧急的,加急的,消息发送者也有多种,短信,电话,Email。将抽线和实现分离后,抽象部分和实现部分都可各自变化,并且可动态切换抽象部分用到的实现接口的对象,因此扩展起来更加方便。抽象部分会持有实现接口的对象,对外提供的方法会转调实现接口的对象的方法来完成业务逻辑。

  • 24)访问者模式

    行为型设计模式,本质是预留通路,回调实现。如果有一个不变的对象结构,也就是说不会增加对象类型,但是需要为所有这些对象不断扩展功能,假设这些对象都实现了同一个接口,如果要扩展功能,那么需要在这个接口上添加方法,并在所有对象里实现该方法,这样扩展起来很不方便。这种情况下适合使用访问者模式,在这些对象的共同接口预留一个接受访问者访问的抽象方法,然后在对象的方法里实现该接口,回调访问者接口的方法以访问具体类对象,访问者接口为每个具体对象都定义了一个方法,该方法接受的是具体类的类型,为了使得访问方便,定义了一个object structure,持有所有的具体类的对象。

¥打赏5毛

取消

感谢您的支持,我会继续努力的!

扫码支持
赏个5毛,支持我把

打开支付宝扫一扫,即可进行扫码打赏哦

本篇目录