隐约雷鸣,阴霾天空,但盼风雨来,能留你在此
隐约雷鸣,阴霾天空,即使天无雨,我亦留此地
设计模式真的是学了忘,忘了学,学了又忘,学的时候想着优化的地方还挺多,要用到的时候根本想不起来。
多学一点东西,多一种思考方式,虽然不一定用得到,但是积累到某个时刻一定会有回报的。
创建型模式
工厂模式
工厂方法模式是一种创建型设计模式, 其在父类中提供一个创建对象的方法, 允许子类决定实例化对象的类型。
使用场景:
- 当你在编写代码的过程中,无法预知对象确切类别及其依赖关系时。
- 如果你希望用户能扩展你软件库或框架的内部组件。
- 如果你希望复用现有对象来节省系统资源,而不是每次都重新创建对象。
实现方法:
让所有产品都遵循同一接口。该接口必须声明对所有产品都有意义的方法。
在创建类中添加一个空的工厂方法。该方法饭的饭会类型必须遵循通用的产品接口。
在创建者代码中找到对于产品构造函数的所有引用。将它们依次替换为对于工厂方法的调用,同时将产品的代码移入工厂方法。你可能需要在工厂方法中添加临时参数俩控制饭会的产品类型。
为工厂方法中的每种产品编写一个创建者子类,然后在子类中重写工厂方法,并将基于方法中的相关创建代码移动到工厂方法中。
如果应用中的产品类型太多,那么为每个产品创建子类并无太大必要,这时可以在子类中复用基类中的控制参数。
例如,设想你有以下一些层次结构的类。基类
邮件
及其子类航空邮件
和陆路邮件
;运输
及其子类飞机
,卡车
,火车
。航空邮件
仅使用飞机
对象,而陆路邮件
则会同时使用卡车
和火车
对象。你可以编写一个新的子类(例如火车邮件
)来处理这两种情况,但是还有其他可选的方案。客户端代码可以给陆路邮件
类传递一个参数,用于控制其希望获得的产品。如果代码经过上述移动后,基础工厂方法中已经没有任何代码,你可以将其转变为抽象类。如果基础工厂方法中还有其他语句,你可以将其设置为该方法的默认行为。
抽象工厂模式
抽象工厂模式是一种创建型设计模式,它能创建一系列相关的对象,而无需指定其具体类。
使用场景:
- 如果代码需要与多个不同系列的相关产品交互,但是由于无法提前获取相关信息,或者处于对未来扩展性的考虑,你不希望代码基于产品的具体类进行构建。
- 如果你有一个基于一组抽象方法的类,且其主要功能因此变得不明确。
实现方法:
- 以不同的产品类型与产品变体为维度绘制矩阵。
- 为所有产品声明抽象产品接口。然后让所有具体产品类实现这些接口。
- 声明抽象工厂接口,并且在接口中为所有抽象产品提供一组构建方法。
- 为每种产品变体实现一个具体工厂类。
- 在应用程序中开发初始化代码。改代码根据应用程序配置或当前环境,对特定具体工厂类进行初始化。然后该工厂对象床底给所有需要创建产品的类。
- 找出代码中所有对产品构造函数的直接调用,将其替换为对工厂对象中相应构建方法的调用。
生成器模式(建造者设计模式)
生成器模式是一种创建型设计模式,使你能够分步骤创建复杂对象。该模式允许你是用相同的创建代码生成不同类型和形式的对象。
使用场景:
使用生成器模式可避免“重叠构造函数”的出现。
当你希望使用代码创建不同形式的产品(例如石头或木头房屋)时。
如果你需要创建的各种形式的产品, 它们的制造过程相似且仅有细节上的差异, 此时可使用生成器模式。
使用生成器构造组合树或其他复杂对象。
实现方法:
- 清晰地定义通用步骤,确保它们可以制造所有形式的产品。否则你将无法进一步实施该模式。
- 在基本生成器接口中声明这些步骤。
- 为每个形式的产品创建具体生成器类,并实现其构造步骤。
- 考虑创建主管类。它可以使用同一生成器对象来封装多种构造产品的方式。
- 客户端代码会同时创建生成器和主管对象。构造开始前,客户端必须将生成器对象传递给主管对象。通常情况下,客户端只需要调用主管类构造函数一次即可。主管类使用生成器对象完成后续所有制造任务。还有一种方式,那就是客户端可以将生成器对象直接传递给主管类的制造方法。
- 只有在所有产品都遵循相同接口的情况下,构造结果可以直接通过主管类获取。否则,客户端应当通过生成器获取构造结果。
原文参加Refactoring.Guru
原型设计模式
原型模式是一种创建型设计模式,使你能够复制已有对象,而又无需使代码上依赖它所属的类。
即,构造函数向外暴露出一个 clone(暂且叫这个名字吧)的方法,实例对象创建的时候也有这个方法,可以通过示例对象的 clone 去克隆出一个一样的对象,而不用去重新实例化一个。其中引用类型的克隆是在堆里面直接新开一个空间来存放的,而不是跟第一个示例对象一样的地址。
使用场景:
- 如果你需要复制一些对象,同时又希望代码独立于这些对象所属的具体类,可以使用原型模式。
- 如果子类的区别仅在于其对象的初始化方式,那么你可以使用该模式来减少子类的数量。别人创建这些子类的目的可能是为了创建特定类型的对象。
实现方法:
- 创建原型接口,并在其中声明克隆方法。如果你已有类层次结构,则只需在其所有类中添加该方法即可。
- 圆形类必须另行定义一个以该类对象为参数的构造函数。构造函数必须复制参数对象中的所有成员变量值到新建实体中。如果你需要修改子类,则必须调用父类构造函数,让父类复制其私有成员变量值。
- 克隆方法通常只需要一行代码:使用
new
运算符调用原型版本的构造函数。注意,每个类都必须显示重写克隆方法并使用自身类名调用new
运算符。否则克隆方法可能会生成父类的对象。 - 你还可以创建一个中心化原型注册表,用于存储常用原型。
单例模式
单例模式是一种创建型设计模式,让你能够保证一个类只有一个示例,并提供一个访问该实例的全局节点。
单例模式同时解决了两个问题(保证一个类只有一个示例,为该示例提供一个全局访问节点),所以违反了单一职责原则。
所有的单例的实现都包含一下两个相同的步骤:
- 将默认的构造函数设为私有,防止其他对象使用单例类的
new
运算符。 - 新建一个静态构建方法作为构造函数。该函数会“偷偷”调用私有构造函数来创建对象,并将其保存在要给静态成员变量中。此后所有对于该函数的调用都将返回这一缓存对象。
使用场景:
- 如果程序后在那个的某个类对于所有客户端都只有一个可用的实例,可以使用单例模式。
- 如果你需要更加严格地控制全局变量,可以使用单例模式。
实现方法:
- 在类中添加一个私有静态成员变量用于保存单例实例。
- 声明一个公有静态构建方法用于获取单例实例。
- 在静态方法中实现“盐池初始化”。该方法每次被调用时都返回该实例。
- 该类的构造函数设为私有。类的静态方法仍能调用构造函数,但是其他对象不能调用。
- 检查客户端代码,将对单例的构造函数的调用替换为对静态构建方法的调用。
结构型模式
适配器模式
适配器模式是一种结构型设计模式,它能使接口不兼容的对象能够相互合作。
基于一些遗留代码的系统常常会使用该模式。
说白了适配器模式不过就是传入参数的格式不同,但是返回的格式相同,而我们在类的内部通过一些不为人知的方法将其转化成相同的格式而已。
使用场景:
当你希望使用某个类,但是其接口与其他代码不兼容时,可以使用适配器类。
适配器模式允许你创建一个中间类层,其可作为代码与遗留类、第三方类或者提供怪异接口的类之间的转换器。
如果你需要复用这样一些类,它们处于同一个继承体系,并且它们又有了额外的一些共同方法,但是这些共同的方法不是所有在这一继承体系中的子类所有的共性。
实现方式:
确保至少有两个类的接口不兼容:
- 一个无法修改(通常是第三方、遗留系统或者存在众多已有依赖的类)的功能性服务类。
- 一个或多个将受益于使用服务类的客户端类。
声明客户端接口,描述客户端如何与服务交互。
创建遵循客户端接口的适配器类。所有的方法暂时都为空。
在适配器类中添加一个成员变量用于保存对于服务对象的引用。通常情况下会通过构造函数对该成员变量进行初始化,但有时在调用其他方法时将该变量传递到适配器会更方便。
依次实现适配器类客户端类接口的所有方法。适配器会将实际工作委派给服务对象,自身只负责接口或数据格式的转换。
客户端必须通过客户端接口使用适配器。这样一来,就可以在不影响客户端代码的情况下修改或扩展适配器。
桥接模式
桥接模式是一种结构型设计模式,可将一个大类或一系列紧密相关的类拆分为抽象和实现两个独立的层次结构,从而能在开发时分别使用。
提出问题:问题的根本原因时我们试图在两个独立的维度——形状与颜色——上扩展形状类。这在处理类继承时是很常见的问题。
解决问题:桥接模式通过继承改为组合的方式来解决这个问题。具体来说就是抽取其中一个维度并使之成为独立的类层次,这样就可以在初始类中引用这个新层次的对象,从而使得一个类不必拥有所有的状态和行为。
使用场景:
如果你想要拆分或重组一个具有多重功能的庞杂类(例如能与多个数据库服务器进行交互的类)
桥接模式可以将庞杂类拆分为几个类层次结构。此后,可以修改任意一个类层次结构而不会影响到其他类层次结构。这种方法可以简化代码的维护工作,并肩修改已有代码的风险降到最低。
如果你希望在几个独立维度上扩展一个类
桥接模式建议将每个维度抽取为独立的类层次。初始类将相关工作委派给属于对应类层次的对象,无需自己完成所有工作。
如果你需要在运行时切换不同实现方式
实现方式
- 明确类中独立的维度。独立的概念可能是:抽象/平台,域/基础设施,前端/后端,接口/实现。
- 了解客户端的业务需求,并在抽象基类中定义它们。
- 确定在所有平台上都可执行的业务。并在通用实现接口中声明抽象部分所需的业务。
- 为你域内的所有平台创建实现类,但需确保它们遵循实现部分的接口。
- 在抽象类中添加指向实现类型的引用成员变量。抽象部分会将大部分工作委派给该成员变量所指向的实现对象。
- 如果你的高层逻辑有多个变体,则可通过扩展抽象基类为每个变体创建一个精确抽象。
- 客户端代码必须将实现对象传递给抽象部分的构造函数才能使其能够相互关联。此后,客户端只需与抽象对象进行交互,无需和实现对象打交道。
组合模式
组合模式是一种结构型设计模式,你可以使用它将对象组合成树状结构,并能像使用独立对象一样使用它们。
组合模式以递归方式处理对象树中的所有项目。
该方式最大的有点在于你无需了解构成树状结构的对象的具体类。你也无需了解对象是简单的产品还是复杂的盒子。你只需要调用通用结构以相同的方式对其进行处理即可。当你调用该方法后,对象会自动沿着树结构传递下去。
使用场景:
如果你需要实现树状对象结构,可以使用组合模式。
组合模式为你提供了两种共享公共接口的基本元素类型:简单叶节点和复杂容器。容器中可以包含叶节点和其他容器。这使得你可以构建树状桥套递归对象结构。
如果你希望客户端代码以相同方式处理简单和复杂元素,可以使用组合模式。
组合模式中定义的所有元素公用同意给接口。在这一接口的帮助下,客户端不必在意其所使用的对象的具体类。
实现方式:
确保应用的核心模型能够以树状结构表示。尝试将其分解成简单元素和容器。记住,容器必须能够同时包含简单元素和其他容器。
声明组件接口及其一系列方法,这些方法对简单和复杂元素都有意义。
创建一个叶节点类表示简单元素。程序中可以有多个不同的叶节点类。
创建一个容器类表示复杂元素。在该类中,创建一个数组成员变量来存储对于其子元素的应用。该数组必须能够同时保存叶节点和容器,因此请确保将其声明为组合结构类型。
实现结构方法时,记住容器应该将大部分工作交给其子元素来完成。
最后,在容器中定义添加和删除子元素的方法。
记住,这些操作可在组件接口中声明。这将会违反接口隔离原则,因为叶节点类中的这些方法为空。但是,这可以让客户端无差别地访问所有元素,即使是组成树状结构的元素。