组件协作模式¶
概念¶
定义¶
- “组件协作” 模式通过 晚绑定,来实现 框架与应用程序 之间的松耦合,是二者协作式常用的模式。
要点¶
- 关注 “框架与应用程序的划分”;
- 要有「框架」与「应用」的区隔思维。
模版方法模式(TemplateMethod)¶
动机¶
- 在软件建构过程中,对于某一项任务,它常常有 稳定 的整体操作结构,但各个子步骤却有很多 改变 的需求。
- 如何在确定稳定操作结构的前提下,来灵活应对各个子步骤的变化或晚期实现需求?
定义¶
- 父类定义算法骨架(稳定),而将算法的一些步骤(变化)延迟到子类中;
- Template Method 使得子类可以不改变(复用)该算法结构的情况下,重定义(override 重写)该算法的某些特定步骤。
UML 类图¶
角色¶
-
抽象类(Abstract Class):抽象模板类,负责给出一个算法的轮廓和骨架。
- 模板方法:定义了算法的骨架,按某种顺序调用其包含的基本方法。
- 基本方法:是整个算法中的一个步骤,包含抽象方法/具体方法/钩子方法
-
具体子类/具体实现(Concrete Class):实现抽象类中所定义的抽象方法和钩子方法
代码¶
// 抽象类
abstract class AbstractClass {
// 模板方法
public TemplateMethod(): void {
this.SpecificMethod();
this.abstractMethod1();
this.abstractMethod2();
}
// 具体方法
public SpecificMethod(): void {
console.log('抽象类中的具体方法被调用...');
}
// 抽象方法1
public abstract abstractMethod1(): void;
// 抽象方法2
public abstract abstractMethod2(): void;
}
// 具体子类
class ConcreteClass extends AbstractClass {
public abstractMethod1(): void {
console.log('抽象方法1的实现被调用...');
}
public abstractMethod2(): void {
console.log('抽象方法2的实现被调用...');
}
}
export default class TemplateMethodPattern {
public static main(args: string[]): void {
const tm: AbstractClass = new ConcreteClass();
tm.TemplateMethod();
}
}
要点总结¶
- Template Method模式是一种非常基础性的设计模式,在面向对象系统中有着大量的应用。它用最简洁的机制(虚函数的多态性)为很多应用程序框架提供了灵活的扩展点,是代码复用方面的基本实现结构。
- 除了可以灵活应对子步骤的变化外,“不要调用我,让我来调用你” 的反向控制结构是Template Method的典型应用。
- 在具体实现方面,被Template Method调用的虚方法可以具有实现,也可以没有任何实现(抽象方法、纯虚方法),但一般推荐将它们设置为protected方法。
- 绝大多数软件框架中都有 Template Method 模式。
- 设计模式必须基于一个稳定点,如果全不稳定就不存在任何适用的设计模式了;反之,如果全稳定就不需要设计模式了。
- Template Method 意为“样板”。run()方法就是样板,细节定义为纯虚函数,交由子类(用户)去定义。
策略模式(Strategy)¶
动机¶
- 在软件构建过程中,某些对象使用的算法可能多种多样,经常改变,如果将这些算法都编码到对象中,将会使对象变得异常复杂;而且有时候支持不使用的算法也是一个性能负担。
- 如何在运行过程是根据需要的改变对象的算法?将算法和对象本身解耦,从而避免上述问题?
模式定义¶
- 定义一系列算法,把它们一个个封装起来,并使用它们相互替换(变化)。该模式使得算法可独立于使用它们的用户程序(稳定)而变化(扩展,子类化)。
UML 类图¶
- 策略模式
- 策略工厂模式
角色¶
- 抽象策略类(Strategy):定义了一个公共接口,各种不同的算法以不同的方式实现这个接口,环境角色使用这个接口调用不同的算法,一般使用接口或抽象类实现。
- 具体策略类(ConcreteStrategy):实现了抽象策略定义的接口,提供具体的算法实现。
- 环境类(Context):持有一个策略类的引用,最终给客户端调用。
代码¶
// 抽象策略类
interface Strategy {
strategyMethod(): void; // 策略方法
}
// 具体策略类A
class ConcreteStrategyA implements Strategy {
public strategyMethod(): void {
console.log('具体策略A的策略方法被访问!');
}
}
// 具体策略类B
class ConcreteStrategyB implements Strategy {
public strategyMethod(): void {
console.log('具体策略B的策略方法被访问!');
}
}
// 环境类
class Context {
private strategy: Strategy;
public getStrategy(): Strategy {
return this.strategy;
}
public setStrategy(strategy: Strategy): void {
this.strategy = strategy;
}
public strategyMethod(): void {
this.strategy.strategyMethod();
}
}
export default class StrategyPattern {
public static main(args: string[]): void {
const c: Context = new Context();
let s: Strategy = new ConcreteStrategyA();
c.setStrategy(s);
c.strategyMethod();
console.log('---------');
s = new ConcreteStrategyB();
c.setStrategy(s);
c.strategyMethod();
}
}
要点总结¶
- Strategy及其子类为组件提供了一系列可重用的算法,从而可以使得类型在 运行时 方便的根据需要在各个算法之间进行切换。
- Strategy模式提供了用判断语句以外的另一种选择, 消除条件判断语句 ,就是在解耦合;含有许多判断条件判断语句的代码通常需要Strategy模式
- 如果Strategy对象没有实例变量,那么上下文可以共享一个Strategy对象,从而节省对象开销。
观察者模式(Observer)¶
动机¶
- 在软件构建过程中,我们需要为某些对象建立一种“通知依赖关系”:一个对象的(Subject)的状态发生改变,所有的依赖对象(Observer)都将得到通知。如果这样的依赖关系过于紧密,将使软件不能很好的抵御变化。
- 使用面向对象技术,可以将这种依赖关系弱化,并形成一种稳定的依赖关系。从而实现软件体系的松耦合。
定义¶
- 定义对象间的一对多(变化)的依赖关系,以便当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并自动更新。
- 又称 “发布-订阅模式”;符合依赖倒置原则。
UML 类图¶
角色¶
- 抽象主题(Subject)角色:也叫抽象目标类,它提供了一个用于保存观察者对象的聚集类和增加、删除观察者对象的方法,以及通知所有观察者的抽象方法。
- 具体主题(Concrete Subject)角色:也叫具体目标类,它实现抽象目标中的通知方法,当具体主题的内部状态发生改变时,通知所有注册过的观察者对象。
- 抽象观察者(Observer)角色:它是一个抽象类或接口,它包含了一个更新自己的抽象方法,当接到具体主题的更改通知时被调用。
- 具体观察者(Concrete Observer)角色:实现抽象观察者中定义的抽象方法,以便在得到目标的更改通知时更新自身的状态。
代码¶
// 抽象目标
abstract class Subject {
protected observers: Array<Observer> = new Array<Observer>();
// 增加观察者方法
public add(observer: Observer): void {
this.observers.push(observer);
}
// 删除观察者方法
public remove(observer: Observer): void {
const index = this.observers.indexOf(observer);
if (index > -1) {
this.observers.splice(index, 1);
}
}
public abstract notifyObserver(): void; // 通知观察者方法
}
// 具体目标
class ConcreteSubject extends Subject {
public notifyObserver(): void {
console.log('具体目标发生改变...');
console.log('--------------');
for (const obs of this.observers) {
obs.response();
}
}
}
// 抽象观察者
interface Observer {
response(): void; // 反应
}
// 具体观察者1
class ConcreteObserver1 implements Observer {
public response(): void {
console.log('具体观察者1作出反应!');
}
}
// 具体观察者1
class ConcreteObserver2 implements Observer {
public response(): void {
console.log('具体观察者2作出反应!');
}
}
export default class ObserverPattern {
public static main(args: string[]): void {
const subject: Subject = new ConcreteSubject();
const obs1: Observer = new ConcreteObserver1();
const obs2: Observer = new ConcreteObserver2();
subject.add(obs1);
subject.add(obs2);
subject.notifyObserver();
}
}
要点总结¶
- 使用面向对象的抽象,Observer观察者模式可以使得我们独立地改变目标与观察者,从而使二者的关系达到松耦合。
- 目标发送通知时,无需指定观察者,通知(可以携带通知信息作为参数)会自动传播。
- 观察者可以自己决定是否需要订阅通知,目标对象对此一无所知。