设计模式总结:本文主要讲解了:建型设计模式, 结构型设计模式。以后文章会详细介绍行为型设计模式。
- 创建型设计模式:
- Singleton Pattern(单例模式)
- Prototype Pattern(原型模式)
- Abstract Factory Pattern(抽象工厂模式)
- Builder Pattern(建造者模式)
- 结构型设计模式:
- Adapter Pattern(适配器模式)
- Bridge Pattern(桥接模式)
- Composite Pattern(组合模式)
- Decorator Pattern(装饰者模式)
- Facade Pattern(外观模式)
- Flyweight Pattern(享元模式)
- Proxy Pattern(代理模式)
- 行为型设计模式:
- Chain of Responsibility Pattern(职责链模式)
- Command Pattern(命令模式)
- Interpreter Pattern(解释器模式)
- Iterator Pattern(迭代器模式)
- FMediator Pattern(中介者模式)
- Memento Pattern(备忘录模式)
- Observer Pattern(观察者模式)
- State Pattern(状态模式)
- Strategy Pattern(策略模式)
- Template Method Pattern(模板方法模式)
- Visitor Pattern(访问者模式)
创建型设计模式:
Singleton Pattern(单例模式) 意图 保证一个类仅有一个实例,并提供一个访问它的全局访问点。
适用性
当类只有一个实例而且客户可以从一个众所周知的访问点访问它时。
当这个唯一实例应该是通过子类化可扩展的,并且客户应该无需更改代码就能使用一个扩展的实例时。
结构
例子 iOS:
[NSUserDefaults standardUserDefaults];
[NSFileManager defaultManager];
[UIApplication sharedApplication];
[NSNotificationCenter defaultCenter];
[UIScreen mainScreen]
Android:
Applicationapp = this.getApplication();
注意事项 开发中使用单例模式,有几点要注意:
1. 只创建一个实例,并且只提供一个全局的访问点;避免创建多个实例的可能。 2. 资源共享情况下,获取实例的方法必须适应多线程并发访问。 3. 提高访问性能。
4. 懒加载(Lazy Load),在需要的时候才被构造。
Prototype Pattern(原型模式) 意图 用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。 适用性
1.当要实例化的类是在运行时刻指定时,例如,通过动态装载;
2.为了避免创建一个与产品类层次平行的工厂类层次时;
3.当一个类的实例只能有几个不同状态组合中的一种时。建立相应数目的原型并克隆它们可能比每次用合适的状态手工实例化该类更方便一些。
结构
参与者:
Prototype(原型)
— 声明一个克隆自身的接口。
ConcretePrototype(具体的原型)
— 实现一个克隆自身的操作。
Client(场景)
— 让一个原型克隆自身从而创建一个新的对象。
例子 阿诺斯瓦辛格主演的《第六日》2000上映。主角叫亚当,来之圣经:上帝在第六日创造了亚当。 在不远的将来,牛、鱼甚至宠物都可以任意克隆,但克隆人类是非法的。但有一天当亚当-吉布森回到家,却发现一个克隆人取代了自己。他被赶出了家门,更遭的是,阴谋的制造者为了掩盖真相,派出杀手追杀亚当灭口。为了逃生,同时也为了重新赢得自己的一切,亚当同神秘的幕后策划者展开了机智的战斗。
优缺点
优点:
性能优良。原型模式是在内存二进制流的拷贝,要比直接new一个对象性能好很多,特别是要在一个循环体内产生大量的对象时,原型模式可以更好地体现其优点。
缺点:
逃避构造函数的约束。这既是它的优点也是缺点,直接在内存中拷贝,构造函数是不会执行的。优点就是减少了约束,缺点也是减少了约束,需要大家在实际应用时考虑。
Simple Factory Pattern(简单工厂模式) GoF中认为简单工厂模式是工厂方法的一个特例。
结构
例子
优缺点
优点:简单,结合单例模式更加方便和节省资源。
缺点:增加新的产品时,需要修改工厂类的if…else逻辑,不符合开闭原则。
Factory Method Pattern(工厂方法模式)
意图 定义一个用于创建对象的接口,让子类决定实例化哪一个类。Factory Method 使一个类的实例化延迟到其子类。 适用性
1.当一个类不知道它所必须创建的对象的类的时候。
2.当一个类希望由它的子类来指定它所创建的对象的时候。
3.当类将创建对象的职责委托给多个帮助子类中的某一个,并且你希望将哪一个帮助子类是代理者这一信息局部化的时候。
结构
参与者:
Product(抽象产品)
— 定义工厂方法所创建的对象的接口。
ConcreteProduct(具体产品)
— 实现Product接口。
Creator(抽象工厂类)
— 声明工厂方法,该方法返回一个 Product类型的对象。 Creator也可以定义一个工厂方 法的缺省实现,它返回一个缺省的
ConcreteProduct对象。 — 可以调用工厂方法以创建一个 Product对象。
ConcreteCreator(具体工厂类)
— 重定义工厂方法以返回一个 ConcreteProduct实例。
例子
优缺点
优点:
首先,良好的封装性,代码结构清晰。一个对象创建是有条件约束的,如一个调用者需要一个具体的产品对象,只要知道这个产品的类名(或约束字符串)就可以了,不用知道创建对象的艰辛过程,降低模块间的耦合。
工厂方法模式是典型的解耦框架。高层模块只需要知道产品的抽象类,其他的实现类都不用关心,符合迪米特法则,我不需要的就不要去交流;也符合依赖倒置原则,只依赖产品类的抽象;当然也符合里氏替换原则,使用产品子类替换产品父类,没问题!
Abstruct Factory Pattern(抽象工厂模式) 意图 提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。
适用性
1.一个系统要独立于它的产品的创建、组合和表示时。
2.一个系统要由多个产品系列中的一个来配置时。
3.当你要强调一系列相关的产品对象的设计以便进行联合使用时。
4.当你提供一个产品类库,而只想显示它们的接口而不是实现时。
结构 参与者
AbstractFactory (抽象工厂)
— 声明一个创建抽象产品对象的操作接口。
ConcreteFactory (具体工厂)
— 实现创建具体产品对象的操作。
AbstractProduct (抽象产品)
— 为一类产品对象声明一个接口。
ConcreteProduct (具体产品)
— 定义一个将被相应的具体工厂创建的产品对象。
— 实现AbstractProduct接口。
Client(使用场景)
— 仅使用由AbstractFactory和AbstractProduct类声明的接口。?
例子
三种工厂模式的区别
工厂方法模式和抽象工厂模式的区别
简单工厂模式:只有一个工厂类一个生产方法,根据参数不同生产不同的产品。
工厂方法模式:每一个工厂类只负责一个产品生产,不生成其它产品。好比一条生产线只生产一个产品线。
抽象工厂模式:每一个工厂类提供多个方法,可以生产不同的产品。好比多条生产线可以生产多家产品。
Builder Pattern(建造者模式) 意图 将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
适用性 * 当创建复杂对象的算法应该独立于该对象的组成部分以及它们的装配方式时。 * 当构造过程必须允许被构造的对象有不同的表示时。
结构
参与者: Builder(抽象建造者角色) — 为创建一个Product对象的各个部件指定抽象接口。 ConcreteBuilder(具体建造者) — 实现Builder的接口以构造和装配该产品的各个部件。 — 定义并明确它所创建的表示。 — 提供一个检索产品的接口。 Director(导演角色) — 构造一个使用Builder接口的对象。 Product(建造的产品) — 表示被构造的复杂对象。 ConcreteBuilder创建该产品的内部表示并定义它的装配过程。
— 包含定义组成部件的类,包括将这些部件装配成最终产品的接口。
例子
建造者模式和工厂模式的区别
当创造一个对象需要很多步骤时适合使用建造者模式。
而当只需调用一个方法就可以简单地创建整个对象时适合使用工厂模式。
结构型设计模式:
Adapter Pattern(适配器模式) 意图 将一个类的接口转换成另外一个客户希望的接口。Adapter 模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。
注:适配器模式在详细设计阶段不需要考虑它,它是为了对现有系统或产品接口兼容时,也就是既成事实的情况下的补救措施。
适用性 * 你想使用一个已经存在的类,而它的接口不符合你的需求。 * 你想创建一个可以复用的类,该类可以与其他不相关的类或不可预见的类(即那些接口可能不一定兼容的类)协同工作。 * (仅适用于对象Adapter)你想使用一些已经存在的子类,但是不可能对每一个都进行子类化以匹配它们的接口。对象适配器可以适配它的父类接口。
结构 1、类适配器
2、对象适配器
参与者 Target (目标角色,要用的接口) — 定义Client使用的与特定领域相关的接口。 Client (使用场景) — 与符合Target接口的对象协同。 Adaptee (被适配者) — 定义一个已经存在的接口,这个接口需要适配。 Adapter (适配器,把Adaptee接口转换为Target可用的接口) — 对Adaptee的接口与Target接口进行适配
例子 1、 类适配器
2、 对象适配器
优缺点
优点:
1. 适配器模式可以让两个没有任何关系的类在一起运行,只要适配器这个角色能够搞定他们就成。 2. 增加了类的透明性 想想看,我们访问的Target目标角色,但是具体的实现都委托给了源角色,而这些对高层次模块是透明的,也是它不需要关心的。
3. 提高了类的复用度当然了,源角色在原有的系统中还是可以正常使用,而在目标角色中也可以充当新的演员。 4. 灵活性非常好
某一天,突然不想要适配器,没问题,删除掉这个适配器就可以了,其他的代码都不用修改,基本上就类似一个灵活的构件,想用就用,不想就卸载。
Bridge Pattern(桥接模式) 意图 将抽象部分与它的实现部分分离,使它们都可以独立地变化。
【GOF95】在提出桥梁模式的时候指出,桥梁模式的用意是"将抽象化(Abstraction)与实现化(Implementation)脱耦,使得二者可以独立地变化"。这句话有三个关键词,也就是抽象化、实现化和脱耦。
* 抽象化 存在于多个实体中的共同的概念性联系,就是抽象化。作为一个过程,抽象化就是忽略一些信息,从而把不同的实体当做同样的实体对待【LISKOV94】。 * 实现化 抽象化给出的具体实现,就是实现化。 * 脱耦 所谓耦合,就是两个实体的行为的某种强关联。而将它们的强关联去掉,就是耦合的解脱,或称脱耦。在这里,脱耦是指将抽象化和实现化之间的耦合解脱开,或者说是将它们之间的强关联改换成弱关联。
将两个角色之间的继承关系改为聚合关系,就是将它们之间的强关联改换成为弱关联。因此,桥梁模式中的所谓脱耦,就是指在一个软件系统的抽象化和实现化之间使用组合/聚合关系而不是继承关系,从而使两者可以相对独立地变化。这就是桥梁模式的用意。
结构
桥梁模式所涉及的角色有: 抽象化(Abstraction)角色:抽象化给出的定义,并保存一个对实现化对象的引用。 修正抽象化(Refined Abstraction)角色:扩展抽象化角色,改变和修正父类对抽象化的定义。 实现化(Implementor)角色:这个角色给出实现化角色的接口,但不给出具体的实现。必须指出的是,这个接
口不一定和抽象化角色的接口定义相同,实际上,这两个接口可以非常不一样。实现化角色应当只给出底层操作,而抽象化角色应当只给出基于底层操作的更高一层
的操作。 具体实现化(Concrete Implementor)角色:这个角色给出实现化角色接口的具体实现。
例子 一个表就一个表带?
还是,一个表+多个可更换的表带?
桥接模式和适配器模式的区别 很多时候经常容易把桥接模式和适配器模式弄混。那什么时候用桥接,什么时候用适配器呢 ?
共同点: 桥接和适配器都是让两个东西配合工作 不同点:出发点不同。 适配器:改变已有的两个接口,让他们相容。 桥接模式:分离抽象化和实现,使两者的接口可以不同,目的是分离。
所以说,如果你拿到两个已有模块,想让他们同时工作,那么你使用的适配器。 如果你还什么都没有,但是想分开实现,那么桥接是一个选择。
桥接是先有桥,才有两端的东西 适配是先有两边的东西,才有适配器 。 桥接是在桥好了之后,两边的东西还可以变化。
桥模式并不同于适配器模式,适配器模式其实是一个事后诸葛亮,当发现以前的东西不适用了才去做一个弥补的措施。桥模式相对来说所做的改变比适配器模式早,它可以适用于有两个甚至两个以上维度的变化。
桥接模式将继承关系转换为关联关系,从而降低了类与类之间的耦合,减少了代码编写量。
Composite Pattern(组合模式) 组合模式,将对象组合成树形结构以表示“部分-整体”的层次结构,组合模式使得用户对单个对象和组合对象的使用具有一致性。 有时候又叫做部分-整体模式,它使我们树型结构的问题中,模糊了简单元素和复杂元素的概念,客户程序可以像处理简单元素一样来处理复杂元素,从而使得客户程序与复杂元素的内部结构解耦。 组合模式让你可以优化处理递归或分级数据结构。有许多关于分级数据结构的例子,使得组合模式非常有用武之地。关于分
级数据结构的一个普遍性的例子是你每次使用电脑时所遇到的:文件系统。文件系统由目录和文件组成。每个目录都可以装内容。目录的内容可以是文件,也可以是目录。按照这种方式,计算机的文件系统就是以递归结构来组织的。如果你想要描述这样的数据结构,那么你可以使用组合模式Composite。
例子
意图: (GoF《设计模式》):将对象组合成树形结构以表示“部分-整体”的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。
结构:
涉及角色: 1.Component 是组合中的对象声明接口,在适当的情况下,实现所有类共有接口的默认行为。声明一个接口用于访问和管理Component子部件。 2.Leaf 在组合中表示叶子结点对象,叶子结点没有子结点。 3.Composite 定义有枝节点行为,用来存储子部件,在Component接口中实现与子部件有关操作,如增加(add)和删除(remove)等。
适用性: 以下情况下适用Composite模式: 1.你想表示对象的部分-整体层次结构
2.你希望用户忽略组合对象与单个对象的不同,用户将统一地使用组合结构中的所有对象。
Decorator Pattern(装饰者模式) Decorator Pattern叫装饰模式,或装饰者模式,以前叫包装器模式(Wrapper,GoF在92-93年由Wrapper改为Decorator)。 装饰模式是在不必改变原类文件和使用继承的情况下,动态地扩展一个对象的功能。它是通过创建一个包装对象,也就是装饰来包裹真实的对象。
Decorator模式的工作原理是:可以创建始于Decorator对象(负责新的功能的对象)终于原对象的一个对象“链”。
装饰者模式隐含的是通过一条条装饰链去实现具体对象,每一条装饰链都始于一个Componet对象,每个装饰者对象后面紧跟着另一个装饰者对象,而对象链终于ConcreteComponet对象。
意图 动态地给一个对象添加一些额外的职责。就增加功能来说,Decorator模式相比生成子类更为灵活。
适用性 1. 需要扩展一个类的功能,或给一个类添加附加职责。 2. 需要动态的给一个对象添加功能,这些功能可以再动态的撤销。 3. 需要增加由一些基本功能的排列组合而产生的非常大量的功能,从而使继承关系变的不现实。 4. 当不能采用生成子类的方法进行扩充时。一种情况是,可能有大量独立的扩展,为支持每一种组合将产生大量的子类,使得子类数目呈爆炸性增长。另一种情况可能是因为类定义被隐藏,或类定义不能用于生成子类。
结构:
在装饰模式中的各个角色有: (1)抽象构件(Component)角色:给出一个抽象接口,以规范准备接收附加责任的对象。 (2)具体构件(Concrete Component)角色:定义一个将要接收附加责任的类。 (3)装饰(Decorator)角色:持有一个构件(Component)对象的实例,并实现一个与抽象构件接口一致的接口。 (4)具体装饰(Concrete Decorator)角色:负责给构件对象添加上附加的责任。
例子
空桌子,没装饰
空桌子,没装饰 装饰后的桌子,有桌布,有花
另:
Java中IO流的设计就大量运用了装饰模式,如:
BufferedReader br = new BufferedReader(new
InputStreamReader(new FileInputStream("..")));
Facade Pattern(外观模式) 意图 为子系统中的一组接口提供一个一致的界面,Facade模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。
适用性 * 当你要为一个复杂子系统提供一个简单接口时。子系统往往因为不断演化而变得越来越复杂。大多数模式使用时都会产生更多更小的类。这使得子系统更具可重用性,
也更容易对子系统进行定制,但这也给那些不需要定制子系统的用户带来一些使用上的困难。Facade可以提供一个简单的缺省视图,这一视图对大多数用户来
说已经足够,而那些需要更多的可定制性的用户可以越过Facade层。 * 客户程序与抽象类的实现部分之间存在着很大的依赖性。引入Facade将这个子系统与客户以及其他的子系统分离,可以提高子系统的独立性和可移植性。 * 当你需要构建一个层次结构的子系统时,使用门面模式定义子系统中每层的入口点。如果子系统之间是相互依赖的,你可以让它们仅通过Facade进行通讯,从而简化了它们之间的依赖关系。
结构
Facade 这个外观类为子系统提供一个共同的对外接口 Clients 客户对象通过一个外观接口读写子系统中各接口的数据资源。
例子
Flyweight Pattern(享元模式) 意图 运用共享技术有效地支持大量细粒度的对象。
适用性 当以下所有的条件都满足时,可以考虑使用享元模式: * 一个应用程序使用了大量的对象。 * 完全由于使用大量的对象,造成很大的存储开销。 * 对象的大多数状态都可变为外部状态。 * 如果删除对象的外部状态,那么可以用相对较少的共享对象取代很多组对象。 * 应用程序不依赖于对象标识。由于Flyweight对象可以被共享,对于概念上明显有别的对象,标识测试将返回真值。
满足以上的这些条件的系统可以使用享元对象。
最后,使用享元模式需要维护一个记录了系统已有的所有享元的表,而这需要耗费资源。因此,应当在有足够多的享元实例可供共享时才值得使用享元模式。
注意要点: 根据我们的经验,当要将一个对象进行共享时,就需要考虑到对象的状态问题了;不同的客户端获得共享的对象之后,可能会修改共享对象的某些状态;大家都修改了共享对象的状态,那么就会出现对象状态的紊乱。对于享元模式,在实现时一定要考虑到共享对象的状态问题。那么享元模式是如何实现的呢? 在享元模式中,有两个非常重要的概念:内部状态(intrinsicState)和外部状态(extrinsicState)。 内部状态存储于flyweight中,它包含了独立于flyweight场景的信息,这些信息使得flyweight可以被共享。而外部状态取决于flyweight场景,并根据场景而变化,因此不可共享。用户对象负责在必要的时候将外部状态传递给flyweight。 flyweight执行时所需的状态必定是内部的或外部的。内部状态存储于ConcreteFlyweight对象之中;而外部对象则由
Client对象存储或计算。当用户调用flyweight对象的操作时,将该状态传递给它。同时,用户不应该直接对ConcreteFlyweight
类进行实例化,而只能从FlyweightFactory对象得到ConcreteFlyweight对象,这可以保证对它们适当地进行共享;由于共享一
个实例,所以在创建这个实例时,就可以考虑使用单例模式来进行实现。 享元模式的工厂类维护了一个实例列表,这个列表中保存了所有的共享实例;当用户从享元模式的工厂类请求共享对象时,首先查询这个实例表,如果不存在对应实例,则创建一个;如果存在,则直接返回对应的实例。
结构
Flyweight:描述一个接口,通过这个接口flyweight可以接受并作用于外部状态; ConcreteFlyweight:实现Flyweight接口,并为定义了一些内部状态,ConcreteFlyweight对象必须是可共享的;同时,它所存储的状态必须是内部的;即,它必须独立于ConcreteFlyweight对象的场景; UnsharedConcreteFlyweight:并非所有的Flyweight子类都需要被共享。Flyweight接口使共享成为可能,但它并不强制共享。 FlyweightFactory:创建并管理flyweight对象。它需要确保合理地共享flyweight;当用户请求一个flyweight时,FlyweightFactory对象提供一个已创建的实例,如果请求的实例不存在的情况下,就新创建一个实例; Client:维持一个对flyweight的引用;同时,它需要计算或存储flyweight的外部状态。
如何共享了Flyweight,如下图:
例子
注:可共享——>可以理解为可以复用的。 不可共享——>可以理解为不需要复用,不可复用的。
优缺点
优点:
享元模式可以避免大量非常相似对象的开销。在程序设计时,有时需要生成大量细粒度的类实例来表示数据。如果能发现这些实例数据除了几个参数外基本都是相同的,使用享元模式就可以大幅度地减少对象的数量。
缺点:
1)享元模式使得系统更加复杂。为了使对象可以共享,需要将一些状态外部化,这使得程序的逻辑复杂化。 2)享元模式将享元对象的状态外部化,而读取外部状态使得运行时间稍微变长。
Proxy Pattern(代理模式)
意图
为其他对象提供一种代理以控制对这个对象的访问。
适用性
在需要用比较通用和复杂的对象指针代替简单的指针的时候,使用Proxy模式。下面是一些可以使用Proxy模式常见情况:
远程代理(Remote Proxy)为一个对象在不同的地址空间提供局部代表。
虚代理(Virtual Proxy)根据需要创建开销很大的对象。
保护代理(Protection Proxy)控制对原始对象的访问。保护代理用于对象应该有不同 的访问权限的时候。
智能指引(Smart Reference)取代了简单的指针,它在访问对象时执行一些附加操作。 它的典型用途包括:
* 对指向实际对象的引用计数,这样当该对象没有引用时,可以自动释放它(也称为SmartPointers)。
* 当第一次引用一个持久对象时,将它装入内存。
* 在访问一个实际对象前,检查是否已经锁定了它,以确保其他对象不能改变它。
结构
例子
优缺点
优点:
(1).职责清晰
真实的角色就是实现实际的业务逻辑,不用关心其他非本职责的事务,通过后期的代理完成一件完成事务,附带的结果就是编程简洁清晰。
(2).代理对象可以在客户端和目标对象之间起到中介的作用,这样起到了中介的作用和保护了目标对象的作用。
(3).高扩展性。
未完待续.......
|