设计模式

设计原则

1.针对接口编程,而不是针对实现编程

2.”针对接口编程“真正的意思是“针对超类型(supertype)编程”

3.多用组合少用继承

4.为了交互对象之间的松耦合设计而努力

5.类应该对扩展开放,对修改关闭

6.要依赖抽象,不要依赖具体类(依赖倒置原则)

7.最少知识原则:只和你的密友谈话

8.别调用我们,我们会调用你

9.一个类应该只有一个引起变化的原因

松耦合的威力

当两个对象之间松耦合,它们依然可以交互,但是不太清楚彼此的细节,观察者模式提供了一种对象设计,让主题和观察者之间松耦合

松耦合的设计之所以能让我们建立有弹性的OO系统,能够应对变化,是因为对象之间的互相依赖降低了最低

策略模式

定义了算法族,分别封装起来,让它们之间可以互相替换,此模式让算法的变化独立于使用算法的客户

要点

1.策略模式通常会用行为或算法配置Context类

观察者模式

定义了对象之间的一对多依赖,这样一来,当一个对象改变状态时,它的所有依赖者会收到通知并自动更新

要点

1.观察者模式定义了对象之间一对多的关系

2.主题(也就是可观察者)用一个共同的接口来更新观察者

3.观察者和可观察者之间用松耦合方式结合(loosecoupling),可观察者不知道观察者的细节,只知道观察者实现了观察者接口

4.使用此模式时,你可从被观察者处推(push)或拉(pull)数据(然而,推的方式被认为更“正确”)。

5.有多个观察者时,不可以依赖特定的通知次序

6.Java有多种观察者模式的实现,包括了通用的java.util.Observable实现上所带来的一些问题

7.如果有必要的话,可以实现自己的Observable,这并不难,不要害怕

8.Swing大量使用观察者模式,许多GUI框架也是如此

9.此模式也被应用在许多地方,例如:JavaBeans,RMI

装饰者模式

装饰者模式动态地将责任附加到对象上。若要扩展功能,装饰者提供了比继承更有弹性的替代方案

装饰者和被装饰对象有相同的超类型

你可以用一个或多个装饰者包装一个对象

既然装饰者和被装饰者有相同的超类型,所以在任何需要原始对象(被包装的)的场合,可以用装饰过的对象替代它

装饰者可以在所委托被装饰者的行为之前与/或之后,加上自己的行为,以达到特定的目的

对象可以在任何时候被装饰,所以可以在运行时动态地,不限量地用你喜欢的装饰者来装饰对象

要点

1.继承属于扩展形式之一,但不见得是达到弹性设计的最佳方式

2.在我们的设计中,应该允许行为可以被扩展,而无须修改现有的代码

3.组合和委托可用与在运行时动态地加上新的行为

4.除了继承装饰者模式也可以让我们扩展行为

5.装饰者模式意味着一群装饰者类,这些类用来包装具体组件

6.装饰者类放映处被装饰的组件类型(事实上,它们具有相同的类型,都经过接口或继承实现)

7.装饰者可以在被装饰者的行为前面与/或后面加上自己的行为,甚至将被装饰者的行为整个取代掉,而达到特定的目的

8.你可以用无数个装饰者包装一个组件

9.装饰者一般对组件的客户是透明的,除非客户程序依赖于组件的具体类型

10.装饰者会导致设计中出现许多小对象,如果过度使用,会让程序变得很复杂

简单工厂方法模式

定义了一个创建对象的接口,但由于子类决定要实例化的类是哪一个。工厂方法让类把实例化推迟到子类

依赖倒置原则

避免违反

1.变量不可以持有具体类的引用 (如果使用new,就会持有具体类的引用。你可以改用工厂避开这样的做法)

2.不要让类派生自具体类 (如果派生自具体类,你就会依赖具体类,请派生自一个抽象[接口或抽象类])

3.不要覆盖基类中已实现的方法 (如果覆盖基类已实现的方法,那么你的基类就不是一个真正适合被继承的抽象。基类中已实现的方法,应该由所有的子类共享)

要点

1.简单工厂,虽然不是真正的设计模式,但仍不失为一个简单的方法,可以将客户程序从具体类解耦

2.工厂方法使用继承,把对象的创建委托给子类,子类实现工厂方法来创建对象

3.工厂方法允许类将实例化延迟到子类进行

4.工厂是很有威力的技巧,帮助外面针对抽象编程,而不要针对具体类编程

抽象工厂模式

提供一个接口,用于创建相关或依赖对象的家族,而不需要明确指定具体类

要点

1.所有的工厂都是用来封装对象的创建

2.抽象工厂使用对象组合,对象的创建被实现在工厂接口所暴露出来的方法中

3.所有工厂模式都通过减少应用程序和具体类之间的依赖促进松耦合

4.抽象工厂创建相关的对象家族,而不需要依赖它们的具体类

5.依赖倒置原则,指导外面避免依赖具体类型,而要尽量依赖抽象

单例模式

确保一个类只有一个实例,并提供一个全局访问的

要点

1.单例模式确保程序中一个类最多只有一个实例

2.单例模式也提供访问这个实例的全局点

3.在Java中实现单例模式需要私有的构造器,一个静态方法和一个静态变量

4.确定在性能和资源上的限制,然后小心地选择适当的方案来实现单例,以解决多线程的问题(我们必须认定所有的程序都是多线程的)

5.小心,你如果使用多个类加载器,可能导致单例失效而产生多个实例

命令模式

将“请求”封装成对象,以便使用不同的请求,队列或者日志来参数化其他对象,命令模式也支持可撤销的操作

要点

1.命令模式将发出请求的对象和执行请求的对象解耦

2.在被解耦的两者之间是通过命令对象进行沟通的。命令对象封装了接收者和一个或一组动作

3.通用者通过调用命令对象的execute()发出请求,这会使得接收者的动作被调用

4.调用者可以接受命令当做参数,甚至在运行时动态地进行

5.命令可以支持撤销,做法是实现一个undo()方法来回到execute()被执行前的状态

6.宏命令是命令的一种简单的延伸,允许调用多个命令。宏方法也可以支持撤销

7.实际操作时,很常见使用“聪明”命令对象,也就是之间实现了请求,而不是将工作委托给接收者

8.命令也可以用来实现日志和事务系统

适配器模式

将一个类的接口,转换成客户期望的另一个接口。适配器让原来接口不兼容的类可以合作无间

外观模式

提供了一个统一的接口,用来访问子系统中的一群接口。外观定义了一个高层接口,让子系统更容易使用

要点

1.当需要使用一个现有的类而其接口并不符合你的需要时,就使用适配器

2.当需要简化并统一一个很大的接口或者一群复杂的接口时,使用外观

3.适配器改变接口以符合客户的期望

4.外观将客户从一个复杂的子系统中解耦

5.实现一个适配器可能需要一番功夫,也可能不费功夫,视目标接口的大小与复杂度而定

6.实现一个外观,需要将子系统组合进外观中,然后将工作委托给子系统执行

7.适配器模式有两种形式:对象适配器和类适配器。类适配器需要用到多重继承

8.你可以为一个子系统实现一个以上的外观

9.适配器将一个对象包装起来以改变其接口,装饰者将一个对象包装起来以增加新的行为和责任;而外观将一群对象“包装”起来以简化其接口

模板方法模式

在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中,模板方法使得子类可以在不改变算法结构的情况下,重新定义算法中的某些步骤

要点

1.”模板方法”定义了算法的步骤,把这些步骤的实现延迟到子类

2.模板方法模式为我们提供了一种代码复用的重要技巧

3.模板方法的抽象类可以定义具体方法,抽象方法和钩子

4.抽象方法由子类实现

5.钩子是一种方法,它在抽象类中不做事,或者只做默认的事,子类可以选择要不要去覆盖它

6.为了防止子类改变模板方法中的算法,可以将模板方法声明为final

7.好莱坞原则告诉我们,将决策权放在高层模块中,以便决定如何以及何时调用底层模块

8.你将在真实世界代码中看到模板方法模式的许多变体,不要期待它们全都是一眼就可以被你认出的

9.策略模式和模板方法模式都封装算法,一个用组合,一个用继承

10.工厂方法是模板方法的一种特殊版本

迭代器模式

提供一种方法顺序访问一个聚合对象中的各个元素,而又不暴露其内部的表示

迭代器模式让我们能游走于聚合内的每一个元素,而不暴露其内部的表示

把游走的任务放在迭代器上,而不是聚合上,这样简化了聚合的接口和实现,也让责任各得其所

要点

1.迭代器允许访问聚合的元素,而不需要暴露它的内部结构

2.迭代器将遍历聚合的工作封装进一个对象中

3.当使用迭代器的时候,我们依赖聚合提供遍历

4.迭代器提供一个通用的接口,让我们遍历聚合的项,当我们编码使用聚合的项时,就可以使用多态

5.我们应该努力让一个类只分配一个责任

单一原则

类的每个责任都有改变的潜在区域。超过一个责任,意味者超过一个改变的区域

这个原则告诉我们,尽量让每个类保持单一责任

组合模式

允许你将对象组合成树型结构来表现“整体/部分”层次结构。组合能让客户以一致的方式处理个别对象以及对象组合

组合模式让我们能用树形创建对象的结构,树里面包含了组合以及个别的对象**

使用组合结构,我们能把相同的操作应用在组合和个别对象上,换句话说,在大多数情况下,我们可以忽略组合和个别对象之间的差别

要点

1.组合模式提供一个结构,可同时包容个别对象和组合对象

2.组合模式允许客户对个别对象以及组合对象一视同仁

3.组合结构内的任意对象称为组件,组件可以是组合,也可以是叶节点

4.在实现组合模式时,有许多设计上的折衷。你要根据需要平衡透明性和安全性

状态模式

允许对象在内部状态改变时改变它的行为,对象看起来好像修改了它的类

要点

1.状态模式允许一个对象基于内部状态而拥有不同得行为

2.和程序状态机(PSM)不同,状态模式用类代表状态

3.Context会将行为委托给当前状态对象

4.通过将每个状态封装进一个类,我们把以后需要做得任何改变局部化了

5.状态模式和策略模式有相同的类图,但是它们的意图不同

6..状态模式允许Context随着状态的改变而改变行为

7.状态转换可以由State类或Context类控制

8.使用状态模式通常会导致设计中类的数目大量增加

9.状态类可以被多个Context实例共享

代理模式

为另一个对象提供一个替身或占位符以控制对这个对象的访问

使用代理模式创建代表(representative)对象,让代表对象控制某对象的访问被代理的对象可以是远程的对象,创建开销打的对象或需要安全的控制对象

要点

1.代理模式为另一个对象提供代表,以便控制客户对对象的访问,管理访问的方式有许多种

2.远程代理管理客户和远程对象之间的交互

3.虚拟代理控制访问实例化开销大的对象

4.保护代理基于调用者控制对象方法的访问

5.代理模式有许多变体,例如:缓存代理,同步代理,防火墙代理和写入时复制代理

6.代理在结构上类似装饰者,但是目的不同

7.装饰者模式为对象加上行为,而代理则是控制访问

8.Java内置的代理支持,可以根据需要建立动态代理,并将所有调用分配到所选的处理器

9.就和其他的包装者(wrapper)一样,代理会造成你的设计中类的数目增加

复合模式

要点

1.MVC是复合模式,结合了观察者模式,策略模式和组合模式

2.模型使用观察者模式,以便观察者更新,同时保持两者之间解耦

3.控制器是视图的策略,视图可以使用不同的控制器实现,得到不同的行为

4.视图使用组合模式实现用户界面,用户界面通常组合了嵌套的组件,像面板,框架和按钮

5.这些模式携手合作,把MVC模型的三层解耦,这样可以保持设计干净又有弹性

6.适配器模式用来将新的模型适配成已有的视图和控制器

7.Model2是MVC在Web上的应用

8.在Model2中,控制器实现成Serblet,而JSP/HTML实现视图

反模式

告诉你如何采用一个不好的解决方案解决一个问题

桥接模式

使用桥接模式(Bridge Pattern)不只改变你的实现,也改变你的抽象

桥接模式通过将实现和抽象放在两个不同的类层次中而使它们可以独立改变

适合使用在需要跨越多个平台的图形和窗口系统上

当需要用不同的方式改变接口和实现时,你会发现桥接模式很好用

优点

1.将实现予以解耦,让它和界面之间不再永久绑定

2.抽象和实现可以独立扩展,不会影响到对方

3.对于“具体的抽象类”所做的改变,不会影响到客户

缺点

桥接模式的缺点是增加了复杂度

生成器模式

使用生成器模式(Builder Pattern)封装一个产品的构造过程,并允许按步骤构造

经常被用来创建组合结构

优点

1.将一个复杂对象的创建过程封装起来

2.允许对象通过多个步骤来创建,并且可以改变过程(这和只有一个步骤的工厂模式不同)

3.向客户隐藏产品内部的表现

4.产品的实现可以被替换,因为客户只看到一个对象的接口

缺点

与工厂模式相比,采用生成器模式创建对象的客户,需要具备更多的领域知识

责任链模式

当你想要让一个以上的对象有机会能够处理某个请求对象的时候,就使用责任链模式(Chain of Responsibility Pattern)

经常被使用在窗口系统中,处理鼠标和键盘之类的事件

优点

将请求的发送者和接收者解耦

可以简化你的对象,因为它不需要知道链的结构

通过改变链内的成员或调动它们的次序,允许你动态地新增或者删除责任

缺点

并不保证请求一定会被执行,如果没有任何对象处理它的话,它可能会落到链尾端之外(这可以是优点也可以是缺点

可能不容易观察运行时的特征,有碍于出错

蝇量模式

如果让某个类的一个实例能够用来提供许多“虚拟实例”,就使用蝇量模式(Flyweight Pattern)

当一个类有许多的实例,而这些实例能被同一方法控制的时候,我们就可以使用蝇量模式

优点

减少运行时对象实例的个数,节省内存

将许多“虚拟”对象的状态集中管理

缺点

一旦你实现了它,那么单个的逻辑实例将无法拥有独立而不同的行为

解释器模式

使用解释器模式(Interpreter Pattern)为语言创建解释器

当你需要实现一个简单的语言时,使用解释器

当你有一个简单的语法,而且简单比效率更重要时,使用解释器

可以处理脚本语言和编程语言

优点

将每一个语法规则表示成一个类,方便于实现语言

因为语法由许多类表示,所以你可以轻易地改变或扩展此语言

通过在类结构中加入新的方法,可以在解释的同时增加新的行为,例如打印格式的美化或者进行复杂的程序验证

缺点

当语法规则的数目太大时,这个模式可能会变得非常繁杂。在这种情况下,使用解析器或编译器的产生器可能更加合适

中介者模式

使用中介者模式(Mediator Pattern)来集中相关对象之间复杂的沟通和控制方式

中介者常常被用来协调相关的GUI组件

优点

通过将对象彼此解耦,可以增加对象的复用性

通过将控制逻辑集中,可以简化系统维护

可以让对象之间所传递的消息变得简单而且大幅减少

缺点

中介者模式的缺点是,如果设计不当,中介者对象本身会变得过于复杂

备忘录模式

当你需要让对象返回之前的状态时(例如,你的用户请求“撤销”),就使用备忘录模式(MementoPattern)

备忘录用于储存状态

目的

储存系统关键对象的重要状态

维护关键对象的封装

优点

将被储存的状态放在外面,不要和关键对象混在一起,这可以帮助维护内聚

保持关键对象的数据封装

提供了容易实现的恢复能力

缺点

储存和恢复状态的过程可能相当耗时

在Java系统时,其实可以考虑使用序列化(serialization)机制储存系统的状态

原型模式

当创建给定类的实例的过程很复杂时,就使用原型模式(Prototype Pattern)

在一个复杂的类层次中,当系统必须从其中的许多类型创建新对象时,可以考虑原型

优点

向客户隐藏制造新实例的复杂性

提供让客户能够产生未知类型对象的选项

在某些环境下,复制对象比创建新对象更有效

缺点

对象的复制有时相当复杂

访问者模式

当你想要成为一个对象的组合增加新的能力,且封装并不重要时,就使用访问者模式(Visitor Pattern)

当采用访问者模式的时候,就会打破组合类的封装

优点

允许你对组合结构加入新的操作,而无需改变结构本身

想要加入新的操作,相对容易

访问者所进行的操作,其代码是集中在一起的

缺点

因为游走的功能牵涉其中,所以对组合结构的改变就更加困难

定义设计模式

模式是在某情景(context)下,针对某问题的某种解决方案

情境就是应用某个模式的情况。这应该是会不断出现的情况

问题就是你想在某情境下达到的目标,但也可以是某情境下的约束

解决方案就是你所追求的:一个通用的设计,用来解决约束,达到目标

如果你发现自己处于某个情境下,面对这所欲达到的目标被一群约束影响着的问题,然而,你能够应用某个设计,克服这些约束并达到该目标,将你领向某个解决方案

要点

1.让设计模式自然而然地出现在你的设计中,而不是为了使用而使用

2.设计模式并非僵化的教条,你可以依据自己的需要采用或调整

3.总是使用满足需要的最简单解决方案,不管它用不用模式

4.学习设计模式的类目,可以帮你自己熟悉这些模式以及它们之间的关系

5.模式的分类(或类目)是将模式分成不用的族群,如果这么做对你有帮助,就采用吧

6.你必须相当专注才能够成为一个模式的作家,这需要时间也需要耐心,同时还必须乐意做大量的精化工作

7.请牢记:你所遇到大多数的模式都是现有模式的变体,而非新的模式

8.模式能够为你带来的最大好处之一是,让你的团队拥有共享词汇

9.任何社群都有自己的行话,模式社群也是如此。别让这些行话绊着,在读完这本书之后,你已经能够应用大部分的行话了

总结

模式 描述
装饰者 包装一个对象,以提供新的行为
状态 封装了基于状态的行为,并使用委托在行为之间切换
迭代器 在对象的集合之中游走,而不暴露集合的实现
外观 简化一群类的接口
策略 封装可以互换的行为,并使用委托来决定要使用哪一个
代理 包装对象,以控制对此对象的访问
工厂方法 由子类决定要创建的具体类是哪一个
适配器 封装对象,并提供不同的接口
观察者 让对象能够在状态改变时被通知
模板方法 客户用一致的方式处理对象集合和单个对象
组合 客户用一致的方式处理对象集合和单个对象
单件(单例) 确保有且只有一个对象被创建
抽象工厂 允许客户创建对象的家族,而无需指定他们的具体类
命令 封装请求成为对象
打赏
  • 版权声明: 本博客所有文章除特别声明外,著作权归作者所有。转载请注明出处!

扫一扫,分享到微信

微信分享二维码
  • Copyrights © 2015-2025 Immanuel
  • 访问人数: | 浏览次数:

请我喝杯咖啡吧~

微信