装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其结构。这种类型的设计模式属于结构型模式,它是作为现有的类的一个包装。
介绍
意图:动态地给一个对象添加一些额外的职责。就增加功能来说,装饰器模式相比生成子类更为灵活。
主要解决:一般的,我们为了扩展一个类经常使用继承方式实现,由于继承为类引入静态特征,并且随着扩展功能的增多,子类会很膨胀。
优点:装饰类和被装饰类可以独立发展,不会相互耦合,装饰模式是继承的一个替代模式,装饰模式可以动态扩展一个实现类的功能。
缺点:
1.多层装饰比较复杂。 2.会导致程序中增加很多功能类似的很小的类
使用场景: 1、扩展一个类的功能。 2、动态增加功能,动态撤销。
注意事项:可代替继承。
示例 Display.ts 用于显示字符串的抽象类 1 2 3 4 5 6 7 8 9 10 11 12 13 export default abstract class Display { public abstract getColumns(): number;//获取横向字符数 public abstract getRows(): number;//获取纵向行数 public abstract getRowText(row: number): string;//获取第row行的字符串 /** *显示所有字符 */ public show(): void { for (let i = 0; i < this.getRows(); i++) { console.log(this.getRowText(i)); } } }
StringDisplay.ts 用于显示单行字符串的类 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 import Display from "./Display"; export default class StringDisplay extends Display { private str: string;//要显示的字符串 constructor(str: string) { super(); this.str = str; } public getColumns(): number {//字符数 return this.str.length; } public getRows(): number {//行数1 return 1; } public getRowText(row: number): string {//只显示第一行 if (row == 0) { return this.str; } else { return null; } } }
Border.ts 用于显示装饰边框的抽象类 1 2 3 4 5 6 7 8 9 import Display from "./Display"; export default abstract class Border extends Display { protected display: Display;//装饰物 constructor(display: Display) { super(); this.display = display; } }
SideBorder.ts 用于显示左右边框的类 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 import Display from "./Display"; import Border from "./Border"; export default class SideBorder extends Border { private borderChar: string;//装饰边框的字符 constructor(display: Display, ch: string) {//指定Display和装饰边框字符 super(display); this.borderChar = ch; } public getColumns(): number { return 1 + this.display.getColumns() + 1;//字符数加上两侧边框的字符数 } public getRows(): number { return this.display.getRows(); } public getRowText(row: number) {//字符串加上边框 return this.borderChar + this.display.getRowText(row) + this.borderChar; } }
FullBorder.ts 显示上下左右边框 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 import Display from "./Display"; import Border from "./Border"; export default class FullBorder extends Border { constructor(display: Display) { super(display); } public getColumns(): number { return 1 + this.display.getColumns() + 1;//增加左右边框字符数 } public getRows(): number { return this.display.getRows() + 2;//增加上下边框字符数 } public getRowText(row: number) { if (row == 0 || row == this.display.getRows() + 1) {//上线边框 return `+${this.makeLine('-', this.display.getColumns())}+`; } else { return `|${this.display.getRowText(row - 1)}|`;//左右边框 } } private makeLine(ch: string, count: number) { let str = ""; for (let i = 0; i < count; i++) { str = str + ch; } return str; } }
index.ts 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 import Display from "./Display"; import StringDisplay from "./StringDisplay"; import SideBorder from "./SideBorder"; import FullBorder from "./FullBorder"; const b1: Display = new StringDisplay("Hello,World.");//直接显示 const b2: Display = new SideBorder(b1, '#');//增加左右边框'#' const b3: Display = new FullBorder(b2);//上下左右都加边框 b1.show(); b2.show(); b3.show(); const b4: Display = new SideBorder(//多层边框 new FullBorder( new FullBorder( new SideBorder( new FullBorder(new StringDisplay("Hello,World.")), "*" ) ) ), "/" ) b4.show();
result 1 2 3 4 5 6 7 8 9 10 11 12 Hello,World. //b1 #Hello,World.# //b2 +--------------+ //b3 |#Hello,World.#| +--------------+ /+------------------+/ //b4 /|+----------------+|/ /||*+------------+*||/ /||*|Hello,World.|*||/ /||*+------------+*||/ /|+----------------+|/ /+------------------+/
类图
角色
Component 增加功能时的核心角色。示例中Display类扮演该角色
ConcreteComponent 该角色实现了Component角色定义的接口。在示例中StringDisplay类扮演该角色
Decorator(装饰物) 该角色具有与Component角色相同的接口,在内部保存了了被装饰的对象->Component。Decorator橘色知道自己要装饰的对象。在示例中,Border类扮演该角色
ConcreteDecorator(具体的装饰物) 该角色是具体的Decorator角色,示例中SideBorder类和FullBorder类扮演该角色