1.前言
现实生活中,当过圣诞节的时候(老外的节日),但是我们有些人崇洋媚外,也会把圣诞节当成一个节日来过,说到圣诞节,肯定有圣诞树了,圣诞树其实只是一颗树而已,只不过上面装饰了一些了铃铛、蝴蝶结、小玩偶、袜子、彩灯、礼物、纸花、树顶星、雪花片、糖果杖、金丝线/银丝线、圣诞圈(请原谅我百度搜索的),就变成了圣诞树,其实本质还是一颗普通的树,还有比较常见的就是房子装修、相片加相框等,都是装饰器模式。
2.概念
装饰器模式(decorator-Pattern)的定义:指在不改变现有对象结构的情况下,动态地给该对象增加一些职责(即增加其额外功能)的模式,它属于对象结构型模式。
3.作用
用来包装原有的类,并在保持真实对象的类结构不变的前提下,提供了额外的功能。
4.模式结构
装饰器模式主要包含以下角色:
1. 抽象组件(Component):定义一个抽象类,不应该具体实现。
2. 具体组件(ConcreteComponent):继承抽象组件
3. 抽象装饰(Decorator):继承抽象组件,并包含具体组件的实例,可以通过其子类扩展具体组件的功能。
4. 具体装饰者(ConcreteDecorator):实现抽象装饰的相关方法,并给具体组件对象添加额外的功能。
5.案例实现
场景:有一位奶茶店的老板,叫你帮他做个销售系统,店里有原味奶茶,丝袜奶茶,港式奶茶,里面可以加珍珠,椰果,红豆,并且每种奶茶都有小杯,中杯,大杯,价格都不一样,销售系统里面要有价格,奶茶的信息
分析:你可能会创建一个MikeTea类,现在奶茶有3种,原料也只是3种,如果要对每种类型状态,恐怕要创建好多类,要是奶茶的种类多了,或者原料的种类多了,那创建的类可能真的要爆炸,所以像这种情况我们可以用装饰者模式,只创建3原味,港式,丝袜奶茶三种类,然后用装饰器模式额外增加一些功能,比如计算总的价格(原料加上奶茶的价格)
先按需求画出类图
- 创建MikeTea Component类,它是个抽象类
//MikeTea.ts
//奶茶 抽象类
export abstract class MikeTea {
//奶茶的描述属性
public description: string;
public size: string;
constructor() {
this.description = " ";
}
//获取描述
public getDescription(): string {
return this.description + "(" + this.getSize() + ")";
}
public setSize(size: string) {
this.size = size;
}
public getSize(): string {
return this.size;
}
//抽象方法 售卖价格
abstract cost(): number;
}
- 创建原味,丝袜,港式奶茶 ConcreteComponent(具体组件)分别继承奶茶抽象类
//HongKongStyle.ts
import { MikeTea } from "./MikeTea"
//港式奶茶类
export class HongKongStyle extends MikeTea {
constructor() {
super();
this.description = "港式奶茶";
}
public cost(): number {
switch (this.size) {
case "小杯":
return 8;
case "中杯":
return 10;
case "大杯":
return 12;
//默认是中杯
default:
return 10;
}
}
}
//Pure.ts
import { MikeTea } from "./MikeTea"
//原味奶茶类
export class Pure extends MikeTea {
constructor() {
super();
this.description = "原味奶茶";
}
public cost(): number {
switch (this.size) {
case "小杯":
return 6;
case "中杯":
return 8;
case "大杯":
return 10;
//默认是中杯
default:
return 8;
}
}
}
//SilkStocking.ts
import { MikeTea } from "./MikeTea"
//丝袜奶茶类
export class SilkStocking extends MikeTea {
constructor() {
super();
this.description = "丝袜奶茶";
}
public cost(): number {
switch (this.size) {
case "小杯":
return 12;
case "中杯":
return 14;
case "大杯":
return 16;
//默认是中杯
default:
return 14;
}
}
}
- 创建抽象Decorator装饰器类
//Decorate.ts
import { MikeTea } from "./MikeTea"
//抽象装饰类
export abstract class Decorate extends MikeTea {
public mikeTea: MikeTea
public materials:string;
public materialsPirce:number;
public totalPrice:number
constructor(mikeTea: MikeTea) {
super();
this.mikeTea = mikeTea;
}
abstract cost(): number
abstract getDescription(): string;
}
- 创建各种原料ConcreteDecorator具体的装饰者
//NataDeCoco.ts
import { Decorate } from "./Decorate";
import { MikeTea } from "./MikeTea";
//椰果类
export class NataDeCoco extends Decorate {
constructor(mikeTea: MikeTea) {
super(mikeTea);
this.materials = "椰果";
this.materialsPirce = 2;
this.totalPrice = this.mikeTea.cost();
}
public getDescription(): string {
return this.materials + "+" + this.mikeTea.getDescription();
};
public cost(): number {
return this.totalPrice += this.materialsPirce;
}
}
//Pearl.ts
import { Decorate } from "./Decorate";
import { MikeTea } from "./MikeTea";
//珍珠类
export class Pearl extends Decorate {
constructor(mikeTea: MikeTea) {
super(mikeTea);
this.materials = "珍珠";
this.materialsPirce = 4;
this.totalPrice = this.mikeTea.cost();
}
public getDescription(): string {
return this.materials + "+" + this.mikeTea.getDescription();
};
public cost(): number {
return this.totalPrice += this.materialsPirce;
}
}
//RedBean.ts
import { Decorate } from "./Decorate";
import { MikeTea } from "./MikeTea";
//红豆类
export class RedBean extends Decorate {
constructor(mikeTea: MikeTea) {
super(mikeTea);
this.materials = "红豆";
this.materialsPirce = 3;
this.totalPrice = this.mikeTea.cost();
}
public getDescription(): string {
return this.materials + "+" + this.mikeTea.getDescription();
};
public cost(): number {
return this.totalPrice += this.materialsPirce;
}
}
- 实现具体调用
//来一杯小杯原味奶茶
let PureMikeTea = new Pure();
//设置成小杯
PureMikeTea.setSize("小杯");
//描述
console.log(PureMikeTea.getDescription());
//原味奶茶价格
console.log(PureMikeTea.cost());
//----------------------------------------------------------------------------
//来一杯红豆丝袜奶茶(默认不说就是中杯)
let silkStockingMikeTea = new SilkStocking();
//设置成中杯
silkStockingMikeTea.setSize("中杯");
//加红豆
let RedBeanDecorate = new RedBean(silkStockingMikeTea);
//描述
console.log(RedBeanDecorate.getDescription());
//红豆丝袜奶茶价格
console.log(RedBeanDecorate.cost());
//----------------------------------------------------------------------------
//来一大杯双倍珍珠的港式奶茶
let HongKongStyleMikeTea = new HongKongStyle();
//设置成中杯
HongKongStyleMikeTea.setSize("大杯");
//加珍珠
let pearlDecorate = new Pearl(HongKongStyleMikeTea);
//加珍珠
let doublePearlDecorate = new Pearl(pearlDecorate);
//获取描述
console.log(doublePearlDecorate.getDescription());
//双倍珍珠分量的港式奶茶价格
console.log(doublePearlDecorate.cost());
//来一大杯珍珠+椰果的原味奶茶
//设置成大杯
PureMikeTea.setSize("大杯");
//加珍珠
let PurepearlDecorate = new Pearl(PureMikeTea);
//加椰果
let PurePearlNataDeCocoDecorate = new NataDeCoco(PurepearlDecorate);
//获取描述
console.log(PurePearlNataDeCocoDecorate.getDescription());
//大杯珍珠+椰果的原味奶茶奶茶价格
console.log(PurePearlNataDeCocoDecorate.cost());
- 看下图的执行结果:
总结优缺点
优点:
1. 装饰器是继承的补充,比继承灵活,在不改变原有对象的情况下,动态的给一个对象扩展功能,即插即用。
2.装饰类和被装饰类可以独立发展,不会相互耦合。
3. 通过使用不同装饰类及这些装饰类的排列组合,可以实现不同效果。
4. 装饰器模式完全遵守开闭原则。
缺点:
1. 装饰器模式会增加许多子类,过度使用会增加程序得复杂性。