设计模式
2020-11-27 17:44:32 0 举报
AI智能生成
设计模式详解
作者其他创作
大纲/内容
创建型 5
定义
用于描述“怎样创建对象”,它的主要特点是“将对象的创建与使用分离”。
创建型模式的主要关注点是“怎样创建对象?”,它的主要特点是“将对象的创建与使用分离”。
这样可以降低系统的耦合度,使用者不需要关注对象的创建细节,对象的创建由相关的工厂来完成。就像我们去商场购买商品时,不需要知道商品是怎么生产出来一样,因为它们由专门的厂商生产。
创建型模式分为以下几种。
单例(Singleton)模式:某个类只能生成一个实例,该类提供了一个全局访问点供外部获取该实例,其拓展是有限多例模式。
单例(Singleton)模式:某个类只能生成一个实例,该类提供了一个全局访问点供外部获取该实例,其拓展是有限多例模式。
原型(Prototype)模式:将一个对象作为原型,通过对其进行复制而克隆出多个和原型类似的新实例。
工厂方法(FactoryMethod)模式:定义一个用于创建产品的接口,由子类决定生产什么产品。
抽象工厂(AbstractFactory)模式:提供一个创建产品族的接口,其每个子类可以生产一系列相关的产品。
建造者(Builder)模式:将一个复杂对象分解成多个相对简单的部分,然后根据不同需要分别创建它们,最后构建成该复杂对象。
单例模式 Singleton
定义
指一个类只有一个实例,且该类能自行创建这个实例的一种模式。
特点
单例类只有一个实例对象;
该单例对象必须由单例类自行创建;
单例类对外提供一个访问该单例的全局访问点;
该单例对象必须由单例类自行创建;
单例类对外提供一个访问该单例的全局访问点;
别名:Singleton模式
优点
1.减少内存开支,避免反复创建和销毁。
2.只有一个实例,减少系统性能开销,特别是产生一个对象需要比较多的资源时。
3.避免对资源的多重占用,例如写文件动作,只有一个实例存在可以避免对同一个资源文件的同时写操作。
4.单例模式可以在系统设置全局的访问点,优化和共享资源访问。
缺点
1.单例模式一般无接口,扩展困难。
2.对测试不利,单例类未完成时不能进行测试。
3.与单一职责原则有冲突,单例模式把要单例的业务逻辑融合在一个类中。
2.对测试不利,单例类未完成时不能进行测试。
3.与单一职责原则有冲突,单例模式把要单例的业务逻辑融合在一个类中。
实现
懒汉式
饿汉式
静态式
案例
【例1】用懒汉式单例模式模拟产生美国当今总统对象。
【例2】用饿汉式单例模式模拟产生猪八戒对象。
扩展
原型模式 Prototype Pattern
定义
原型(Prototype)模式的定义如下:用一个已经创建的实例作为原型,通过复制该原型对象来创建一个和原型相同或相似的新对象。在这里,原型实例指定了要创建的对象的种类。用这种方式创建对象非常高效,根本无须知道对象创建的细节。
针对自我复制的模式
结构
具体原型类:实现抽象原型类的 clone() 方法,它是可被复制的对象。
访问类:使用具体原型类中的 clone() 方法来复制新的对象。
抽象原型类:规定了具体原型对象必须实现的接口。
具体原型类:实现抽象原型类的 clone() 方法,它是可被复制的对象。
访问类:使用具体原型类中的 clone() 方法来复制新的对象。
实现
【例1】用原型模式模拟“孙悟空”复制自己。
分析:孙悟空拔下猴毛轻轻一吹就变出很多孙悟空,这实际上是用到了原型模式。这里的孙悟空类 SunWukong 是具体原型类,而 Java 中的 Cloneable 接口是抽象原型类。
同前面介绍的猪八戒实例一样,由于要显示孙悟空的图像(点击此处下载该程序所要显示的孙悟空的图片),所以将孙悟空类定义成面板 JPanel 的子类,里面包含了标签,用于保存孙悟空的图像。
另外,重写了 Cloneable 接口的 clone() 方法,用于复制新的孙悟空。访问类可以通过调用孙悟空的 clone() 方法复制多个孙悟空,并在框架窗体 JFrame 中显示。图 2 所示是其结构图。
分析:孙悟空拔下猴毛轻轻一吹就变出很多孙悟空,这实际上是用到了原型模式。这里的孙悟空类 SunWukong 是具体原型类,而 Java 中的 Cloneable 接口是抽象原型类。
同前面介绍的猪八戒实例一样,由于要显示孙悟空的图像(点击此处下载该程序所要显示的孙悟空的图片),所以将孙悟空类定义成面板 JPanel 的子类,里面包含了标签,用于保存孙悟空的图像。
另外,重写了 Cloneable 接口的 clone() 方法,用于复制新的孙悟空。访问类可以通过调用孙悟空的 clone() 方法复制多个孙悟空,并在框架窗体 JFrame 中显示。图 2 所示是其结构图。
【例2】用原型模式生成“三好学生”奖状。
分析:同一学校的“三好学生”奖状除了获奖人姓名不同,其他都相同,属于相似对象的复制,同样可以用原型模式创建,然后再做简单修改就可以了。图 4 所示是三好学生奖状生成器的结构图。
分析:同一学校的“三好学生”奖状除了获奖人姓名不同,其他都相同,属于相似对象的复制,同样可以用原型模式创建,然后再做简单修改就可以了。图 4 所示是三好学生奖状生成器的结构图。
建造者模式 Builder Pattern
定义
建造者(Builder)模式的定义:指将一个复杂对象的构造与它的表示分离,使同样的构建过程可以创建不同的表示,这样的设计模式被称为建造者模式。它是将一个复杂的对象分解为多个简单的对象,然后一步一步构建而成。它将变与不变相分离,即产品的组成部分是不变的,但每一部分是可以灵活选择的。
该模式的主要优点如下:
各个具体的建造者相互独立,有利于系统的扩展。
客户端不必知道产品内部组成的细节,便于控制细节风险。
其缺点如下:
产品的组成部分必须相同,这限制了其使用范围。
如果产品的内部变化复杂,该模式会增加很多的建造者类。
建造者(Builder)模式和工厂模式的关注点不同:建造者模式注重零部件的组装过程,而工厂方法模式更注重零部件的创建过程,但两者可以结合使用。
该模式的主要优点如下:
各个具体的建造者相互独立,有利于系统的扩展。
客户端不必知道产品内部组成的细节,便于控制细节风险。
其缺点如下:
产品的组成部分必须相同,这限制了其使用范围。
如果产品的内部变化复杂,该模式会增加很多的建造者类。
建造者(Builder)模式和工厂模式的关注点不同:建造者模式注重零部件的组装过程,而工厂方法模式更注重零部件的创建过程,但两者可以结合使用。
针对复杂对象,应用组合式的方式进行创建的模式。
结构
建造者(Builder)模式的主要角色如下。
产品角色(Product):它是包含多个组成部件的复杂对象,由具体建造者来创建其各个滅部件。
抽象建造者(Builder):它是一个包含创建产品各个子部件的抽象方法的接口,通常还包含一个返回复杂产品的方法 getResult()。
具体建造者(Concrete Builder):实现 Builder 接口,完成复杂产品的各个部件的具体创建方法。
指挥者(Director):它调用建造者对象中的部件构造与装配方法完成复杂对象的创建,在指挥者中不涉及具体产品的信息。
产品角色(Product):它是包含多个组成部件的复杂对象,由具体建造者来创建其各个滅部件。
抽象建造者(Builder):它是一个包含创建产品各个子部件的抽象方法的接口,通常还包含一个返回复杂产品的方法 getResult()。
具体建造者(Concrete Builder):实现 Builder 接口,完成复杂产品的各个部件的具体创建方法。
指挥者(Director):它调用建造者对象中的部件构造与装配方法完成复杂对象的创建,在指挥者中不涉及具体产品的信息。
实现
图 1 给出了建造者(Builder)模式的主要结构,其相关类的代码如下。
(1) 产品角色:包含多个组成部件的复杂对象。
(2) 抽象建造者:包含创建产品各个子部件的抽象方法。
(3) 具体建造者:实现了抽象建造者接口。
(4) 指挥者:调用建造者中的方法完成复杂对象的创建。
(5) 客户类。
工厂方法模式 Factory Method
定义
工厂方法模式 Factory Method
定义一个创建产品对象的工厂接口,将产品对象的实际创建工作推迟到具体子工厂类当中。这满足创建型模式中所要求的“创建与使用相分离”的特点。
对简单工厂模式的进一步抽象化,其好处是可以使系统在不修改原来代码的情况下引进新的产品,即满足开闭原则。
优点
1.拥有良好的封装性。调用者需要一个具体产品,只需要向工厂获取,而不用知道创建对象的艰辛过程,降低模块间的耦合。
2.易于扩展。增加产品时,只需要适当修改具体的工厂类或者扩展一个工厂,甚至不用修改。
3.屏蔽产品类,解除耦合。产品类的实现如何变化,调用者都不需要关心,只需要关心产品的接口即可。
缺点
每增加一个产品就要增加一个具体产品类和一个对应的具体工厂类,这增加了系统的复杂度。
结构
工厂方法模式由抽象工厂、具体工厂、抽象产品和具体产品等4个要素构成。本节来分析其基本结构和实现方法。
抽象工厂(Abstract Factory):提供了创建产品的接口,调用者通过它访问具体工厂的工厂方法 newProduct() 来创建产品。
具体工厂(ConcreteFactory):主要是实现抽象工厂中的抽象方法,完成具体产品的创建。
抽象产品(Product):定义了产品的规范,描述了产品的主要特性和功能。
具体产品(ConcreteProduct):实现了抽象产品角色所定义的接口,由具体工厂来创建,它同具体工厂之间一一对应。
实现
女娲造人
大家都听过女娲捏土造人的神话故事。这软件开发中,这个过程设计到三个对象:女娲、八卦炉、不同肤色的人。女娲可以使用场景类 Client 表示,八卦炉类似于一个工厂 Factory,负责制造具体的产品 Product(即人类 Human)。
工厂生成
工厂通过工具,生成出各式各样的产品。
扩展?
简单工厂模式
静态工厂模式
应用
抽象工厂模式 Abstract Factory
定义
使用抽象工厂模式一般要满足以下条件。
系统中有多个产品族,每个具体工厂创建同一族但属于不同等级结构的产品。
系统一次只可能消费其中某一族产品,即同族的产品一起使用。
抽象工厂模式除了具有工厂方法模式的优点外,其他主要优点如下。
可以在类的内部对产品族中相关联的多等级产品共同管理,而不必专门引入多个新的类来进行管理。
当增加一个新的产品族时不需要修改原代码,满足开闭原则。
其缺点是:当产品族中需要增加一个新的产品时,所有的工厂类都需要进行修改。
是一种为访问类提供一个创建一组相关或相互依赖对象的接口,且访问类无须指定所要产品的具体类就能得到同族的不同等级的产品的模式结构。
抽象工厂模式是工厂方法模式的升级版本,工厂方法模式只生产一个等级的产品,而抽象工厂模式可生产多个等级的产品。
使用抽象工厂模式一般要满足以下条件。
系统中有多个产品族,每个具体工厂创建同一族但属于不同等级结构的产品。
系统一次只可能消费其中某一族产品,即同族的产品一起使用。
抽象工厂模式除了具有工厂方法模式的优点外,其他主要优点如下。
可以在类的内部对产品族中相关联的多等级产品共同管理,而不必专门引入多个新的类来进行管理。
当增加一个新的产品族时不需要修改原代码,满足开闭原则。
其缺点是:当产品族中需要增加一个新的产品时,所有的工厂类都需要进行修改。
结构
抽象工厂模式同工厂方法模式一样,也是由抽象工厂、具体工厂、抽象产品和具体产品等 4 个要素构成,但抽象工厂中方法个数不同,抽象产品的个数也不同。现在我们来分析其基本结构和实现方法。
1. 模式的结构
抽象工厂模式的主要角色如下。
抽象工厂(Abstract Factory):提供了创建产品的接口,它包含多个创建产品的方法 newProduct(),可以创建多个不同等级的产品。
具体工厂(Concrete Factory):主要是实现抽象工厂中的多个抽象方法,完成具体产品的创建。
抽象产品(Product):定义了产品的规范,描述了产品的主要特性和功能,抽象工厂模式有多个抽象产品。
具体产品(ConcreteProduct):实现了抽象产品角色所定义的接口,由具体工厂来创建,它 同具体工厂之间是多对一的关系。
1. 模式的结构
抽象工厂模式的主要角色如下。
抽象工厂(Abstract Factory):提供了创建产品的接口,它包含多个创建产品的方法 newProduct(),可以创建多个不同等级的产品。
具体工厂(Concrete Factory):主要是实现抽象工厂中的多个抽象方法,完成具体产品的创建。
抽象产品(Product):定义了产品的规范,描述了产品的主要特性和功能,抽象工厂模式有多个抽象产品。
具体产品(ConcreteProduct):实现了抽象产品角色所定义的接口,由具体工厂来创建,它 同具体工厂之间是多对一的关系。
实现
【例1】用抽象工厂模式设计农场类。
分析:农场中除了像畜牧场一样可以养动物,还可以培养植物,如养马、养牛、种菜、种水果等,所以本实例比前面介绍的畜牧场类复杂,必须用抽象工厂模式来实现。
本例用抽象工厂模式来设计两个农场,一个是韶关农场用于养牛和种菜,一个是上饶农场用于养马和种水果,可以在以上两个农场中定义一个生成动物的方法 newAnimal() 和一个培养植物的方法 newPlant()。
对马类、牛类、蔬菜类和水果类等具体产品类,由于要显示它们的图像(点此下载图片),所以它们的构造函数中用到了 JPanel、JLabel 和 ImageIcon 等组件,并定义一个 show() 方法来显示它们。
客户端程序通过对象生成器类 ReadXML 读取 XML 配置文件中的数据来决定养什么动物和培养什么植物(点此下载 XML 文件)。其结构图如图 3 所示。
分析:农场中除了像畜牧场一样可以养动物,还可以培养植物,如养马、养牛、种菜、种水果等,所以本实例比前面介绍的畜牧场类复杂,必须用抽象工厂模式来实现。
本例用抽象工厂模式来设计两个农场,一个是韶关农场用于养牛和种菜,一个是上饶农场用于养马和种水果,可以在以上两个农场中定义一个生成动物的方法 newAnimal() 和一个培养植物的方法 newPlant()。
对马类、牛类、蔬菜类和水果类等具体产品类,由于要显示它们的图像(点此下载图片),所以它们的构造函数中用到了 JPanel、JLabel 和 ImageIcon 等组件,并定义一个 show() 方法来显示它们。
客户端程序通过对象生成器类 ReadXML 读取 XML 配置文件中的数据来决定养什么动物和培养什么植物(点此下载 XML 文件)。其结构图如图 3 所示。
应用
结构性 7
定义
用于描述如何将类或对象按某种布局组成更大的结构。
由于组合关系或聚合关系比继承关系耦合度低,满足“合成复用原则”,所以对象结构型模式比类结构型模式具有更大的灵活性。
结构型模式分为以下 7 种:
代理(Proxy)模式:为某对象提供一种代理以控制对该对象的访问。即客户端通过代理间接地访问该对象,从而限制、增强或修改该对象的一些特性。
适配器(Adapter)模式:将一个类的接口转换成客户希望的另外一个接口,使得原本由于接口不兼容而不能一起工作的那些类能一起工作。
桥接(Bridge)模式:将抽象与实现分离,使它们可以独立变化。它是用组合关系代替继承关系来实现的,从而降低了抽象和实现这两个可变维度的耦合度。
装饰(Decorator)模式:动态地给对象增加一些职责,即增加其额外的功能。
外观(Facade)模式:为多个复杂的子系统提供一个一致的接口,使这些子系统更加容易被访问。
享元(Flyweight)模式:运用共享技术来有效地支持大量细粒度对象的复用。
组合(Composite)模式:将对象组合成树状层次结构,使用户对单个对象和组合对象具有一致的访问性。
代理(Proxy)模式:为某对象提供一种代理以控制对该对象的访问。即客户端通过代理间接地访问该对象,从而限制、增强或修改该对象的一些特性。
适配器(Adapter)模式:将一个类的接口转换成客户希望的另外一个接口,使得原本由于接口不兼容而不能一起工作的那些类能一起工作。
桥接(Bridge)模式:将抽象与实现分离,使它们可以独立变化。它是用组合关系代替继承关系来实现的,从而降低了抽象和实现这两个可变维度的耦合度。
装饰(Decorator)模式:动态地给对象增加一些职责,即增加其额外的功能。
外观(Facade)模式:为多个复杂的子系统提供一个一致的接口,使这些子系统更加容易被访问。
享元(Flyweight)模式:运用共享技术来有效地支持大量细粒度对象的复用。
组合(Composite)模式:将对象组合成树状层次结构,使用户对单个对象和组合对象具有一致的访问性。
适配器模式 Adapter
定义
将一个类的接口变换成客户端所期待的另一种接口,从而使原本因接口不匹配而无法在一起工作的两个类能够在一起工作。
角色
目标对象
被适配者
适配器
该角色即适配器模式的核心角色,它的职责十分简单,就是把 Adaptee 转化成 Target,怎么转化?通过继承或者类关联的方式。
场景
装饰器模式 Decorator
定义
动态地给一个对象添加一些额外的职责。就增加功能来说, 装饰模式相比生成子类更为灵活。
角色
场景
需要扩展一个类的功能, 或给一个类增加附加功能。
需要动态地给一个对象增加功能, 这些功能可以再动态地撤销。
需要为一批的兄弟类进行改装或加装功能, 当然是首选装饰模式。
代理模式 Proxy
定义
为其它对象提供一种代理,以控制对这对象的访问。
角色
1.Subject 抽象主题角色(共同接口)
可以是抽象类或者接口,是一个最普通的业务类型定义,无特殊要求。
可以是抽象类或者接口,是一个最普通的业务类型定义,无特殊要求。
2.RealSubject 具体主题角色(真实对象)
也叫被代理角色。是业务逻辑的具体执行者。
也叫被代理角色。是业务逻辑的具体执行者。
3.Proxy 代理主题角色(代理对象)
也叫代理类。负责对真实角色的应用,把所有抽象主题类定义的方法限制委托给真实主题角色去实现,并且在真实主题角色处理完毕前后做预处理和善后操作。
也叫代理类。负责对真实角色的应用,把所有抽象主题类定义的方法限制委托给真实主题角色去实现,并且在真实主题角色处理完毕前后做预处理和善后操作。
优点
1.职责清晰
真实角色就是实现实际的业务逻辑,而不需要关心其他非本职工作(例如前处理、后处理),通过代理完成一件事务,结果就是编程简洁清晰。
真实角色就是实现实际的业务逻辑,而不需要关心其他非本职工作(例如前处理、后处理),通过代理完成一件事务,结果就是编程简洁清晰。
2.高扩展性
具体的主题角色是随时都可能发生变化的,当需要改变时,通过代理类,我们就可以做到不修改原角色下实现扩展。
具体的主题角色是随时都可能发生变化的,当需要改变时,通过代理类,我们就可以做到不修改原角色下实现扩展。
场景
打官司:打官司为什么要找个律师?因为你不想参与到中间过程的是是非非,只需要完成自己的答辩,其它诸如事前调查、事后追查都由律师来搞定,这就是为了减轻你的负担。
财务代理
扩展
静态代理
动态代理
外观模式 Facade
定义
外观(Facade)模式的定义:是一种通过为多个复杂的子系统提供一个一致的接口,而使这些子系统更加容易被访问的模式。该模式对外有一个统一接口,外部应用程序不用关心内部子系统的具体的细节,这样会大大降低应用程序的复杂度,提高了程序的可维护性。
在现实生活中,常常存在办事较复杂的例子,如办房产证或注册一家公司,有时要同多个部门联系,这时要是有一个综合部门能解决一切手续问题就好了。
软件设计也是这样,当一个系统的功能越来越强,子系统会越来越多,客户对系统的访问也变得越来越复杂。这时如果系统内部发生改变,客户端也要跟着改变,这违背了“开闭原则”,也违背了“迪米特法则”,所以有必要为多个子系统提供一个统一的接口,从而降低系统的耦合度,这就是外观模式的目标。
软件设计也是这样,当一个系统的功能越来越强,子系统会越来越多,客户对系统的访问也变得越来越复杂。这时如果系统内部发生改变,客户端也要跟着改变,这违背了“开闭原则”,也违背了“迪米特法则”,所以有必要为多个子系统提供一个统一的接口,从而降低系统的耦合度,这就是外观模式的目标。
优点
外观(Facade)模式是“迪米特法则”的典型应用,它有以下主要优点。
降低了子系统与客户端之间的耦合度,使得子系统的变化不会影响调用它的客户类。
对客户屏蔽了子系统组件,减少了客户处理的对象数目,并使得子系统使用起来更加容易。
降低了大型软件系统中的编译依赖性,简化了系统在不同平台之间的移植过程,因为编译一个子系统不会影响其他的子系统,也不会影响外观对象。
缺点
外观(Facade)模式的主要缺点如下。
外观(Facade)模式的主要缺点如下。
不能很好地限制客户使用子系统类。
增加新的子系统可能需要修改外观类或客户端的源代码,违背了“开闭原则”。
结构
外观(Facade)角色:为多个子系统对外提供一个共同的接口。
子系统(Sub System)角色:实现系统的部分功能,客户可以通过外观角色访问它。
客户(Client)角色:通过一个外观角色访问各个子系统的功能。
场景
抽象外观模式
在外观模式中,当增加或移除子系统时需要修改外观类,这违背了“开闭原则”。如果引入抽象外观类,则在一定程度上解决了该问题.
应用
对分层结构系统构建时,使用外观模式定义子系统中每层的入口点可以简化子系统之间的依赖关系。
当一个复杂系统的子系统很多时,外观模式可以为系统设计一个简单的接口供外界访问。
当客户端与多个子系统之间存在很大的联系时,引入外观模式可将它们分离,从而提高子系统的独立性和可移植性。
桥梁模式 Bridge
定义
桥接(Bridge)模式的定义如下:将抽象与实现分离,使它们可以独立变化。它是用组合关系代替继承关系来实现,从而降低了抽象和实现这两个可变维度的耦合度。
桥接(Bridge)模式的优点是:
1. 由于抽象与实现分离,所以扩展能力强;
2. 其实现细节对客户透明。
缺点是:由于聚合关系建立在抽象层,要求开发者针对抽象化进行设计与编程,这增加了系统的理解与设计难度。
1. 由于抽象与实现分离,所以扩展能力强;
2. 其实现细节对客户透明。
缺点是:由于聚合关系建立在抽象层,要求开发者针对抽象化进行设计与编程,这增加了系统的理解与设计难度。
业务抽象角色引用业务实现角色。
可以将抽象化部分与实现化部分分开,取消二者的继承关系,改用组合关系。
结构
抽象化(Abstraction)角色:定义抽象类,并包含一个对实现化对象的引用。
扩展抽象化(Refined Abstraction)角色:是抽象化角色的子类,实现父类中的业务方法,并通过组合关系调用实现化角色中的业务方法。
实现化(Implementor)角色:定义实现化角色的接口,供扩展抽象化角色调用。
应用场景
【例1】用桥接(Bridge)模式模拟女士皮包的选购。
分析:女士皮包有很多种,可以按用途分、按皮质分、按品牌分、按颜色分、按大小分等,存在多个维度的变化,所以采用桥接模式来实现女士皮包的选购比较合适。
本实例按用途分可选钱包(Wallet)和挎包(HandBag),按颜色分可选黄色(Yellow)和红色(Red)。可以按两个维度定义为颜色类和包类。
颜色类(Color)是一个维度,定义为实现化角色,它有两个具体实现化角色:黄色和红色,通过 getColor() 方法可以选择颜色;包类(Bag)是另一个维度,定义为抽象化角色,它有两个扩展抽象化角色:挎包和钱包,它包含了颜色类对象,通过 getName() 方法可以选择相关颜色的挎包和钱包。
本实例按用途分可选钱包(Wallet)和挎包(HandBag),按颜色分可选黄色(Yellow)和红色(Red)。可以按两个维度定义为颜色类和包类。
颜色类(Color)是一个维度,定义为实现化角色,它有两个具体实现化角色:黄色和红色,通过 getColor() 方法可以选择颜色;包类(Bag)是另一个维度,定义为抽象化角色,它有两个扩展抽象化角色:挎包和钱包,它包含了颜色类对象,通过 getName() 方法可以选择相关颜色的挎包和钱包。
组合模式 Composite
定义
定义
组合(Composite)模式的定义:有时又叫作部分-整体模式,它是一种将对象组合成树状的层次结构的模式,用来表示“部分-整体”的关系,使用户对单个对象和组合对象具有一致的访问性。
优点
组合模式使得客户端代码可以一致地处理单个对象和组合对象,无须关心自己处理的是单个对象,还是组合对象,这简化了客户端代码;
更容易在组合体内加入新的对象,客户端不会因为加入了新的对象而更改源代码,满足“开闭原则”;
缺点
设计较复杂,客户端需要花更多时间理清类之间的层次关系;
不容易限制容器中的构件;
不容易用继承的方法来增加构件的新功能;
不容易限制容器中的构件;
不容易用继承的方法来增加构件的新功能;
结构
抽象构件(Component)角色:它的主要作用是为树叶构件和树枝构件声明公共接口,并实现它们的默认行为。在透明式的组合模式中抽象构件还声明访问和管理子类的接口;在安全式的组合模式中不声明访问和管理子类的接口,管理工作由树枝构件完成。
1. 树叶构件(Leaf)角色:是组合中的叶节点对象,它没有子节点,用于实现抽象构件角色中 声明的公共接口。
1. 树枝构件(Composite)角色:是组合中的分支节点对象,它有子节点。它实现了抽象构件角色中声明的接口,它的主要作用是存储和管理子部件,通常包含 Add()、Remove()、GetChild() 等方法。
组合模式分为透明式的组合模式和安全式的组合模式。
(1) 透明方式:在该方式中,由于抽象构件声明了所有子类中的全部方法,所以客户端无须区别树叶对象和树枝对象,对客户端来说是透明的。但其缺点是:树叶构件本来没有 Add()、Remove() 及 GetChild() 方法,却要实现它们(空实现或抛异常),这样会带来一些安全性问题。其结构图如图 1 所示。
(1) 透明方式:在该方式中,由于抽象构件声明了所有子类中的全部方法,所以客户端无须区别树叶对象和树枝对象,对客户端来说是透明的。但其缺点是:树叶构件本来没有 Add()、Remove() 及 GetChild() 方法,却要实现它们(空实现或抛异常),这样会带来一些安全性问题。其结构图如图 1 所示。
应用
在现实生活中,存在很多“部分-整体”的关系,例如,大学中的部门与学院、总公司中的部门与分公司、学习用品中的书与书包、生活用品中的衣月艮与衣柜以及厨房中的锅碗瓢盆等。在软件开发中也是这样,例如,文件系统中的文件与文件夹、窗体程序中的简单控件与容器控件等。对这些简单对象与复合对象的处理,如果用组合模式来实现会很方便。
享元模式 Flyweight
定义
享元(Flyweight)模式的定义:运用共享技术来有効地支持大量细粒度对象的复用。它通过共享已经存在的又橡来大幅度减少需要创建的对象数量、避免大量相似类的开销,从而提高系统资源的利用率。
享元模式的主要优点是:相同对象只要保存一份,这降低了系统中对象的数量,从而降低了系统中细粒度对象给内存带来的压力。
其主要缺点是:
为了使对象可以共享,需要将一些不能共享的状态外部化,这将增加程序的复杂性。
读取享元模式的外部状态会使得运行时间稍微变长。
享元模式的主要优点是:相同对象只要保存一份,这降低了系统中对象的数量,从而降低了系统中细粒度对象给内存带来的压力。
其主要缺点是:
为了使对象可以共享,需要将一些不能共享的状态外部化,这将增加程序的复杂性。
读取享元模式的外部状态会使得运行时间稍微变长。
结构
享元模式中存在以下两种状态:
内部状态,即不会随着环境的改变而改变的可共享部分;
外部状态,指随环境改变而改变的不可以共享的部分。享元模式的实现要领就是区分应用中的这两种状态,并将外部状态外部化。下面来分析其基本结构和实现方法。
1. 模式的结构
享元模式的主要角色有如下。
抽象享元角色(Flyweight):是所有的具体享元类的基类,为具体享元规范需要实现的公共接口,非享元的外部状态以参数的形式通过方法传入。
具体享元(Concrete Flyweight)角色:实现抽象享元角色中所规定的接口。
非享元(Unsharable Flyweight)角色:是不可以共享的外部状态,它以参数的形式注入具体享元的相关方法中。
享元工厂(Flyweight Factory)角色:负责创建和管理享元角色。当客户对象请求一个享元对象时,享元工厂检査系统中是否存在符合要求的享元对象,如果存在则提供给客户;如果不存在的话,则创建一个新的享元对象。
图 1 是享元模式的结构图。图中的 UnsharedConcreteFlyweight 是与淳元角色,里面包含了非共享的外部状态信息 info;而 Flyweight 是抽象享元角色,里面包含了享元方法 operation(UnsharedConcreteFlyweight state),非享元的外部状态以参数的形式通过该方法传入;ConcreteFlyweight 是具体享元角色,包含了关键字 key,它实现了抽象享元接口;FlyweightFactory 是享元工厂角色,它逝关键字 key 来管理具体享元;客户角色通过享元工厂获取具体享元,并访问具体享元的相关方法。
内部状态,即不会随着环境的改变而改变的可共享部分;
外部状态,指随环境改变而改变的不可以共享的部分。享元模式的实现要领就是区分应用中的这两种状态,并将外部状态外部化。下面来分析其基本结构和实现方法。
1. 模式的结构
享元模式的主要角色有如下。
抽象享元角色(Flyweight):是所有的具体享元类的基类,为具体享元规范需要实现的公共接口,非享元的外部状态以参数的形式通过方法传入。
具体享元(Concrete Flyweight)角色:实现抽象享元角色中所规定的接口。
非享元(Unsharable Flyweight)角色:是不可以共享的外部状态,它以参数的形式注入具体享元的相关方法中。
享元工厂(Flyweight Factory)角色:负责创建和管理享元角色。当客户对象请求一个享元对象时,享元工厂检査系统中是否存在符合要求的享元对象,如果存在则提供给客户;如果不存在的话,则创建一个新的享元对象。
图 1 是享元模式的结构图。图中的 UnsharedConcreteFlyweight 是与淳元角色,里面包含了非共享的外部状态信息 info;而 Flyweight 是抽象享元角色,里面包含了享元方法 operation(UnsharedConcreteFlyweight state),非享元的外部状态以参数的形式通过该方法传入;ConcreteFlyweight 是具体享元角色,包含了关键字 key,它实现了抽象享元接口;FlyweightFactory 是享元工厂角色,它逝关键字 key 来管理具体享元;客户角色通过享元工厂获取具体享元,并访问具体享元的相关方法。
应用
行为型 11
定义
用于描述类或对象之间怎样相互协作共同完成单个对象都无法单独完成的任务,以及怎样分配职责。
策略模式 Strategy
定义
定义一组算法, 将每个算法都封装起来, 并且使它们之间可以互换。
使用策略【替换算法】
角色
Context 封装角色
Strategy 抽象策略角色
ConcreteStrategy 具体策略角色
场景模拟
场景
多个类只有在算法或行为上稍有不同的场景。
算法需要自由切换的场景。
需要屏蔽算法规则的场景。
我们做同样一件事情时,完成这件事情的方法有很多种。这里的方法就是策略。
观察者模式 Observer
定义
发布订阅模式(Publish/subscribe)
观察者模式(ObserverPattern)
定义对象间一种一对多的依赖关系,使得每当一个对象改变状态,则所有依赖于它的对象都会得到通知并被自动更新。
一种一对多的关系「一个被观察者对应多个观察者」,当被观察者的状态发生改变时,所有观察者都会得到通知。
角色
Subject:就是“被观察”的角色,它将所有观察者对象的引用保存在一个集合中。
Observer:是抽象的“观察”角色,它定义了一个更新接口,使得在被观察者状态发生改变时通知自己。
ConcreteObserver:具体的观察者。
场景
事件多级触发场景 。
跨系统的消息交换场景,如消息队列的处理机制。
关联行为场景。关联行为是可拆分的,而不是组合关系。
模版方法模式 Template Function
定义
使用模板方法【变换算法】。
父类定义一系列方法,并通过一个模板方法控制流程,而这些方法由子类去实现。
优点
1.封装不变部分,扩展可变部分
2.提取公共部分代码,便于维护
3.行为由父类控制,子类实现
场景
1.多个子类有公有方法,且逻辑基本相同时。
2.重构时,模版方法是常使用的一个模式,把相同的代码抽取到父类中,然后可以通过钩子函数(下面介绍)约束其行为。
3. 车子举例:假设需要定义一个汽车的模型,它有一些启动、引擎轰鸣、鸣笛、停止、行驶等方法。其中 run() 方法就是一个模版方法,它是所有子类公共的部分代码,且定义了一系列基本方法的执行顺序(执行逻辑)。而 start()、stop() ... 等方法便是一系列子类去实现的基本方法,例如不同的车会有不同的启动方式、不同的鸣笛声音等等。
扩展
钩子函数
由外界条件的改变,影响到模版方法的执行,这种方法就叫做钩子函数(Hook Method)。
有了钩子函数,我们就可以更灵活的控制模版方法的执行了。
迭代器模式 Iterator
定义
提供一种方法顺序访问一个聚合对象中各个元素,而又不暴露该对象的内部表示。
角色
场景
1.当你需要访问一个聚集对象,而且不管这些对象是什么都需要遍历的时候,你就应该考虑用迭代器模式。
2.你需要对聚集有多种遍历方式时,可以考虑使用迭代器模式。
3.为遍历不同的聚集结构提供如开始,下一个,是否结束,当前哪一项等统一接口
2.你需要对聚集有多种遍历方式时,可以考虑使用迭代器模式。
3.为遍历不同的聚集结构提供如开始,下一个,是否结束,当前哪一项等统一接口
责任链模式 Chain of Responsibility
命令模式 Command
定义
将一个请求封装成一个对象,从而让你使用不同的请求把客户端参数化,对请求排队或者记录请求的日志,可以提供命令的撤销和恢复功能。
简单的说,命令模式是实现的 “行为请求者(Invoker)” 与 “行为实现者(Receiver)” 间的解耦,将请求封装成一个命令,调用者只需要执行相应的 “命令(Command)” 即可。
角色
1. Receiver 接受者、行为执行者
该角色就是具体的干活者,命令传递到这里就是被执行的。
该角色就是具体的干活者,命令传递到这里就是被执行的。
2. Command 命令
通常该角色只是定义一个命令的抽象(接口),声明执行的方法。
通常该角色只是定义一个命令的抽象(接口),声明执行的方法。
3. ConcreteCommand 具体命令
命令接口的实现,通常代表一个具体的命令,例如 “开机” 命令。
通常我们会将 Receiver 对象封装在 Command 中,这样调用命令的 “执行” 方法时,就可以使用 Receiver 接收者去具体实现了。
命令接口的实现,通常代表一个具体的命令,例如 “开机” 命令。
通常我们会将 Receiver 对象封装在 Command 中,这样调用命令的 “执行” 方法时,就可以使用 Receiver 接收者去具体实现了。
4. Invoker 调用者、行为请求者
接收并执行命令,通常可以持有一个或多个 Command 命令。
接收并执行命令,通常可以持有一个或多个 Command 命令。
5. Client 用户
场景
1. 命令的发送者和命令执行者有不同的生命周期。命令发送了并不是立即执行。
2. 命令需要进行各种管理逻辑。
3. 需要支持撤消\重做操作。
备忘录模式 Memento
状态模式 State
定义
状态(State)模式的定义:对有状态的对象,把复杂的“判断逻辑”提取到不同的状态对象中,允许状态对象在其内部状态发生改变时改变其行为。
状态模式是一种对象行为型模式,其主要优点如下。
状态模式将与特定状态相关的行为局部化到一个状态中,并且将不同状态的行为分割开来,满足“单一职责原则”。
减少对象间的相互依赖。将不同的状态引入独立的对象中会使得状态转换变得更加明确,且减少对象间的相互依赖。
有利于程序的扩展。通过定义新的子类很容易地增加新的状态和转换。
状态模式的主要缺点如下。
状态模式的使用必然会增加系统的类与对象的个数。
状态模式的结构与实现都较为复杂,如果使用不当会导致程序结构和代码的混乱。
状态模式是一种对象行为型模式,其主要优点如下。
状态模式将与特定状态相关的行为局部化到一个状态中,并且将不同状态的行为分割开来,满足“单一职责原则”。
减少对象间的相互依赖。将不同的状态引入独立的对象中会使得状态转换变得更加明确,且减少对象间的相互依赖。
有利于程序的扩展。通过定义新的子类很容易地增加新的状态和转换。
状态模式的主要缺点如下。
状态模式的使用必然会增加系统的类与对象的个数。
状态模式的结构与实现都较为复杂,如果使用不当会导致程序结构和代码的混乱。
结构
状态模式把受环境改变的对象行为包装在不同的状态对象里,其意图是让一个对象在其内部状态改变的时候,其行为也随之改变。现在我们来分析其基本结构和实现方法。
1. 模式的结构
状态模式包含以下主要角色。
环境(Context)角色:也称为上下文,它定义了客户感兴趣的接口,维护一个当前状态,并将与状态相关的操作委托给当前状态对象来处理。
抽象状态(State)角色:定义一个接口,用以封装环境对象中的特定状态所对应的行为。
具体状态(Concrete State)角色:实现抽象状态所对应的行为。
1. 模式的结构
状态模式包含以下主要角色。
环境(Context)角色:也称为上下文,它定义了客户感兴趣的接口,维护一个当前状态,并将与状态相关的操作委托给当前状态对象来处理。
抽象状态(State)角色:定义一个接口,用以封装环境对象中的特定状态所对应的行为。
具体状态(Concrete State)角色:实现抽象状态所对应的行为。
场景
【例1】用“状态模式”设计一个学生成绩的状态转换程序。
分析:本实例包含了“不及格”“中等”和“优秀” 3 种状态,当学生的分数小于 60 分时为“不及格”状态,当分数大于等于 60 分且小于 90 分时为“中等”状态,当分数大于等于 90 分时为“优秀”状态,我们用状态模式来实现这个程序。
首先,定义一个抽象状态类(AbstractState),其中包含了环境属性、状态名属性和当前分数属性,以及加减分方法 addScore(intx) 和检查当前状态的抽象方法 checkState();然后,定义“不及格”状态类 LowState、“中等”状态类 MiddleState 和“优秀”状态类 HighState,它们是具体状态类,实现 checkState() 方法,负责检査自己的状态,并根据情况转换;最后,定义环境类(ScoreContext),其中包含了当前状态对象和加减分的方法 add(int score),客户类通过该方法来改变成绩状态。图 2 所示是其结构图。
分析:本实例包含了“不及格”“中等”和“优秀” 3 种状态,当学生的分数小于 60 分时为“不及格”状态,当分数大于等于 60 分且小于 90 分时为“中等”状态,当分数大于等于 90 分时为“优秀”状态,我们用状态模式来实现这个程序。
首先,定义一个抽象状态类(AbstractState),其中包含了环境属性、状态名属性和当前分数属性,以及加减分方法 addScore(intx) 和检查当前状态的抽象方法 checkState();然后,定义“不及格”状态类 LowState、“中等”状态类 MiddleState 和“优秀”状态类 HighState,它们是具体状态类,实现 checkState() 方法,负责检査自己的状态,并根据情况转换;最后,定义环境类(ScoreContext),其中包含了当前状态对象和加减分的方法 add(int score),客户类通过该方法来改变成绩状态。图 2 所示是其结构图。
访问者模式 Visitor
定义
角色
场景
中介者模式 Mediator
定义
角色
场景
解释器模式 Interpreter
定义
给定一门语言,定义它的文法的一种表示,并定义一个解释器,该解释器使用该表示来解释语言中的句子。
角色
AbstractExpression 抽象解释器
TerminalExpression 终结符表达式
NonterminalExpression 非终结符表达式
场景
一些简单或者标准的语法需要解释的场景
例如 SQL 语法分析,金融中大量的运算等。不过这些部分也逐渐被专门工具所替代了。
例如 SQL 语法分析,金融中大量的运算等。不过这些部分也逐渐被专门工具所替代了。
数据分析工具、报表设计工具、科学计算工具等
重复发生的问题
例如多个服务器产生的大量日志,需要对日志进行分类处理,文件格式可能不同但是数据要素都是一样的,按照解释器的说法就是终结符表达式都是一样的,不过非终结符表达式需要制定。
例如多个服务器产生的大量日志,需要对日志进行分类处理,文件格式可能不同但是数据要素都是一样的,按照解释器的说法就是终结符表达式都是一样的,不过非终结符表达式需要制定。
扩展型 5
规则模式
对象池模式
雇工模式
黑板模式
空对象模式
0 条评论
下一页