Decorator模式(装饰器模式)

装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其结构。这种类型的设计模式属于结构型模式,它是作为现有的类的一个包装。

介绍

  • 意图:动态地给一个对象添加一些额外的职责。就增加功能来说,装饰器模式相比生成子类更为灵活。
  • 主要解决:一般的,我们为了扩展一个类经常使用继承方式实现,由于继承为类引入静态特征,并且随着扩展功能的增多,子类会很膨胀。
  • 优点:装饰类和被装饰类可以独立发展,不会相互耦合,装饰模式是继承的一个替代模式,装饰模式可以动态扩展一个实现类的功能。
  • 缺点:
    1.多层装饰比较复杂。
    2.会导致程序中增加很多功能类似的很小的类
  • 使用场景:
    1、扩展一个类的功能。
    2、动态增加功能,动态撤销。

注意事项:可代替继承。

示例

Display.ts 用于显示字符串的抽象类
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 用于显示单行字符串的类
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 用于显示装饰边框的抽象类
import Display from "./Display";

export default abstract class Border extends Display {
    protected display: Display;//装饰物
    constructor(display: Display) {
        super();
        this.display = display;
    }
}
SideBorder.ts 用于显示左右边框的类
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 显示上下左右边框
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
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
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类扮演该角色
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 160,585评论 4 365
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,923评论 1 301
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 110,314评论 0 248
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,346评论 0 214
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,718评论 3 291
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,828评论 1 223
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 32,020评论 2 315
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,758评论 0 204
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,486评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,722评论 2 251
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,196评论 1 262
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,546评论 3 258
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,211评论 3 240
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,132评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,916评论 0 200
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,904评论 2 283
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,758评论 2 274

推荐阅读更多精彩内容