Skip to content

单一职责模式

概念

定义

  • 软件组件的设计中,如果责任划分的不清晰,使用继承得到的结果往往是随着需求的变化,子类急剧膨胀,同时充斥着重复代码;这时候的关键是划清责任。

要点

  • 责任划分不清,必然导致重复代码
  • 单一职责原则

装饰模式(Decorator)

动机(Motivation)

  • 在某些情况下我们可能会 “过度地使用继承来扩展对象的功能”,由于继承为类型引入的静态特质,使得这种扩展方式缺乏灵活性; 并且随着子类的增多(扩展功能的增多),各种子类的组合(扩展功能的组合)会导致更多子类的膨胀。
  • 如何使 “对象功能的扩展” 能够根据需要来动态地实现? 同时避免“扩展功能的增多”带来的子类膨胀问题? 从而使得任何“功能扩展变化”所导致的影响将为最低?

模式定义

  • 动态(组合)地给一个对象增加一些额外的职责。就增加功能而言,Decorator模式比生成子类(继承)更为灵活(消除重复代码 & 减少子类个数)。

UML 类图

decorator

角色

  • 抽象构件(Component)角色:定义一个抽象接口及规范,准备接收附加责任的对象。
  • 具体构件(ConcreteComponent)角色:实现抽象构件,通过装饰角色为其添加一些职责。
  • 抽象装饰(Decorator)角色:继承抽象构件,并包含具体构件的实例,可以通过其子类扩展具体构件的功能。
  • 具体装饰(ConcreteDecorator)角色:实现抽象装饰的相关方法,并给具体构件对象添加附加的责任。

代码

// 抽象构件角色
interface Component {
  operation(): void;
}

// 具体构件角色
class ConcreteComponent implements Component {
  constructor() {
    console.log('创建具体构件角色');
  }

  public operation(): void {
    console.log('调用具体构件角色的方法operation()');
  }
}

// 抽象装饰角色
class Decorator implements Component {
  private component: Component;

  constructor(component: Component) {
    this.component = component;
  }

  public operation(): void {
    this.component.operation();
  }
}

// 具体装饰角色
class ConcreteDecorator extends Decorator {
  constructor(component: Component) {
    super(component);
  }

  public operation(): void {
    super.operation();
    this.addedFunction();
  }

  public addedFunction(): void {
    console.log('为具体构件角色增加额外的功能 addedFunction()');
  }
}

export default class DecoratorPattern {
  public static main(args: string[]): void {
    const p: Component = new ConcreteComponent();
    p.operation();
    console.log('----------');
    const d: Component = new ConcreteDecorator(p);
    d.operation();
  }
}

要点总结

  • 通过采用组合而非继承的手法, Decorator模式实现了在 运行时 动态扩展对象功能的能力,而且可以根据需要扩展多个功能。 避免了使用继承带来的“灵活性差”和“多子类衍生问题”。
  • Decorator类在接口上表现为is-a Component的继承关系,即 Decorator类继承 Component类所具有的接口。 但在实现上又表现为 has-a Component的组合关系,即Decorator类又使用了另外一个Component类。
  • Decorator模式的目的并非解决“多子类衍生的多继承”问题,Decorator模式应用的要点在于解决“主体类在多个方向上的扩展功能”——是为“装饰”的含义。
  • 如果类 A 继承类 B,同时类 A 又组合类 B,则类 A 有99%的可能是 Decorator 设计模式。

桥接模式(Bridge)

动机

  • 由于某些类型的固有的实现逻辑,使得它们具有两个变化的维度,乃至多个维度的变化。
  • 如何应对这种“多维度的变化”? 如何利用面向对象技术来使得类型可以轻松地沿着两个乃至多个方向变化,而不引入额外的复杂度?

模式定义

  • 将抽象部分(业务功能)与实现部分(平台实现)分离,使它们都可以独立地变化。
  • 抽象与实现分离

UML 类图

x

角色

  • 抽象化(Abstraction)角色:定义抽象类,并包含一个对实现化对象的引用。
  • 扩展抽象化(Refined Abstraction)角色:是抽象化角色的子类,实现父类中的业务方法,并通过组合关系调用实现化角色中的业务方法。
  • 实现化(Implementor)角色:定义实现化角色的接口,供扩展抽象化角色调用。
  • 具体实现化(Concrete Implementor)角色:给出实现化角色接口的具体实现。

代码

// 实现化角色
interface Implementor {
  OperationImpl(): void;
}

// 具体实现化角色
class ConcreteImplementorA implements Implementor {
  public OperationImpl(): void {
    console.log('具体实现化(Concrete Implementor)角色被访问');
  }
}

// 抽象化角色
abstract class Abstraction {
  protected imple: Implementor;

  protected constructor(imple: Implementor) {
    this.imple = imple;
  }

  public abstract Operation(): void;
}

// 扩展抽象化角色
class RefinedAbstraction extends Abstraction {
  constructor(imple: Implementor) {
    super(imple);
  }

  public Operation(): void {
    console.log('扩展抽象化(Refined Abstraction)角色被访问');
    this.imple.OperationImpl();
  }
}

export default class BridgeTest {
  public static main(args: string[]): void {
    const imple: Implementor = new ConcreteImplementorA();
    const abs: Abstraction = new RefinedAbstraction(imple);
    abs.Operation();
  }
}

要点总结

  • Bridge模式使用 “对象间的组合关系”解耦了抽象和实现之间固有的绑定关系,使得抽象和实现可以沿着各自的维度来变化。 所谓抽象和实现沿着各自纬度的变化,即“子类化”它们。
  • Bridge模式有时候类似于多继承方案,但是多继承方案往往违背单一职责原则(即一个类只有一个变化的原因),复用性比较差。 Bridge模式是比多继承方案更好的解决方法。
  • Bridge模式的应用一般在“两个非常强的变化维度”,有时一个类也有多于两个的变化维度,这时可以使用Bridge的扩展模式。