TypeScript实现的24种设计模式

设计模式的六大原则

0.总原则: 开闭原则(Open Close Principle)

开闭原则就是说对扩展开放,对修改关闭。在程序需要进行拓展的时候,不能去修改原有的代码,而是要扩展原有代码,实现一个热插拔的效果。所以一句话概括就是:为了使程序的扩展性好,易于维护和升级。

1.单一职责原则 (Single Responsibility Principle)

不要存在多于一个导致类变更的原因,也就是说每个类应该实现单一的职责,如若不然,就应该把类拆分。

2.里氏替换原则(Liskov Substitution Principle)

里氏代换原则(Liskov Substitution Principle LSP)面向对象设计的基本原则之一。 里氏代换原则中说,任何基类可以出现的地方,子类一定可以出现。 LSP是继承复用的基石,只有当衍生类可以替换掉基类,软件单位的功能不受到影响时,基类才能真正被复用,而衍生类也能够在基类的基础上增加新的行为。里氏代换原则是对“开-闭”原则的补充。实现“开-闭”原则的关键步骤就是抽象化。而基类与子类的继承关系就是抽象化的具体实现,所以里氏代换原则是对实现抽象化的具体步骤的规范。

3.依赖倒转原则(Dependence Inversion Principle)

这个是开闭原则的基础,具体内容:面向接口编程,依赖于抽象而不依赖于具体。写代码时用到具体类时,不与具体类交互,而与具体类的上层接口交互。

4.接口隔离原则(Interface Segregation Principle)

这个原则的意思是:每个接口中不存在子类用不到却必须实现的方法,如果不然,就要将接口拆分。使用多个隔离的接口,比使用单个接口(多个接口方法集合到一个的接口)要好。

5.迪米特法则(也叫做最少知道原则)(Demeter Principle)

就是说:一个类对自己依赖的类知道的越少越好。也就是说无论被依赖的类多么复杂,都应该将逻辑封装在方法的内部,通过public方法提供给外部。这样当被依赖的类变化时,才能最小的影响该类。最少知道原则的另一个表达方式是:只与直接的朋友通信。类之间只要有耦合关系,就叫朋友关系。耦合分为依赖、关联、聚合、组合等。我们称出现为成员变量、方法参数、方法返回值中的类为直接朋友。局部变量、临时变量则不是直接的朋友。我们要求陌生的类不要作为局部变量出现在类中。

6.合成复用原则(Composite Reuse Principle)

原则是尽量首先使用合成/聚合的方式,而不是使用继承。

设计模式的分类

0.总体分为三大类:

  1. 创建型模式(共六种):单例模式、简单工厂模式、工厂方法模式、抽象工厂模式、原型模式、建造者模式。说明:创建型模式就是创建对象的模式,抽象了实例化的过程.它帮助一个系统独立于如何创建,组合和表示它的那些对象.关注的是对象的创建,创建型模式将创建对象的过程进行了抽象,也可以理解为将创建对象的过程进行了封装,作为客户程序仅仅需要去使用对象,而不再关心创建对象过程中的逻辑。
  2. 结构型模式(共七种):适配器模式、桥接模式、装饰器模式、组合模式、外观模式、享元模式、代理模式。说明:结构型模式是为了解决怎样组装现有的类,设计他们的交互方式,从而达到实现一定的功能。
  3. 行为型模式(共十一种):模板方法模式、命令模式、访问者模式、迭代器模式、观察者模式、中介者模式、备忘录模式、解释器模式、状态模式、策略模式、职责链模式。说明:行为型模式是为了对不同的对象之间划分责任和算法的抽象化,行为型模式不仅仅关注类和对象的结构,而且重点关注它们之间的相互作用,通过行为型模式,可以更加清晰地划分类与对象的职责,并研究系统在运行时实例对象之间的交互。
  4. 详情图:
    在这里插入图片描述

1.创建型模式

1.1单例模式
(1)定义:

一个类仅有一个实例,并提供一个访问它的全局访问点(静态方法)

(2)优缺点:
  • 优点:减少内存、系统性能等开销;优化和共享资源访问;
  • 缺点:扩展困难;对测试不利;与单一职责原则冲突;
(3)应用场景:
  • 需要频繁地进行创建和销毁对象、创建对象耗时或耗资源过多但又经常用到。
(4)角色:
  • Singleton:单例。
(5)类图:

在这里插入图片描述

(6)TypeScript代码:
// 饿汉式
class Singleton1 {
    // 1. 构造器私有化,外部不能new
    private constructor(){}
 
    // 2. 本类内部创建对象实例化
    private static instance : Singleton1 = new Singleton1();
 
    // 3. 提供一个公有的静态方法,返回实例对象
    public static getInstance() : Singleton1 {
        return this.instance;
    }
}
 
console.log(Singleton1.getInstance(), '饿汉式(即一开始实例化对象使返回的引用相同)');
 
// 懒汉式
class Singleton2 {
	// 1. 构造器私有化,外部不能new
    private constructor(){}
	// 2. 本类内部创建对象置为空
    private static instance: Singleton2 = null;
	// 3. 提供一个公有的静态方法,返回实例对象
    public static getInstance() : Singleton2 {
		// 4. 内部创建对象为空则才开始实例对象
        if (this.instance === null) {
            this.instance = new Singleton2();
        }
		// 5. 内部对象已实例则直接返回
        return this.instance;
    }
}
 
console.log(Singleton2.getInstance(), '懒汉式(即一开始并未实例化对象后经判断实例化对象使返回的引用相同)')
1.2简单工厂模式
(1)定义:

定义一个创建对象的类,由这个类来封装实例化对象的行为

(2)优缺点:
  • 优点:客户类与具体子类解耦:客户类不需要知道所有子类细节;
  • 缺点:工厂类职责过重;增加工厂类增加了系统复杂度;系统扩展困难(会修改工厂逻辑);
(3)应用场景:
  • 工厂类负责创建的对象较少、客户端对如何创建对象不关心
(4)角色:
  • SimpleFactory:简单工厂类。
  • Product:抽象产品类。
  • ConcreteProduct:具体产品类。
(5)类图:

在这里插入图片描述

(6)TypeScript代码:
// 抽象产品接口
interface Product{}
 
// 具体产品一
class ConcreteProduct1 implements Product {
    constructor(){}
}
 
// 具体产品二
class ConcreteProduct2 implements Product {
    constructor(){}
}
 
// 简单工厂
class SimpleFactory {
	// 按指定来实例化对应对象
    public static createProduct(type : number) : Product {
        let product = null;
        if (type === 1) {
            product = new ConcreteProduct1();
        } else if ( type === 2) {
            product = new ConcreteProduct2();
        }
        return product;
    }
}
 
// 使用
let product = SimpleFactory.createProduct(1);
console.log(product);
1.3工厂方法模式
(1)定义:

定义了一个创建对象的抽象方法,由子类决定要实例化的类。

(2)优缺点:
  • 优点:用户只需要关心产品对应的工厂;添加新产品是只要添加一个具体工厂和具体产品(符合开闭原则);
  • 缺点:类个数成倍增加(增加一个产品会增加具体类和实现工厂);
(3)应用场景:
  • 所有生成对象的地方都可以使用(权衡代价)、需要灵活且可扩展的框架…
(4)角色:
  • Creator:抽象工厂类。
  • ConcreteCreator:具体工厂类。
  • Product:抽象产品类。
  • ConcreteProduct:具体产品类。
(5)类图:

在这里插入图片描述

(6)TypeScript代码:
// 抽象产品接口
interface Product {
    method1() : void;
    method2() : void;
}
 
// 具体产品一
class ConcreteProduct_1 implements Product {
    constructor(){}
    method1() {
 
    }
    method2() {
 
    }
}
 
// 具体产品二
class ConcreteProduct_2 implements Product {
    constructor(){}
    method1() {
 
    }
    method2() {
        
    }
}
 
// 抽象工厂
abstract class Creator {
    public abstract createProduct(type : number) : Product;
}
 
// 具体工厂
class ConcreteCreator extends Creator {
    constructor(){
        super();
    }
 
    public createProduct(type : number) : Product {
        let product = null;
        if (type === 1) {
            product = new ConcreteProduct_1();
        } else if (type === 2) {
            product = new ConcreteProduct_2();
        }
        return product;
    }
}
 
// 使用
const creator : Creator = new ConcreteCreator();
const myProduct : Product = creator.createProduct(1);
1.4抽象工厂模式
(1)定义:

为创建一组相关或相互依赖的对象提供一个接口,而且无须指定它们的具体类

(2)优缺点:
  • 优点:增加新的产品族很容易
  • 缺点:增加新的产品结构很麻烦(违背开闭原则)
(3)应用场景:
  • 无需关心对象创建的过程、系统有多于一个的产品族且每次只需要某一个产品族、产品等级结构稳定。
(4)角色:
  • AbstractProduct:抽象产品类
  • ConcreteProduct:具体产品类
  • AbstractFactory:抽象工厂类
  • ConcreteFactory:具体工厂类
(5)类图:

在这里插入图片描述

(6)TypeScript代码:
// 抽象工厂接口
interface AbstractFactory {
    createProductA() : AbstractProductA;
    createProductB() : AbstractProductB;
}

// 抽象产品A接口
interface AbstractProductA {}
 
// 抽象产品B接口
interface AbstractProductB {}
 
// 具体工厂1
class ConcreteFactory1 implements AbstractFactory {
    constructor() {}
    public createProductA() : AbstractProductA {
        return new ConcreteProductA1();
    }
    public createProductB() : AbstractProductB {
        return new ConcreteProductB1();
    }
}
 
// 具体工厂2
class ConcreteFactory2 implements AbstractFactory {
    constructor() {}
    public createProductA() : AbstractProductA {
        return new ConcreteProductA2();
    }
    public createProductB() : AbstractProductB {
        return new ConcreteProductB2();
    }
}
 
// 具体产品A1
class ConcreteProductA1 implements AbstractProductA {}
// 具体产品A2
class ConcreteProductA2 implements AbstractProductA {}
// 具体产品B1
class ConcreteProductB1 implements AbstractProductB {}
// 具体产品B2
class ConcreteProductB2 implements AbstractProductA {}
 
// 使用
const factory1 : AbstractFactory = new ConcreteFactory1();
const factory2 : AbstractFactory = new ConcreteFactory2();
const productA1 : AbstractProductA = factory1.createProductA();
const productA2 : AbstractProductA = factory2.createProductA();
const productB1 : AbstractProductB = factory1.createProductB();
const productB2 : AbstractProductB = factory2.createProductB();
1.5 原型模式
(1)定义:

用原型实例指定创建对象的种类,并且通过拷贝这些原型,创建新的对象

(2)优缺点:
  • 优点:简化创建新对象的过程并提供效率;可动态获取对象运行时的状态;原始对象变化(增加或减少属性)相应克隆对象也会变化
  • 缺点:对已有类修改时,需要修改源码,违背开闭原则
(3)应用场景:
  • 创建成本比较大的场景;需要动态获取当前的对象运行状态的场景。
(4)角色:
  • Prototype:抽象原型对象。
  • ConcretePrototype:具体原型角色(被复制的对象)。
(5)类图:

在这里插入图片描述

(6)TypeScript代码:
// 定义一个原型接口
interface Prototype {
    clone():Prototype;
}
// 实现原型接口
class Dog implements Prototype {
    public name: string;
    public birthYear: number;
    public sex: string;
    public presentYear: number;
    constructor() {
        this.name = "lili";
        this.birthYear = 2015;
        this.sex = "男";
        this.presentYear = 2018;
    }
 
    public getDiscription(): string {
        return `狗狗叫${this.name},性别${this.sex},${this.presentYear}${this.presentYear - this.birthYear}岁了`
    }
 
    // 实现复制
    public clone(): Prototype {
        return Object.create(this);
    }
}
 
// 使用
const dog = new Dog();
console.log(dog.getDiscription());
dog.presentYear = 2020;
const dog1 = Object.create(dog);
console.log(dog1.getDiscription());
 
1.6 建造者模式
(1)定义:

将一个复杂对象的构建与它的表示分离、使得同样的构建过程可以创建不同的表示

(2)优缺点:
  • 优点:将产品本身与产品构建过程解耦、使得相同创建过程可以创建不同产品;具体建造者相互独立、扩展容易;可以精确控制产品创建过程
  • 缺点:使用范围受限(要求所创建产品一般具有较多共同点);产品内部变化复杂会导致具体建造者过多
(3)应用场景:
  • 要求按照指定的蓝图建造产品,通过组装零配件产生一个新产品(产品对象具备共性)
(4)角色:
  • Product:产品角色,一个具体的产品对。
  • Builder:抽象建造者。
  • ConcreteBuilder:具体建造者,构建和装配各个部件。
  • Director:指挥者,创建一个复杂的对象。
(5)类图 :

在这里插入图片描述

(6)TypeScript代码:
// 产品角色
class Product {
    public doSomething() : void {
        // 独立业务
    }
}

// 抽象建造者
abstract class Builder {
    public abstract buildPartA() : void;
    public abstract buildPartB() : void;
    public abstract buildPartC() : void;
    public abstract buildProduct() : Product;
}
 
// 具体建造者
class ConcreteBuilder extends Builder {
    private product : Product;
    constructor(product : Product) {
        super();
        this.product = product;
    }
 
    public buildPartA() : void {}
    public buildPartB() : void {}
    public buildPartC() : void {}
 
    // 最终组建一个产品
    public buildProduct() : Product {
        return this.product;
    }
}
 
// 指挥者
class Director {
    private _builder : Builder;
	
	// 初始建造者角色
    constructor(builder : Builder) {
        this._builder = builder;
    }
	
	// 重设建造者角色
    public setBuilder(builder : Builder) {
        this._builder = builder;
    }
	
    // 将处理建造的流程交给指挥者
    public constructorProduct() {
        this._builder.buildPartA();
        this._builder.buildPartB();
        this._builder.buildPartC();
        return this._builder.buildProduct();
    }
}
 
// 使用
const builder : Builder = new ConcreteBuilder(new Product());
const director : Director = new Director(builder);
const product : Product = director.constructorProduct();

2.结构型模式

2.1适配器模式
(1)定义:

适配器模式(也称变压器模式、包装模式)是将一个类的接口变换为客户端所期待的另一种接口,从而使原本因接口不匹配而无法在一起工作的两个类能够在一起工作。

(2)优缺点:
  • 优点:让两个没关系的类可以一起运行;提高类的复用性(源角色在原有系统还可以使用);
  • 缺点:【1】类适配器:不支持多继承语言,一次只能适配一个被适配者类,而且目标抽象类只能为接口,有一定局限性;被适配者类的方法在Adapter中都会暴露出来;【2】对象适配器:与类适配器模式相比,要想置换被适配类的方法就不容易;
(3)应用场景:
  • 类适配器和对象适配器:接口不符合规范,通过适配后变成符合规范的接口进行使用。
  • 接口适配器:适用于一个接口不想使用其所有的方法的情况。
(4)角色:
  • Target:目标角色,定义把其他类转换为何种接口。
  • Adaptee:被适配者,就是源角色。
  • Adapter:适配器,负责将Adaptee的接口转换为Target接口。
(5)类图:
  • 类适配器:
    在这里插入图片描述

  • 对象适配器:
    在这里插入图片描述

  • 接口适配器:
    在这里插入图片描述

(6)TypeScript代码:
// 1.类适配器模式
// 目标对象
interface Target1 {
    request() : void;
}

// 被适配者
class Adaptee1 {
    constructor() {}
    // 这是源角色,有自己的的业务逻辑
    public specificRequest() : void {}
}

// 适配器
class Adapter1 extends Adaptee1 implements Target1 {
    constructor() {
        super();
    }
    public request() : void {
        super.specificRequest();
    }
}

// 使用
const target1 : Target1 = new Adapter1();
target1.request();

// 2.对象适配器模式
// 目标对象
interface Target2 {
    request() : void;
}

// 被适配者
class Adaptee2 {
    constructor() {}
    // 这是源角色,有自己的的业务逻辑
    public specificRequest() : void {}
}

// 适配器
class Adapter2 implements Target2 {
    private adaptee : Adaptee2;
    constructor(adaptee : Adaptee2) {
        this.adaptee = adaptee;
    }
    public request() : void {
        this.adaptee.specificRequest();
    }
}

// 使用
const target2 : Target2 = new Adapter2(new Adaptee2());
target2.request();

// 3.抽象适配器模式
// 接口适配器
interface Adaptee3 {
    operation1() : void;
    operation2() : void;
}

// 抽象的适配器
abstract class AbsAdapter3 implements Adaptee3 {
    public operation1() : void {}
    public operation2() : void {}
}
 
// 适配类
class UseClass extends AbsAdapter3 {
    public operation1() : void {}// 重写该类
}

// 使用
const target3 = new UseClass();
target3.operation1();
2.2 桥接模式
(1)定义:

将实现与抽象放在两个不同的层次中,使得两者可以独立地变化。(最主要的将实现和抽象两个层次划分开来)。

(2)优缺点:
  • 优点:实现了抽象和实现部分的分离,提高了系统的灵活性;替代了多层继承方案,减少了子类的个数;
  • 缺点:增加了系统的理解和设计模式;要求正确识别出系统中两个独立变化的纬度,使用范围有一定局限性;
(3)应用场景:
  • 不希望使用继承或因为多层继承导致类爆炸的系统;JDBC驱动程序、银行转账系统(转账分类和转账用户类型)、消息管理(消息类型、消息分类)。
(4)角色:
  • Abstraction:抽象类,充当桥接类,主要职责是定义出该角色的行为,同时保存一个对实现化角色的引用。
  • RefinedAbstraction:是Abstraction抽象类的子类。
  • Implementor:行为实现化的接口。
  • ConcreteImplementor:行为的具体实现类。
(5)类图:

在这里插入图片描述

(6)TypeScript代码:
// 实现接口角色
interface Implementor {
    doSomething() : void;
    doAnything() : void;
}

// 具体实现角色
class ConcreteImplementor1 implements Implementor {
    public doSomething() : void {
 
    }
    public doAnything() : void {
 
    }
}

// 具体实现角色
class ConcreteImplementor2 implements Implementor {
    public doSomething() : void {
 
    }
    public doAnything() : void {
        
    }
}
 
// 抽象类
abstract class Abstraction {
    private imp : Implementor;
    constructor(imp : Implementor) {
        this.imp = imp;
    }
 
    // 自身的行为和属性
    public request() : void {
        this.imp.doSomething();
    }
}

// 具体抽象化角色
class RefinedAbstraction extends Abstraction {
    constructor(imp : Implementor) {
        super(imp);
    }
 
    public request() : void {
        // 自己写一些处理业务
        super.request();
    }
}
 
// 调用
// 定义一个实现化角色
const imp : Implementor = new ConcreteImplementor1();

// 定义一个抽象化角色
const abs : Abstraction = new RefinedAbstraction(imp);

// 执行上下文
abs.request();
 
2.3 装饰者模式
(1)定义:

动态地将新功能附加到对象上。使对象功能扩展方便、它比继承更有弹性、装饰者模式也体现了开闭原则(OCP)

(2)优缺点:
  • 优点:【1】装饰类和被装饰类可以独立发展,而不会相互耦合;【2】装饰模式是继承关系的一个替代方案,但是装饰者模式比继承更灵活;【3】装饰模式可以动态地扩展一个实现类的功能;
  • 缺点:多层的装饰是比较复杂的。由于装饰者模式会导致设计中出现许多小对象,过度使用会让程序变得尤为复杂;
(3)应用场景:
  • 需要扩展一个类的功能或给一个类增加附加功能。
  • 需要动态地给一个对象增加功能并且这些功能可以再动态地撤销。
  • 需要增加由一些基本功能的排列组合而产生的非常大量的功能。
(4)角色:
  • Component:一个接口或者抽象类,定义最核心的对象。
  • ConcreteComponent:最核心、最原始、最基本的接口或抽象类的实现,要装饰的对象。
  • Decorator:持有一个构件对象的实例并定义一个与抽象构件接口一致的接口。
  • ConcreteDecoratorA和ConcreteDecoratorB:两个具体的装饰类。
(5)类图:

在这里插入图片描述

(6)TypeScript代码:
// 抽象构件
abstract class Component {
    public abstract operate() : void;
}
 
// 具体构件
class ConcreteComponent extends Component {
    public operate() : void {
        console.log('do something');
    }
}
 
// 装饰角色
abstract class Decorator extends Component {
    private component : Component = null;
    constructor(component : Component ) {
        super();
        this.component = component;
    }
    public operate() : void {
        this.component.operate();
    }
}

// 具体装饰者
class ConcreteDecoratorA extends Decorator {
    constructor(component : Component) {
        super(component);
    }
 
    // 定义自己的修饰方法
    private methodA() : void {
        console.log('methodA修饰');
    }
 
    // 重写父类方法
    public operate() : void {
        this.methodA();
        super.operate();
    }
}

// 具体装饰者
class ConcreteDecoratorB extends Decorator {
    constructor(component : Component) {
        super(component);
    }
 
    // 定义自己的修饰方法
    private methodB() : void {
        console.log('methodB修饰');
    }
 
    // 重写父类方法
    public operate() : void {
        this.methodB();
        super.operate();
    }
}

function main() {
	// 实现构件
    let component : Component = new ConcreteComponent();
    // 第一次装饰
    component = new ConcreteDecoratorA(component);
    // 第二次装饰
    component = new ConcreteDecoratorB(component);
    // 装饰后运行
    component.operate();
}
 
main();
2.4组合模式
(1)定义:

组合模式也叫合成模式或部分-整体模式,主要用来描述部分和整体的关系。将对象组合成树形结构以表示"部分-整体"的层次结构,使得用户对单个对象和组合对象的使用具有一致性。

(2)优缺点:
  • 优点:【1】调用简单:只需要面对一致的对象而不用考虑整体部分或者叶子节点的问题。【2】扩展性强:一方面当更改组合对象时,只需要调整内部的层次关系,用户不用做出任何改动;另一方面容易增加节点,只要找到它的父节点即可;
  • 缺点:要求较高的抽象性,如果节点和叶子节点有很多差异性的话(比如很多方法和属性不一样)则不适合使用组合模式;
(3)应用场景:
  • 需要遍历组织结构或者处理的对象具有树形结构时,非常适合使用组合模式。
  • 维护和展示部分-整体关系的场景,如树形菜单,文件和文件夹管理。
  • 组合模式是应树形结构而生,所以组合模式的使用场景就是出现树形结构的地方。
(4)角色:
  • Component:定义参加组合对象的共有方法和属性,可以定义一些默认的行为或属性。
  • Leaf:在组合中表示叶子节点。
  • Composite:非叶子节点,主要作用是存储和管理子部件,通常包含Add()、Remove()、GetChild()等方法。
(5)类图:

在这里插入图片描述

(6)TypeScript代码:
// 抽象构件
abstract class Component {
    protected name : string;
    constructor(name : string) {
        this.name = name;
    }
 
    public abstract doOperation() : void;
 
    public add(component : Component) : void {
 
    }
 
    public remove(component : Component) : void {
 
    }
 
    public getChildren() : Array<Component> {
        return [];
    }
}

// 构件容器(可封装组件)
class Composite extends Component {
    private componentList : any;
    constructor(name : string) {
        super(name);
        this.componentList = [];
    }
 
    public doOperation() : void {
        console.log(`这是容器${this.name},处理一些逻辑业务!`);
    }
 
    public add(component : Component) : void {
        this.componentList.push(component);
    }
 
    public remove(component : Component) : void {
        const componentIndex = this.componentList.findIndex((value : Component, index : Number) => {
            return value == component;
        });
        this.componentList.splice(componentIndex, 1);
    }
 
    public getChildren() : Array<Component> {
        return this.componentList;
    }
}

// 构件组件(不可封装组件但可执行操作)
class Leaf extends Component {
    constructor(name : string) {
        super(name);
    }
 
    public doOperation() : void {
        console.log(`这是叶子节点${this.name},处理一些逻辑业务!`);
    }
}

function main() {
	// 实现构件容器
    const root : Component  = new Composite('root');
	// 实现构件组件
    const node1 : Component = new Leaf('1');
	// 实现子构件容器
    const node2 : Component = new Composite('2');
	// 实现构件组件
    const node3 : Component = new Leaf('3');
	
    root.add(node1);
    root.add(node2);
    root.add(node3);
 
    const node2_1 : Component = new Leaf("2_1");
    node2.add(node2_1);
 
    const children1 = root.getChildren();
    console.log(children1);
 
    root.remove(node2);
 
    const children2 = root.getChildren();
    console.log(children2);
}
 
main();
2.5外观模式
(1)定义:

要求一个子系统的外部与其内部的通信必须通过一个统一的对象进行。外观模式提供一个高层次的接口,使得子系统更易于使用。(简而言之就是提供一个统一的接口用来访问子系统中的一群接口从而让子系统更容易使用)

(2)优缺点:
  • 优点:【1】外观模式最大的优点就是使得复杂的子系统的接口变得简单可用,减少了客户端对子系统的依赖,达到了解耦的效果。【2】让子系统内部的模块更易维护和扩展。【3】遵循迪米特法则,对内封装具体细节,对外只暴露必要的接口。
  • 缺点:不符合开闭原则,如果要修改某一个子系统的功能,通常外观类也要一起修改。
(3)应用场景:
  • 为一个复杂的模块或子系统提供一个供外界访问的接口。
  • 子系统相对独立(外界对子系统的访问只要黑箱操作即可)。
  • 维护一个大型遗留系统的时候,可能这个系统已经非常难以维护和扩展,此时可以考虑为新系统开发一个Facede类,来提供遗留系统的比较清晰简单的接口让新系统与Facede类交互,提高复用性。
  • 当系统需要进行分层设计时,可以考虑使用Facede模式。
(4)角色:
  • Facade:提供一个外观接口,对外它提供一个易于客户端访问的接口,对内它可以访问子系统中所有功能。
  • SubSystem:实现系统的部分功能,客户可以通过外观角色访问它。
(5)类图:

在这里插入图片描述

(6)TypeScript代码:
// A类
class SubSystemA {
    public doOperationA() : void {
        console.log('子系统A的举动');
    }
}

// B类
class SubSystemB {
    public doOperationB() : void {
        console.log('子系统B的举动');
    }
}

// 外观者类
class Facade {
    private subSystemA : SubSystemA;
    private subSystemB : SubSystemB;
    constructor() {
        this.subSystemA = new SubSystemA();
        this.subSystemB = new SubSystemB();
    }
 
    public doOperation() : void {
        this.subSystemA.doOperationA();
        this.subSystemB.doOperationB();
    }
}
 
function main() {
    const facade : Facade = new Facade();
    facade.doOperation();
}
 
main();
2.6享元模式
(1)定义:

享元模式也叫做蝇量模式,使用共享对象可有效地支持大量细粒度的对象,是池技术的重要实现方式。

(2)扩展:
  • 享元模式提出了两个要求:细粒度和共享对象。这里就涉及到内部状态和外部状态了,即将对象的信息分为两个部分:内部状态和外部状态:
    • 内部状态:指对象共享出来的信息,存储在享元对象内部且不会随环境的改变而改变。
    • 外部状态:指对象得以依赖的一个标记,是随环境改变而改变的、不可共享的状态。
(3)优缺点:
  • 优点:享元模式大大减少了对象的创建,降低了程序内存的占用,提高效率。
  • 缺点:提高了系统复杂度,需要分离出外部状态和内部装填,而且外部状态具有固化特性,不应该随内部状态改变而改变,否则会导致系统的逻辑混乱。
(4)应用场景:
  • 最典型的应用场景是需要缓冲池的场景,比如String常量池、数据库连接池。
  • 系统中存在大量的相似对象。
  • 细粒度的对象都具备较近的外部状态,而且内部状态与环境无关,也就是说对象没有特定身份。
(5)角色:
  • FlyWeight:一个产品的抽象类,同时定义出对象的内部状态和外部状态的接口或实现。
  • ConcreteFlyweight:具体的一个产品类,实现抽象角色定义的业务。
  • unsharedConcreteFlyweight:该对象一般不会出现在享元工厂中。
  • FlyweightFactory:用于构建一个池容器(集合),同时提供从池中获取对象方法。
(6)类图:

在这里插入图片描述

(7)TypeScript代码:
// 抽象享元
abstract class Flyweight {
    public abstract doOperation(extrinsicState : string) : void;
}

// 具体享元
class ConcreteFlyweight extends Flyweight {
    private intrinsicState : string;
    constructor(intrinsicState : string) {
        super();
        this.intrinsicState = intrinsicState;
    }
 
    public doOperation(extrinsicState : string) : void {
        console.log(`这是具体享元角色,内部状态为${this.intrinsicState},外部状态为${extrinsicState}`);
    }
}

// 享元接口对象
interface flyweightObject {
    [key : string] : Flyweight // key为字符串类型的享元数组
}

// 享元工厂
class FlyweightFactory {
    private flyweights : flyweightObject;
    constructor() {
        this.flyweights = {};
    }
	// 获取享元
    public getFlyweight(intrinsicState : string) : Flyweight {
        if (!this.flyweights[intrinsicState]) {
            const flyweight : Flyweight = new ConcreteFlyweight(intrinsicState);
            this.flyweights[intrinsicState] = flyweight;
        }
        return this.flyweights[intrinsicState];
    }
}
 
function main() {
	// 实现享元工厂
    const factory : FlyweightFactory = new FlyweightFactory();
	// 获取享元
    const flyweight1 : Flyweight = factory.getFlyweight("aa");
    const flyweight2 : Flyweight = factory.getFlyweight("aa");
    flyweight1.doOperation('x');
    flyweight2.doOperation('y');
}
 
main();
2.7代理模式
(1)定义:

为其他对象提供一种代理以控制对这个对象的访问。

(2)实现分类:
  • 静态代理:
    • 定义:在使用时需要定义接口或者父类,被代理对象(即目标对象)与代理对象一起实现相同的接口或者是继承相同的父类。
    • 优缺点:
      • 优点: 在不修改目标对象的功能前提下,能通过代理对象对目标功能扩展;
      • 缺点:【1】因为代理对象需要与目标对象实现一样的接口所以会有很多代理类;【2】一旦接口增加方法,目标对象与代理对象都要维护;【3】增加代理类之后明显会增加处理时间,拖慢处理速度;
    • 角色:
      • Subject:是抽象类也可以是接口。
      • RealSubject:是业务逻辑的具体执行者。
      • Proxy:负责对真实角色的应用,把所有抽象主题类定义的方法限制委托给真实主题角色实现,并且在真实主题角色处理完毕前后做预处理和善后处理工作。
    • 类图:
      在这里插入图片描述
  • 动态代理:
    • 定义 :代理对象不需要实现接口,但是目标对象要实现接口否则不能用动态代理。其中代理对象的生成是利用JDK的API,动态地在内存中构建代理对象。
    • 优缺点:
      • 优点:代理对象不需要实现接口;
      • 缺点:增加代理类之后明显会增加处理时间,拖慢处理速度;
    • 角色:ProxyFactory:代理工厂角色
    • 类图:
      在这里插入图片描述
    • Cglib代理(子代理,暂未实现):指在内存中创建一个子类对象从而实现对目标对象功能的扩展;
(3)应用场景
  • 远程代理:控制对远程对象(不同地址空间)的访问,它负责将请求及其参数进行编码并向不同地址空间中的对象发送已经编码的请求。
  • 虚拟代理:根据需要创建开销很大的对象,它可以缓存实体的附加信息,以便延迟对它的访问,例如在网站加载一个很大的图片时,不能马上完成则可以使用虚拟代理缓存图片的大小信息,然后生成一张临时图片来替代原始图片。
  • 保护代理:按权限控制对象的访问,它负责检查调用者是否具有实现一个请求所必须的访问权限。
  • 智能代理:取代了简单的指针,它在访问对象时执行一些附加操作即记录对象的引用次数;当第一次引用一个对象时,将它装入内存;在访问一个实际对象前,检查是否已经锁定了它,以确保其它对象不能改变它。
(4)TypeScript代码:
// 静态代理
// 业务接口
interface Subject1 {
    doOperation() : void;
}

// 业务类
class RealSubject1 implements Subject1 {
    public doOperation() {
        console.log('我是RealSubject1类,正在执行');
    }
}

// 代理类
class MyProxy1 implements Subject1 {
    private target : Subject1;
    constructor(realSubject1 : Subject1) {
        this.target = realSubject1;
    }
 
    public doOperation() {
        console.log('我是代理类');
        this.target.doOperation();
    }
}

function main1() {
    const realSubject1 : Subject1 = new RealSubject1();
    const myProxy1 : Subject1 = new MyProxy1(realSubject1);
 
    myProxy1.doOperation();
}

main1();

// 由于在此处typescript默认使用es5而Proxy是es6的新特性故而需要在此处自定义
//定义proxy中必须实现的handler参数接口
/**
interface ProxeeHandler<T extends object, TOut extends object> {
    get?<K extends keyof TOut>(target: T, p: K, receiver: TOut): TOut[K];
    set?<K extends keyof TOut>(target: T, p: K, value: TOut[K], receiver: TOut): boolean;
}
//定义proxy的构造函数接口
interface ProxeeConstructor {
    new <T extends object, TOut extends object>(target: T, handler: ProxeeHandler<T, TOut>): TOut;
}
//用以上两个接口声明一个Proxee,其用法和Proxy一模一样
declare var Proxy: ProxeeConstructor
**/
// 动态代理
// 业务接口
interface Subject2 {
    doOperation() : void;
}
 
// 业务类
class RealSubject2 implements Subject2 {
    constructor() {}
 
    public doOperation() : void {
        console.log('我是RealSubject类,正在执行');
    }
}
 
// 代理工厂类
class ProxyFactory {
    private target : any;
    constructor(target : any) {
        this.target = target;
    }
    public getProxyInstance() : any {
        return new Proxy(this.target, {
            get: (target, propKey) => {
                // 做的一些拦截处理
                return target[propKey];
            }
        });
    }
}
 
function main2() {
    const target : Subject2 = new RealSubject2();
    const proxyInstance : Subject2 = <Subject2>new ProxyFactory(target).getProxyInstance();
    proxyInstance.doOperation();
}
 
main2();

3.行为型模式

3.1模板方法模式
(1)定义:

定义一个操作中的算法框架,将一些步骤延迟到子类中,使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤

(2)优缺点:
  • 优点:【1】既统一了算法也提供了很大的灵活性。父类的模板方法确保了算法的结构不变,同时由子类提供部分步骤的实现;【2】实现了最大化代码复用。父类的模板方法和已实现的某些步骤会被子类继承而直接使用;
  • 缺点:每一个不同的实现都需要一个子类实现,导致类的个数增加,使得系统更加庞大。
(3)应用场景:
  • 当要完成某个过程,该过程要执行一系列步骤,而这一系列步骤的基本逻辑相同但个别步骤在实现时可能不同的情况下通常考虑使用模板方法模式来处理。
  • 重构时。把相同的代码抽取到父类中,然后通过钩子函数约束其行为。
(4)角色:
  • AbstractClass:它的方法分为两类:基本方法和模板方法。基本方法是由子类实现的方法,并且在模板方法中调用;模板方法定义算法的骨架。
  • ConcreteClass:实现父类抽象方法或按需重写方法。
(5)类图:

在这里插入图片描述

(6)TypeScript代码:
// 抽象类
abstract class AbstractClass {
    constructor() {}
 
    // 模板方法
    public template() : void {
        this.operation1();
        this.hookMethod() && this.operation2();
        this.operation3();
    }
 
    // 基本方法
    protected operation1() : void {
        console.log('使用了方法operation1');
    }
 
    protected operation2() : void {
        console.log('使用了方法operation2');
    }
 
    protected operation3() : void {
        console.log('使用了方法operation3');
    }
 
    // 钩子方法
    protected hookMethod() : boolean {
        return true;
    }
}
 
// 具体A类
class ConcreteClassA extends AbstractClass {
    protected operation2() :void {
        console.log('对该方法operation2进行了修改再使用');
    }
 
    protected operation3() :void {
        console.log('对该方法operation3进行了修改再使用');
    }
}
 
// 具体B类
class ConcreteClassB extends AbstractClass {
    // 覆盖钩子方法
    protected hookMethod() : boolean {
        return false;
    }
}
 
function main() {
    const class1 : AbstractClass = new ConcreteClassA();
    const class2 : AbstractClass = new ConcreteClassB();
 
    class1.template();
    class2.template();
}
 
main();
3.2命令模式
(1)定义:

将一个请求封装成一个对象,从而让你使用不同的请求把客户端参数化,对请求排队或者记录请求日志(使用不同参数来表示不同的请求)可以提供命令的撤销和恢复功能。

(2)优缺点:
  • 优点:【1】命令模式使得请求发送者与请求接收者消除彼此之间的耦合;【2】可扩展性。Command子类可以非常容易地扩展。
  • 缺点:可能导致某些系统有过多的具体命令类从而增加了系统的复杂度。
(3)应用场景:
  • 命令模式适合在需要将请求调用者和请求接收者进行解耦的场景,经典的应用场景是界面的每一个按钮都是一条命令,可以采用命令模式;如:模拟CMD(DOS命令);订单的撤销/恢复;触发-反馈机制。
(4)角色:
  • Invoker:通过它来调用命令
  • Receiver:命令的真正执行者
  • Command:需要执行的所有命令都在这里声明
  • ConcreteCommand:将一个接收者对象与一个动作绑定,调用接收者相应的操作,实现execute
(5)类图:

在这里插入图片描述

(6)TypeScript代码:
// 命令接口
interface Command {
    execute() : void;
    undo() : void;
}

// 开启命令
class ConcreteCommandOn implements Command {
    private receiver : Receiver;
    constructor(receiver : Receiver) {
        this.receiver = receiver;
    }
 
    // 执行命令的方法
    public execute() : void {
        this.receiver.actionOn();
    }
 
    // 撤销命令的方法
    public undo() : void {
        this.receiver.actionOff();
    }
}
 
// 关闭命令
class ConcreteCommandOff implements Command {
    private receiver : Receiver;
    constructor(receiver : Receiver) {
        this.receiver = receiver;
    }
 
    // 执行命令的方法
    public execute() : void {
        this.receiver.actionOff();
    }
 
    // 撤销命令的方法
    public undo() : void {
        this.receiver.actionOn();
    }
}
 
// 空命令(省去判空操作)
class NoCommand implements Command {
    public execute() : void {}
    public undo() : void {}
}
 
class Receiver {
    public actionOn() : void {
        console.log('我是命令接收者,开启了某动作');
    }
    
    public actionOff() : void {
        console.log('我是命令接收者,关闭了某动作');
    }
}
 
class Invoker {
    private onCommands : Array<Command>;
    private offCommands : Array<Command>;
    private undoCommand : Command;
    private slotNum : number = 7;
    constructor() {
        this.undoCommand = new NoCommand();
        this.onCommands = [];
        this.offCommands = [];
 
        for (let i = 0; i < this.slotNum; i++) {
            this.onCommands[i] = new NoCommand();
            this.offCommands[i] =  new NoCommand();
        }
    }
 
    public setCommand(index : number, onCommand : Command, offCommand : Command) : void {
        this.onCommands[index] = onCommand;
        this.offCommands[index] = offCommand;
    }
 
    // 开启
    public on (index : number) : void {
        this.onCommands[index].execute();// 调用相应方法
        //记录这次操作,用于撤销
        this.undoCommand = this.onCommands[index];
    }
 
    // 关闭
    public off (index : number) : void {
        this.offCommands[index].execute();
        this.undoCommand = this.offCommands[index];
    }
 
    // 撤销
    public undo () : void {
        this.undoCommand.undo();
    }
}
 
function main() {
    // 创建接收者
    const receiver : Receiver = new Receiver();
 
    // 创建命令
    const commandOn : Command = new ConcreteCommandOn(receiver);
    const commandOff : Command = new ConcreteCommandOff(receiver);
 
    // 创建调用者
    const invoker : Invoker = new Invoker();
    invoker.setCommand(0, commandOn, commandOff);

    invoker.on(0);
    invoker.off(0);
    invoker.undo();
}
 
main();
3.3访问者模式
(1)定义:

封装一些作用于某种数据结构中的各元素操作,它可以在不改变数据结构的前提下定义作用于这些元素的新操作。(主要将数据结构与数据操作进行分离,解决数据结构和操作耦合性问题)

(2)优缺点:
  • 优点:【1】访问者模式符合单一职责原则、让程序具有优秀的扩展性、灵活性非常高;【2】访问者模式可以对功能进行统一,可以做报表、UI、拦截器与过滤器,适用于数据结构相对稳定的系统;
  • 缺点:【1】具体元素对访问者公布细节,也就是说访问者关注了其他类的内部细节,这是迪米特法则所不建议的,这样造成了具体元素变更比较困难;【2】违背了依赖倒转原则。访问者依赖的是具体元素,而不是抽象元素;【3】因此,如果一个系统有比较稳定的数据结构,又有经常变化的功能需求,那么访问者模式就是比较合适的;
(3)应用场景:
  • 访问者模式主要应用的场景是:需要对一个对象结构中的对象进行很多不同操作(这些操作彼此没有关联)同时需要避免让这些操作"污染"这些对象的类,可以选用访问者模式解决。
(4)角色:
  • Visitor:抽象类或者接口,声明访问者可以访问哪些元素。为该对象结构中的ConcreteElement的每一个类声明一个visit操作。
  • ConcreteVistor:实现每个有Visitor声明的操作,是每个操作的实现部分。
  • Element:接口或者抽象类,定义一个accept方法,接收一个访问者对象。
  • ConcreteElement:实现了accept方法。
  • ObjectStructure:是一个包含元素角色的容器,提供让访问者对象遍历容器中所有元素的方法。
(5)类图:

在这里插入图片描述

(6)TypeScript代码:
// 抽象业务类
abstract class AbstractElement {
    // 定义业务逻辑
    public abstract doSomething() : void;
    // 允许谁来访问
    public abstract accept (visitor : Visitor) : void;
}

// 具体业务类1
class ConcreteElement1 extends AbstractElement{
    public doSomething() : void {
        console.log('ConcreteElement1执行的业务逻辑');
    }
    public accept(visitor : Visitor) : void {
        visitor.visit1(this)
    }
}

// 具体业务类2
class ConcreteElement2 extends AbstractElement{
    public doSomething() : void {
        console.log('ConcreteElement1执行的业务逻辑');
    }
    public accept(visitor : Visitor) : void {
        visitor.visit2(this)
    }
}

// 抽象访问类
abstract class Visitor {
    public abstract visit1(element1 : ConcreteElement1) : void;
    public abstract visit2(element2 : ConcreteElement2) : void;
}

// 具体访问类
class ConcreteVistor extends Visitor {
	// 业务类1业务访问
    public visit1(element1 : ConcreteElement1) : void {
        console.log('进入处理element1')
        element1.doSomething();
    }
	// 业务类2业务访问
    public visit2(element2 : ConcreteElement2) : void {
        console.log('进入处理element2');
        element2.doSomething();
    }
}
 
// 数据结构,管理很多业务(ConcreteElement1,ConcreteElement1)
class ObjectStructure {
	// 业务类列表
    private listSet : Set<AbstractElement>;
    constructor() {
        this.listSet = new Set();
    }
 
    // 增加业务
    public attach(element : AbstractElement) : void {
        this.listSet.add(element);
    }
 
    // 删除业务
    public detach(element : AbstractElement) : void {
        this.listSet.delete(element);
    }
 
    // 统一将业务对某个访问者进行开放
    public display(visitor : Visitor) : void {
        for (let element of this.listSet.values()) {
            element.accept(visitor);
        }
    }
}
 
function main() {
	// 业务管理者
    const objectStructure : ObjectStructure = new ObjectStructure();
	// 添加业务列表
    objectStructure.attach(new ConcreteElement1());
    objectStructure.attach(new ConcreteElement2());
	// 访问者
    const visitor :Visitor = new ConcreteVistor();
	// 业务管理者统一将业务对该访问者开放
    objectStructure.display(visitor);
}
 
main();
3.4迭代器模式
(1)定义:

提供一种方法访问一个容器对象中各个元素,而又不需暴露该对象的内部细节

(2)优缺点:
  • 优点:【1】提供一个统一的方法遍历对象,客户不用再考虑聚合的类型,使用一种方法就可以遍历对象了;【2】隐藏了聚合的内部结构,客户端要遍历聚合的时候只能取到迭代器而不会知道聚合的具体组合;【3】提供了一种设计思想,就是一个类应该只有一个引起变化的原因(叫做单一责任原则)。在聚合类中我们把迭代器分开,就是要把管理对象集合和遍历对象集合的责任分开,这样一来集合改变的话,只影响到聚合对象。而如果遍历方式改变的话,只影响到迭代器;
  • 缺点:每个聚合对象都要一个迭代器,会生成多个迭代器从而使得维护类不容易
(3)应用场景:
  • 当要展示一组相似对象或者遍历一组相同对象时使用
(4)角色:
  • Iterator:抽象迭代器定义负责访问和遍历元素的接口
  • ConcreteIterator:实现迭代器接口,完成容器元素的遍历
  • Aggregate:负责创建具体迭代器角色的接口,必然提供一个类似createIterator()这样的方法
  • ConcreteAggregate:实现容器接口定义的方法,创建出容纳迭代器的对象
(5)类图:

在这里插入图片描述

(6)TypeScript代码:
// 迭代功能接口
interface AbstractIterator {
    next() : any;
    hasNext() : boolean;
    remove() : boolean;
}
 
// 具体迭代器
class ConcreteIterator implements  AbstractIterator {
    private list : any[];
    public cursor : number = 0;
    constructor(array : any[]) {
        this.list = array;
    }
 
    public next() : any {
        return this.hasNext() ? this.list[this.cursor++] : null;
    }
 
    public hasNext() : boolean {
        return this.cursor < this.list.length;
    }
 
    public remove() : boolean{
        this.list.splice(this.cursor--, 1);
        return true;
    }
}

// 聚合功能接口
interface Aggregate {
    add(value : any) : void;
    remove(value : any) : void;
    createIterator() : AbstractIterator;
}

// 具体聚合器
class ConcreteAggregate implements Aggregate {
    // 容纳对象的容器
    private list : any[];
    constructor() {
        this.list = [];
    }
 
    add(value : any) : void {
        this.list.push(value)
    }
 
    remove(value : any) : void {
        const index = this.list.findIndex((listValue) => {
            return value === listValue;
        });
        this.list.splice(index, 1);
    }
 
    createIterator() : AbstractIterator {
        return new ConcreteIterator(this.list);
    }
}
 
function main() {
    const aggregate : Aggregate = new ConcreteAggregate();
    aggregate.add('11111');
    aggregate.add('222222');
 
    const iterator : AbstractIterator = aggregate.createIterator();
    while(iterator.hasNext()) {
        console.log(iterator.next());
    }
}
 
main();
3.5观察者模式
(1)定义:

定义对象间一种一对多的依赖关系,当一个对象改变状态,则所有依赖于它的对象都会得到通知并自动更新

(2)优缺点:
  • 优点:【1】在被观察者和观察者之间建立一个抽象的耦合;【2】观察者模式支持广播通讯,被观察者会向所有的登记过的观察者发出通知;
  • 缺点:观察者模式需要考虑一下开发效率和运行效率问题,一个观察者,多个观察者,开发和调试就会比较复杂;
(3)应用场景:
  • 对一个对象状态的更新需要其他对象同步更新,而且其他对象的数量动态可变
  • 对象仅需要将自己的更新通知给其他对象而不需要知道其他对象的细节
(4)角色:
  • Subject:定义被观察者实现的职责,用于管理观察者并通知观察者
  • Observer:观察者收到消息后进行update操作
  • ConcreteSubject:具体实现
  • ConcreteObserver:具体实现
(5)类图:

在这里插入图片描述

(6)与发布订阅模式的区别
  • 观察者模式存在问题:目标无法选择自己想要的消息发布,观察者会接收所有消息
  • 发布订阅模式:基于观察者模式的基础,在目标和观察者之间增加一个调度中心。订阅者(对应观察者)把自己想要订阅的事件注册到调度中心,当该事件触发的时候,发布者(对应目标)发布该事件到调度中心,由调度中心统一调度订阅者注册到调度中心的处理代码
  • 区别:观察者模式是由具体目标调度的,而发布/订阅模式是统一由调度中心调度的,所以观察者模式的订阅者与发布者之间是存在依赖的,而发布/订阅模式则不会,所以发布订阅模式的组件是松散耦合的
(7)TypeScript代码:
// 观察者模式
interface AbstractSubject {
    registerObserver(observer : Observer) : void;
    remove(observer : Observer) : void;
    notifyObservers() : void;
}
 
class ConcreteSubject implements AbstractSubject {
    private observers : Array<Observer>;
 
    constructor() {
        this.observers = [];
    }
 
    public registerObserver(observer : Observer) : void {
        this.observers.push(observer);
    };
 
    public remove(observer : Observer) : void {
        const observerIndex = this.observers.findIndex(value => {
            return value == observer;
        })
 
        observerIndex >= 0 && this.observers.splice(observerIndex, 1);
    };
 
    public notifyObservers() : void {
        this.observers.forEach(observer => observer.update())
    };
}
 
interface Observer {
    update() : void;
}
 
class ConcreteObserver1 implements Observer {
    public update() : void {
        console.log('已经执行更新操作1,值为');
    }
}
class ConcreteObserver2 implements Observer {
    public update() : void {
        console.log('已经执行更新操作2,值为');
    }
}
 
function main() {
    const subject : AbstractSubject = new ConcreteSubject();
    const observer1 : Observer = new ConcreteObserver1();
    const observer2 : Observer = new ConcreteObserver2();
    
    subject.registerObserver(observer1);
    subject.registerObserver(observer2);
 
    subject.notifyObservers();
}
 
main();

// 发布订阅模式
interface Publish {
    registerObserver(eventType : string, subscribe : Subscribe) : void;
    remove(eventType : string, subscribe ?: Subscribe) : void;
    notifyObservers(eventType : string) : void;
}
interface SubscribesObject{
    [key : string] : Array<Subscribe>
}
class ConcretePublish implements Publish {
    private subscribes : SubscribesObject;
 
    constructor() {
        this.subscribes = {};
    }
 
    registerObserver(eventType : string, subscribe : Subscribe) : void {
        if (!this.subscribes[eventType]) {
            this.subscribes[eventType] = [];
        }
 
        this.subscribes[eventType].push(subscribe);
    }
 
    remove(eventType : string, subscribe ?: Subscribe) : void {
        const subscribeArray = this.subscribes[eventType];
        if (subscribeArray) {
            if (!subscribe) {
                delete this.subscribes[eventType];
            } else {
                for (let i = 0; i < subscribeArray.length; i++) {
                    if (subscribe === subscribeArray[i]) {
                        subscribeArray.splice(i, 1);
                    }
                }
            }
        }
    }
 
    notifyObservers(eventType : string, ...args : any[]) : void {
        const subscribes = this.subscribes[eventType];
        if (subscribes) {
            subscribes.forEach(subscribe => subscribe.update(...args))
        }
    }
}
 
interface Subscribe {
    update(...value : any[]) : void;
}
 
class ConcreteSubscribe1 implements Subscribe {
    public update(...value : any[]) : void {
        console.log('已经执行更新操作1,值为', ...value);
    }
}
class ConcreteSubscribe2 implements Subscribe {
    public update(...value : any[]) : void {
        console.log('已经执行更新操作2,值为', ...value);
    }
}
 
function main() {
    const publish = new ConcretePublish();
    const subscribe1 = new ConcreteSubscribe1();
    const subscribe2 = new ConcreteSubscribe2();
 
    publish.registerObserver('1', subscribe1);
    publish.registerObserver('2', subscribe2);
 
    publish.notifyObservers('2', '22222');
}
 
main();
3.6中介者模式
(1)定义:

用一个中介对象封装一系列的对象交互,中介者使各对象不需要显示地相互作用,从而使其耦合松散,而且可以独立地改变它们之间的交互

(2)优缺点:
  • 优点:【1】多个类相互耦合,会形成网状结构,使用中介者模式将网状结构分离为星型结构从而进行解耦;【2】减少类间依赖,降低了耦合,符合迪米特原则;
  • 缺点:【1】中介者承担了较多的责任,一旦中介者出现了问题,整个系统就会收到影响;【2】设计不当会使中介者对象本身变得过于复杂;
(3)应用场景:
  • 适用于多个对象之间紧密耦合的情况(紧密耦合的标准是:在类图中出现了蜘蛛网状结构),这种情况要考虑中介者模式,将蜘蛛网梳理为星型结构
  • 如:机场调度中心、MVC框架、媒体网关、中介服务等
(4)角色:
  • Mediator:抽象中介者角色定义统一的接口,用于各同事角色之间的通信
  • ConcreteMediator:通过协调各同事角色实现协作行为
  • Colleague:抽象同事角色
  • ConcrerteColleague:每个同事只知道自己的行为,与其他同事角色通信的时候一定要通过中介者角色协作
(5)类图:

在这里插入图片描述

(6)TypeScript代码:
abstract class Colleague {
    public abstract onEvent(eventType : string) : void;
}
 
class ConcreteColleagueA extends Colleague{
    private mediator : Mediator;
    constructor(mediator : Mediator) {
        super();
        this.mediator = mediator;
    }
    public onEvent(eventType : string) : void {
        this.mediator.doEvent(eventType);
    }
 
    // 自己的一些事情
    public doSomething() : void {
        console.log('A被运行了');
    }
}
 
class ConcreteColleagueB extends Colleague {
    private mediator : Mediator;
    constructor(mediator : Mediator) {
        super();
        this.mediator = mediator;
    }
 
    public onEvent(eventType : string) : void {
        this.mediator.doEvent(eventType);
    }
 
    // 自己的一些事情
    public doSomething() : void {
        console.log('B被运行了');
    }
}
 
abstract class Mediator {
    protected _colleagueA ?: ConcreteColleagueA;
    protected _colleagueB ?: ConcreteColleagueB;
 
    set colleagueA(colleagueA : ConcreteColleagueA) {
        this._colleagueA = colleagueA;
    }
 
    set colleagueB(colleagueB : ConcreteColleagueB) {
        this._colleagueB = colleagueB;
    }
    public abstract doEvent(eventType : string) : void;
}
 
class ConcreteMediator extends Mediator {
    //1. 根据得到消息,完成对应任务
	//2. 中介者在这个方法,协调各个具体的同事对象,完成任务
    public doEvent(eventType : string) : void {
        switch (eventType) {
            case "A": {
                this.doColleagueAEvent();
                break;
            }
            case "B": {
                this.doColleagueBEvent();
                break;
            }
            default: {
 
            }
        }
    }
 
    // 相应业务逻辑
    public doColleagueAEvent() : void {
        super._colleagueA && super._colleagueA.doSomething();
        super._colleagueB && super._colleagueB.doSomething();
        console.log('A-B执行完毕');
    }
 
    public doColleagueBEvent() : void {
        super._colleagueB && super._colleagueB.doSomething();
        super._colleagueA && super._colleagueA.doSomething();
        console.log('B-A执行完毕');
    }
}
 
 
function main() {
    const mediator : Mediator = new ConcreteMediator();
    const myColleagueA : ConcreteColleagueA = new ConcreteColleagueA(mediator);
    const myColleagueB : ConcreteColleagueB = new ConcreteColleagueB(mediator);
    mediator.colleagueA = myColleagueA;
    mediator.colleagueB = myColleagueB;
 
    myColleagueA.onEvent('A');
    myColleagueB.onEvent('B');
}
 
main();
3.7备忘录模式
(1)定义:

在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可将该对象恢复到原来保存的状态

(2)优缺点:
  • 优点:【1】给用户提供了一种可以恢复状态的机制,可以使用户能够比较方便地回到某个历史的状态;【2】实现了信息的封装,使得用户不需要关心状态的保存细节;
  • 缺点:如果类的成员变量过多,势必会占用比较大的资源,而且每一次保存都会消耗一定的内存,这个需要注意;
(3)应用场景:
  • 需要保存和恢复数据的相关状态场景,例如:Word中的CTRL+Z组合键,IE浏览器中的后退按钮,文件管理器上的backspace键等
  • 需要监控的副本场景中
  • 数据库连接的事物管理就是用的备忘录模式
(4)角色:
  • Originator:记录当前时刻的内部状态,负责定义哪些署于备份范围的状态,负责创建和恢复备忘录数据
  • Memento:负责存储Originator发起人对象的内部状态,在需要的时候提供发起人需要的内部状态
  • Caretaker:负责保存多个备忘录对象,使用集合管理,提高效率
(5)类图:

在这里插入图片描述

(6)TypeScript代码:
// 备忘录
class Memento {
    private _state : string;
    constructor(state : string) {
        this._state = state;
    }
 
    get state() : string {
        return this._state;
    }
}

// 发起者
class Originator {
    private _state : string = '';
    constructor() {}
 
    get state() {
        return this._state;
    }
 
    set state(value) {
        this._state = value;
    }
 
    // 创建一个备忘录
    public createMemento() : Memento {
        console.log('创建了一个备忘录!');
        return new Memento(this._state);
    }
 
    // 恢复一个备忘录
    public recoverMemento(memento : Memento) {
        console.log('恢复了一个备忘录!');
        this.state = memento.state;
    }
}

// 看守者
class Caretaker {
    // 保存一次状态用此,保存多次用数组
    private memento ?: Memento;
 
    public getMemento() : Memento | undefined {
        return this.memento;
    }
 
    public setMemento(memento : Memento) {
        this.memento = memento;
    }
}


function main() {
    // 定义发起人
    const originator : Originator = new Originator();
    // 定义守护者
    const caretaker : Caretaker = new Caretaker();
    // 创建一个备忘录
    const memento : Memento = originator.createMemento();
    // 将备忘录存储到守护者
    caretaker.setMemento(memento);
    // 恢复一个备忘录
    originator.recoverMemento(memento);
}
 
main();
3.8解释器模式
(1)定义:

给定一门语言,定义它的文法的一种表示,并定义一个解释器,该解释器使用该表示来解释语言中的句子

(2)优缺点:
  • 优点:扩展性好,修改语法规则只需要修改相应的非终结符表达式就可以,若扩展语法则只需要增加非终结符类就可以;
  • 缺点:【1】解释器模式会引起类膨胀(非终结符表达式);【2】解释器模式采用递归调用方法,将会导致调试非常复杂;【3】效率可能降低(因为使用了大量的循环和递归);
(3)应用场景:
  • 应用可以将一个需要解释执行的语言中的句子表示为一个抽象语法树
  • 重复发生的问题可以使用解释器模式,例如多个服务器产生的日志进行分析(各个服务器的日志格式不同,但是数据要素是相同的)
  • 一个简单语法需要解释的场景
  • 编译器、运算表达式计算、正则表达式、机器人等
(4)角色:
  • AbstractExpression:具体的解释任务由各个实现类完成,具体的解释器分别由TerminalExpression和NonTeminalExpression完成
  • TerminalExpression:实现与文法中的元素相关联的解释操作,通常一个解释器模式中只有一个终结符表达式,但有多个实例,对应不同的终结符
  • NonTeminalExpression:文法中每条规则对应于一个非终结表达式,具体到四则运算中就是加减法规则分别对应到AddExpression和SubExpresssion两个类
  • Context:含有解释器之外的全局信息
(5)类图:

在这里插入图片描述

(6)TypeScript代码:
// 以下是一个规则检验器实现,具有 and 和 or 规则,通过规则可以构建一颗解析树,用来检验一个文本是否满足解析树定义的规则。
 
// 例如一颗解析树为 D And (A Or (B C)),文本 "D A" 满足该解析树定义的规则
abstract class Expression {
    public abstract interpreter(str : string) : boolean;
}
 
class TerminalExpression extends Expression {
    private literal : string;
    constructor(str : string) {
        super();
        this.literal = str;
    }
 
    public interpreter(str : string) : boolean {
        for (let charVal of str) {
            if (charVal === this.literal) {
                return true;
            }
        }
 
        return false;
    }
}
 
class AndExpression extends Expression {
    private expression1 : Expression;
    private expression2 : Expression;
 
    constructor(expression1 : Expression, expression2 : Expression) {
        super();
        this.expression1 = expression1;
        this.expression2 = expression2;
    }
 
    public interpreter(str : string) : boolean {
        return this.expression1.interpreter(str) && this.expression2.interpreter(str);
    }
}
 
class OrExpression extends Expression {
    private expression1 : Expression;
    private expression2 : Expression;
 
    constructor(expression1 : Expression, expression2 : Expression) {
        super();
        this.expression1 = expression1;
        this.expression2 = expression2;
    }
 
    public interpreter(str : string) : boolean {
        return this.expression1.interpreter(str) || this.expression2.interpreter(str);
    }
}
 
function buildInterpreterTree() {
    const terminal1 : Expression = new TerminalExpression('A');
    const terminal2 : Expression = new TerminalExpression('B');
    const terminal3 : Expression = new TerminalExpression('C');
    const terminal4 : Expression = new TerminalExpression('D');
 
    // B And C
    const alternation1 : Expression = new AndExpression(terminal2, terminal3);
    // A Or (B C)
    const alternation2 : Expression = new OrExpression(terminal1, alternation1);
    // D And (A Or (B C))
    return new AndExpression(terminal4, alternation2);
}
 
function main() {
    const define : Expression = buildInterpreterTree();
    const context1 : string = "D A";
    const context2 : string = "D B C";
    console.log(define.interpreter(context1));
    console.log(define.interpreter(context2));
}
 
main();
3.9状态模式
(1)定义:

当一个对象内在状态改变时允许其改变行为,这个对象看起来像改变了其类

(2)优缺点:
  • 优点:【1】代码有很强的可读性。状态模式将每个状态的行为封装到对应的一个类中;【2】方便维护。将容易产生问题的if-else语句删除了,如果把每个状态的行为都放到一个类中,每次调用方法时都要判断当前是什么状态,不然会产出很多的if-else语句而且容易出错;【3】符合"开闭原则"。容易增删状态;
  • 缺点:会产生很多类。每个状态都要一个对应的类,当状态过多时会产生很多类,加大维护难度;
(3)应用场景:
  • 行为随状态改变而改变的场景。当一个事件或者对象有很多状态,状态之间会相互转换,对不同的状态要求有不同的行为的时候,可以考虑使用状态模式
  • 条件、分支判断语句的替代者。在抽象中大量使用switch语句或者if判断语句会导致程序结构不清晰,使用状态模式,通过扩展子类实现了条件的判断处理
(4)角色:
  • State:接口或抽象类,负责对象状态定义,并且封装环境角色以实现状态切换
  • ConcreteState:每一个具体状态必须完成两个职责(本状态的行为管理以及取向状态处理)
  • Context:定义客户端需要的接口,并且负责具体状态的切换
(5)类图:

在这里插入图片描述

(6)TypeScript代码:
abstract class State {
    public abstract handle1() : void;
    public abstract handle2() : void;
}
 
class ConcreteState1 extends State {
    private context : Context;
    constructor(context : Context) {
        super();
        this.context = context;
    }
 
    // 本状态下需要处理的逻辑
    public handle1() : void {
        console.log('State1的状态需要处理的逻辑');
    }
 
    // 将进行状态转移
    public handle2() : void {
        this.context.currentState = this.context.STATE2;
        console.log('由状态state1转为state2');
    }
}
 
class ConcreteState2 extends State {
    private context : Context;
    constructor(context : Context) {
        super();
        this.context = context;
    }
 
    // 进行状态转移
    public handle1() : void {
        this.context.currentState = this.context.STATE1;
        console.log('由状态state2转为state1');
    }
 
    // 本状态下的处理逻辑
    public handle2() : void {
        console.log('State2的状态需要处理的逻辑');
    }
}
 
class Context {
    public STATE1 : State = new ConcreteState1(this);
    public STATE2 : State = new ConcreteState2(this);
    public currentState : State;
 
    constructor() {
        this.currentState = this.STATE1;
    }
 
    public doOperation1() {
        this.currentState?.handle2();
    }
    public doOperation2() {
        this.currentState?.handle1();
    }
}
 
function main() {
    const context : Context = new Context();
    context.doOperation1();
    context.doOperation2();
}
 
main();
3.10策略模式
(1)定义:

定义一组算法、将每个算法都封装,并且使它们之间可以互换

(2)优缺点:
  • 优点:【1】算法可以自由切换。只要实现抽象策略,它就成为策略家族的一个成员,通过封装角色对齐进行封装,保证对外提供"可自由切换"的策略;【2】避免使用多重条件判断;【3】扩展性良好。客户端增加行为不用修改原有代码,只要添加一种策略即可。
  • 缺点:【1】策略类数量增多会导致类数目庞大;【2】所有的策略类都需要对外暴露,对于这个缺陷可通过工厂方法模式、代理模式或享元模式修正;
(3)应用场景:
  • 多个类只有在算法或行为上稍有不同的场景
  • 算法需要自由切换的场景
  • 需要屏蔽算法规则的场景
(4)角色:
  • Context:起承上启下封装作用,屏蔽高层模块对策略、算法的直接访问,封装可能存在的变化
  • Strategy:策略、算法家族的抽象,通常为接口,定义每个策略或算法必须具有的方法和属性
  • ConcreteStrategy:实现抽象策略中的操作
(5)类图:

在这里插入图片描述

(6)TypeScript代码:
// 策略接口
interface Strategy {
    // 策略模式运算法则
    doSomething() : void;
}

// 具体策略1
class ConcreteStrategy1 implements Strategy {
    public doSomething() : void {
        console.log('使用的策略1');
    }
}
 
// 具体策略2
class ConcreteStrategy2 implements Strategy {
    public doSomething() : void {
        console.log('使用的策略2');
    }
}

// 全局策略
class ContextofStrategy {
    private _strategy : Strategy;
    constructor(strategy : Strategy) {
        this._strategy = strategy;
    }
 
    set strategy(strategy : Strategy) {
        this._strategy = strategy;
    }
 
    //封装后的策略方法
    doOperation() : void {
        this._strategy.doSomething();
    }
}
 
function main() {
    const strategy1 : Strategy = new ConcreteStrategy1();
    const strategy2 : Strategy = new ConcreteStrategy2();
    const context : ContextofStrategy = new ContextofStrategy(strategy1);
    context.doOperation();
    context.strategy = strategy2;
    context.doOperation();
}
 
main();
3.11职责链模式
(1)定义:

使多个对象都要机会处理请求,从而避免了请求发送者和接收者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,直到有对象处理它为止

(2)优缺点:
  • 优点:【1】将请求和处理分开,实现解耦,提高系统的灵活性;【2】简化了对象,使对象不需要知道链的结构;
  • 缺点:【1】性能会受到影响,特别使在链比较长的时候,因此需要控制链;【2】调试不方便。采用了类似递归的方式,调试时逻辑可能比较复杂;
(3)应用场景:
  • 最佳应用场景:有多个对象可以处理同一个请求时,例如多级请求、请假审批流程、拦截器等。
(4)角色:
  • Handler:定义了一个处理请求的接口,同时包含另外的Handler
  • ConcreteHandler:处理它自己负责的请求,可以访问它的后继者(即下一个处理者),如果可以处理当前请求则处理,否则就将该请求交给后继者去处理,从而形成一个职责链
  • Request:包含很多属性,表示一个请求
(5)类图:

在这里插入图片描述

(6)TypeScript代码:
abstract class Handler {
    // 下一个处理者
    public successor ?: Handler;
    public name : string;
    constructor(name : string) {
        this.name = name;
    }
 
    public abstract handleRequest(request : MyRequest) : void;
 
    public setNext(successor : Handler) : void {
        this.successor = successor;
    }
}
 
class ConcreteHandler1 extends Handler {
    constructor(name : string) {
        super(name);
    }
 
    public handleRequest (request : MyRequest) : void {
        // 首先判断当前级别是否能够处理,不能够处理则交给下一个级别处理
        if (request.level <= 1) {
            console.log('被一级处理');
        } else {
            // 交给下一级处理
            this.successor && this.successor.handleRequest(request);
        }
    }
}
 
class ConcreteHandler2 extends Handler {
    constructor(name : string) {
        super(name);
    }
 
    public handleRequest (request : MyRequest) : void {
        // 首先判断当前级别是否能够处理,不能够处理则交给下一个级别处理
        if (request.level > 1 && request.level <= 2) {
            console.log('被二级处理');
        } else {
            // 交给下一级处理
            this.successor && this.successor.handleRequest(request);
        }
    }
}
 
class ConcreteHandler3 extends Handler {
    constructor(name : string) {
        super(name);
    }
 
    public handleRequest (request : MyRequest) : void {
        // 首先判断当前级别是否能够处理,不能够处理则交给下一个级别处理
        if (request.level > 2) {
            console.log('被三级处理');
        } else {
            // 交给下一级处理
            this.successor && this.successor.handleRequest(request);
        }
    }
}
 
class MyRequest {
    private _level : number;
    constructor(level : number) {
        this._level = level;
    }
 
    get level() : number {
        return this._level;
    }
 
    set level(value : number) {
        this._level = this.level;
    }
}
 
function main() {
    // 创建一个请求
    const request : MyRequest = new MyRequest(5);
 
    // 创建相关处理人
    const handler1 : Handler = new ConcreteHandler1('lili');
    const handler2 : Handler = new ConcreteHandler2('linlin');
    const handler3 : Handler = new ConcreteHandler3('shunshun');
 
    // 设置下级别审批,构成环形结构
    handler1.setNext(handler2);
    handler2.setNext(handler3);
    handler3.setNext(handler1);
 
    handler1.handleRequest(request);
}
 
main();

参考来源:图解23种设计模式

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值