对象创建模式¶
概念¶
定义¶
- 通过“对象创建” 模式绕开new,来避免对象创建(new)过程中所导致的紧耦合(依赖具体类),从而支持对象创建的稳定。
要点¶
- 它是接口抽象之后的第一步工作。
- 对象创建的不稳定,容易诱发生命周期后续阶段的混乱。
工厂方法模式(FactoryMethod)¶
动机¶
- 在软件系统中,经常面临着创建对象的工作;由于需求的变化,需要创建的对象的具体类型经常变化。
- 如何应对这种变化?如何绕过常规的对象创建方法(new),提供一种“封装机制”来避免客户程序和这种“具体对象创建工作”的紧耦合?
模式定义¶
- 定义一个用于创建对象的接口,让子类决定实例化哪一个类。
- Factory Method 使得一个类的实例化延迟(目的:解耦,手段:虚函数)到子类。
UML 类图¶
角色¶
- 抽象工厂(Abstract Factory):提供了创建产品的接口,调用者通过它访问具体工厂的工厂方法 newProduct() 来创建产品。
- 具体工厂(ConcreteFactory):主要是实现抽象工厂中的抽象方法,完成具体产品的创建。
- 抽象产品(Product):定义了产品的规范,描述了产品的主要特性和功能。
- 具体产品(ConcreteProduct):实现了抽象产品角色所定义的接口,由具体工厂来创建,它同具体工厂之间一一对应。
代码¶
// 抽象产品:提供了产品的接口
interface Product {
show(): void;
}
// 具体产品1:实现抽象产品中的抽象方法
class ConcreteProduct1 implements Product {
public show(): void {
console.log('具体产品1显示...');
}
}
// 具体产品2:实现抽象产品中的抽象方法
class ConcreteProduct2 implements Product {
public show(): void {
console.log('具体产品2显示...');
}
}
// 抽象工厂:提供了厂品的生成方法
interface AbstractFactory {
newProduct(): Product;
}
// 具体工厂1:实现了厂品的生成方法
class ConcreteFactory1 implements AbstractFactory {
public newProduct(): Product {
console.log('具体工厂1--生产-->具体产品1');
return new ConcreteProduct1();
}
}
// 具体工厂2:实现了厂品的生成方法
class ConcreteFactory2 implements AbstractFactory {
public newProduct(): Product {
console.log('具体工厂2--生产-->具体产品2');
return new ConcreteProduct2();
}
}
export default class AbstractFactoryTest {
public static main(args: string[]): void {
const af: AbstractFactory = new ConcreteFactory2();
const p: Product = af.newProduct();
p.show();
}
}
要点总结¶
- Factory Method 模式用于隔离类对象的使用者和具体类型之间的耦合关系。面对一个经常变化的具体类型,紧耦合的关系(new)会导致软件的脆弱。
- Factory Method 模式通过面向对象的手法,将所要创建的具体对象工作延迟到子类,从而实现一种扩展(而非更改)的策略,较好的解决了这种紧耦合的关系。
- Factory Method模式解决了“单个对象”的需求变化,缺点在于要求创建方法/参数相同。
抽象工厂模式(AbstractFactory)¶
动机¶
- 在软件系统中,经常面临着“一系列相互依赖的对象”的创建工作;同时,由于需求的变化,往往存在更多系列对象的创建工作。
- 如何应对这种变化?如何绕过常规的对象创建方法(new),提供一种“封装机制”来避免客户程序和这种“多系列具体对象创建工作”的紧耦合?
模式定义¶
- 提供一个接口,让该接口负责创建一系列 “相关或者相互依赖的对象”,无需指定它们具体的类
- 何时使用:系统的产品有多于一个的产品族,而系统只消费其中某一族的产品。
问题域¶
- 横轴:产品等级,同一级别产品; 纵轴:产品族,同一个工厂。
- 图示
UML 类图¶
角色¶
- 抽象工厂(Abstract Factory):提供了创建产品的接口,它包含多个创建产品的方法 newProduct(),可以创建多个不同等级的产品。
- 具体工厂(Concrete Factory):主要是实现抽象工厂中的多个抽象方法,完成具体产品的创建。
- 抽象产品(Product):定义了产品的规范,描述了产品的主要特性和功能,抽象工厂模式有多个抽象产品。
- 具体产品(ConcreteProduct):实现了抽象产品角色所定义的接口,由具体工厂来创建,它同具体工厂之间是多对一的关系。
代码¶
// 抽象产品
interface Product1 {
show(): void;
}
interface Product2 {
show(): void;
}
// 具体产品
class ConcreteProduct1 implements Product1 {
public show(): void {
console.log('具体产品 1 展示...');
}
}
class ConcreteProduct2 implements Product2 {
public show(): void {
console.log('具体产品 2 展示...');
}
}
interface AbstractFactory {
newProduct1(): Product1;
newProduct2(): Product2;
}
class ConcreteFactory1 implements AbstractFactory {
public newProduct1(): Product1 {
console.log('具体工厂 1 生成-->具体产品 1...');
return new ConcreteProduct1();
}
public newProduct2(): Product2 {
console.log('具体工厂 1 生成-->具体产品 2...');
return new ConcreteProduct2();
}
}
export default class AbstractFactoryTest {
public static main(args: string[]): void {
const af: AbstractFactory = new ConcreteFactory1();
const p1: Product1 = af.newProduct1();
const p2: Product2 = af.newProduct2();
p1.show();
p2.show();
}
}
要点总结¶
- 如果没有应对“多系列对象构建”的需求变化,则没有必要使用Abstract Factory模式,这时候使用简单的工厂完全可以。
- “系列对象”指的是在某一特定系列下的对象之间有相互依赖、或作用的关系。不同系列的对象之间不能相互依赖。
- Abstract Factory 模式主要在于应对“新系列”的需求变动。其缺点在于难以应对“新对象”的需求变动。
原型模式(Prototype)¶
动机¶
- 在软件系统中,经常面临着“某些结构复杂的对象”的创建工作; 由于需求的变化,这些对象经常面临着剧烈的变化,但是它们却拥有比较稳定一致的接口。
- 如何应对这种变化?如何向“客户程序(使用这些对象的程序)”隔离出“这些易变对象” ,从而使得“依赖这些易变对象的客户程序”不随着需求改变而改变?
模式定义¶
- 使用原型实例指定创建对象的种类,然后通过拷贝原型来创建新的对象。
- 用于创建重复的对象,同时又能保证性能。
UML 类图¶
角色¶
- 抽象原型类:规定了具体原型对象必须实现的接口。
- 具体原型类:实现抽象原型类的 clone() 方法,它是可被复制的对象。
- 访问类:使用具体原型类中的 clone() 方法来复制新的对象。
代码¶
interface Cloneable {
clone(): Cloneable;
}
class Prototype implements Cloneable {
clone(): Cloneable {
// function F() {}
// F.prototype = this;
// return new F();
return Object.create(this);
}
}
// 具体原型类
class Realizetype extends Prototype implements Cloneable {
constructor() {
super();
console.log('具体原型创建成功!');
}
public clone(): Cloneable {
console.log('具体原型复制成功!');
return super.clone();
}
}
export default class PrototypeTest {
public static main(args: string[]): void {
const obj1: Realizetype = new Realizetype();
const obj2: Realizetype = obj1.clone();
console.log('obj1 === obj2 ?', obj1 === obj2);
}
}
要点总结¶
- Prototype 模式同样用于隔离类对象的使用者和具体类型(易变类)之间的耦合关系,它同样要求这些“易变类”拥有“稳定的接口”。
- Prototype 模式对于“如何创建易变类的实体对象”采用“原型克隆”的方法来做,它使得我们可以非常灵活地动态创建“拥有某些稳定接口”的新对象——所需工作仅仅是注册一个新类的对象(即原型),然后在任何需要的地方Clone。
- Prototype模式中的Clone方法可以利用某些框架中的序列化来实现深拷贝。
- 适用于大对象重复创建(初始化)的场景,或者初始化过程复杂、步骤比较多的场景。 使用原型模式后,可以多次拷贝“原型”作为对象的构造过程。这样可以节省系统开销,还可以减少程序的重复代码。
建造者模式(Builder)¶
动机¶
- 在软件系统中,有时候面临着“一个复杂对象”的创建工作,其通常由各个部分的子对象用一定的算法构成; 由于需求的变化,这个复杂对象的各个部分经常面临着剧烈的变化,但是将它们组合在一起的算法却相对稳定。
- 如何应对这种变化?如何提供一种“封装机制”来隔离出“复杂对象的各个部分”的变化,从而保持系统中的“稳定构建算法”不随着需求改变而改变?
模式定义¶
- 将一个复杂对象的构建与其表示相分离,使得同样的构建过程(稳定)可以创建不同的表示(变化)。
-
定义
- 建造者模式(Builder Pattern)使用多个简单的对象一步一步构建成一个复杂的对象。
- 将一个复杂对象的 构建 与 表示 分离,使得同样的构建过程可以创建不同的表示。
- 分离了部件的构造(由 Builder 来负责)和装配(由 Director 负责)。
- 不同的构建器,相同的装配,也可以做出不同的对象;相同的构建器,不同的装配顺序也可以做出不同的对象。也就是实现了构建算法、装配算法的解耦,实现了更好的复用。
UML 类图¶
角色¶
- 抽象建造者类( Builder ):规定实现复杂对象的那些部分的创建,并不涉及具体的对象的创建。
- 具体建造者类 ( ConcreteBuilder ):实现 Builder 接口,完成复杂产品的各个部件的具体创建方法。
- 产品类( Product ):要创建的复杂对象。
- 指挥者类( Director ):调用具体建造者来创建复杂对象的各个部分,不涉及具体产品的信息,只负责保证对象各部分完整创建或按某种顺序创建。
代码¶
// 产品角色 Product:包含多个组成部件的复杂对象。
class Product {
private partA: string;
private partB: string;
private partC: string;
public setPartA(partA: string): void {
this.partA = partA;
}
public setPartB(partB: string): void {
this.partB = partB;
}
public setPartC(partC: string): void {
this.partC = partC;
}
public show(): void {
// 显示产品的特性
console.log(this);
}
}
// 抽象建造者 Builder:包含创建产品各个子部件的抽象方法。
abstract class Builder {
// 创建产品对象
protected product: Product = new Product();
public abstract buildPartA(): void;
public abstract buildPartB(): void;
public abstract buildPartC(): void;
// 返回产品对象
public getResult(): Product {
return this.product;
}
}
// 具体建造者 ConcreteBuilder:实现了抽象建造者接口。
class ConcreteBuilder extends Builder {
public buildPartA(): void {
this.product.setPartA('建造 PartA');
}
public buildPartB(): void {
this.product.setPartB('建造 PartB');
}
public buildPartC(): void {
this.product.setPartC('建造 PartC');
}
}
// 指挥者 Director:调用建造者中的方法完成复杂对象的创建
class Director {
private builder: Builder;
public constructor(builder: Builder) {
this.builder = builder;
}
// 产品构建与组装方法
public construct(): Product {
this.builder.buildPartA();
this.builder.buildPartB();
this.builder.buildPartC();
return this.builder.getResult();
}
}
// 客户类
export default class BuilderPatternClient {
public static main(args: string[]): void {
const builder: Builder = new ConcreteBuilder();
const director: Director = new Director(builder);
const product: Product = director.construct();
product.show();
}
}
模式对比¶
-
工厂方法模式 vs 建造者模式
- 工厂方法模式注重的是整体对象的创建方式;
- 而建造者模式 注重的是部件构建的过程,意在通过一步一步地确构造创建出一个复杂的对象。
-
抽象工厂模式 vs 建造者模式
- 抽象工厂模式实现对产品家族的创建,一个产品家族是这样的一系列产品:具有不同分类维度的产品组合,采用抽象工厂模式则是 不需要关心构建过程,只关心什么产品由什么工厂生产即可。
- 建造者模式则是要求按照指定的蓝图建造产品,它的主要目的是通过组装零配件而产生一个新产品。
要点总结¶
- Builder 模式主要用于“分步骤构建一个复杂的对象”。在这其中“分步骤”是一个稳定的算法,而复杂对象的各个部分则经常变化。
- 变化点在哪里,封装哪里—— Builder模式主要在于应对“复杂对象各个部分”的频繁需求变动。其缺点在于难以应对“分步骤构建算法”的需求变动。