乐趣区

关于jquery:装饰者模式动态的包装原有对象的行为

明天来介绍 装璜者模式(_Decorator Design Pattern_)。

假如咱们须要给一家 火锅店 设计一套 结账零碎 ,也就是统计顾客生产的 总价格。怎样才能设计出一个好的零碎呢?

1,结账零碎需要剖析

既然要设计一个结账零碎,当然须要晓得火锅店都有哪些食品及食品的价格,如果咱们从火锅店老板那里失去以下食品清单:

  • 锅底类:

    • 清汤锅底:5 元
    • 麻辣锅底:7 元
    • 其它
  • 配菜类:

    • 青菜:3 元
    • 羊肉:6 元
    • 其它
  • 饮料类:

    • 可乐:2 元
    • 其它

能够看到,食品共有三大类,别离是:锅底类,配菜类和饮料类,每个大类下边都有很多具体的食品。

为了设计出一个可保护,可扩大,有弹性的零碎,应该怎么设计呢?

咱们能够这样对待食品之间的关系,将锅底类看作 主品 ,所有其它的都为 副品 ,也就是 附加 在主品之上的食品。

副品以主品为核心,围绕在主品四周,包裹着主品,一层层的往外扩大

如下图所表白的一样:

2,装璜者模式

像这种,须要在原来(主品)的根底上,附加其它的货色(副品),这样的业务场景都能够应用 装璜者模式

装璜者模式 的定义为:动静的给一个对象增加其它性能。从扩展性来说,这种形式比继承更有弹性,更加灵便,可作为代替继承的计划

装璜者模式的 长处 在于,它可能更灵便的,动静 的给对象 增加其它性能 ,而不须要批改任何现有的底层代码。也就是不须要通过 批改 代码,而是通过 扩大 代码,来实现新的业务需要。

这就十分合乎咱们所说的设计准则中的 开闭准则 对扩大凋谢,对批改敞开。也就是尽量不要批改原有代码,而是通过扩大代码来实现工作。这样做的益处是能够缩小对原有零碎的批改,从而缩小引入 bug 的危险。

装璜者模式的类图如下:

ConcreteComponent 为被装璜者,Decorator 是所有装璜者的超类。

装璜者和被装璜者有着独特的超类型,这一点很重要,因为装璜者必须可能取代被装璜者。这样,装璜者能力在被装璜者的根底上,加上本人的行为,以 加强 被装璜者的能力。

一个被装璜者能够被多个装璜者顺次包装,这个包装行为是动静的,不限次数的。

3,实现结账零碎

那么依据装璜者模式的类图,咱们能够设计出火锅店结账零碎的类图,如下:

火锅的锅底作为被装璜者,配菜和饮料作为装璜者。

每个类都有两个办法:

  • describe:返回以后火锅的形容。
  • cost:返回以后火锅的价格。

首先编写 HotPot 类:

class HotPot {
    protected String desc = "HotPot";
    protected double price = 0;

    public String description() {return desc;}

    public double cost() {return price;}
 
    public void printMenu() {System.out.println("菜单:" + description() + "生产总价:" + cost());
    }
} 

HotPot 类中有两个属性 descprice,还有三个办法 descriptioncostprintMenuprintMenu 用于输入菜单和生产总价。

再编写 SideDish 类:

class SideDish extends HotPot {
    protected HotPot hotpot;
    
    public double cost() {return hotpot.cost() + price;
    };

    public String description() {return hotpot.description() +"+"+ desc;
    };
} 

SideDish 继承了 HotPot,增加了本人的属性 hotpot,并且重写了两个办法 descriptioncost

留神 SideDish 类对两个办法 descriptioncost 进行了重写,这十分重要,这体现出了装璜者与被装璜者的区别,装璜者能在被装璜者的根底上附加本人的行为, 起因就在这里

编写两个锅底类:

class SoupPot extends HotPot {public SoupPot() {
        desc = "Soup";
        price = 5;
    }
}

class SpicyPot extends HotPot {public SpicyPot() {
        desc = "Spicy";
        price = 7;
    }
} 

这两个类都继承HotPot,并别离在构造方法中设置本人的 descprice

再编写三个配菜类:

class VegetablesDish extends SideDish {public VegetablesDish(HotPot hotpot) {
        this.hotpot = hotpot;
        desc = "Vegetables";
        price = 3;
    }
}

class MuttonDish extends SideDish {public MuttonDish(HotPot hotpot) {
        this.hotpot = hotpot;
        desc = "Mutton";
        price = 6;
    }
}

class ColaDish extends SideDish {public ColaDish(HotPot hotpot) {
        this.hotpot = hotpot;
        desc = "Cola";
        price = 2;
    }
} 

这三个类都继承 SideDish,并别离在构造方法中设置本人的hotpotdescprice

4,测试结账零碎

用如下代码来测试:

// 只有一份清汤锅底
HotPot hotpot = new SoupPot(); // 被装璜者不需装璜者包装也能够应用
hotpot.printMenu();

// 清汤锅底 + 蔬菜
hotpot = new VegetablesDish(hotpot);
hotpot.printMenu();

// 清汤锅底 + 蔬菜 + 羊肉
hotpot = new MuttonDish(hotpot);
hotpot.printMenu();

// 清汤锅底 + 蔬菜 + 羊肉 + 可乐
hotpot = new ColaDish(hotpot);
hotpot.printMenu();

// 清汤锅底 + 蔬菜 + 羊肉 + 可乐 + 蔬菜
hotpot = new VegetablesDish(hotpot);
hotpot.printMenu(); 

输入如下:

菜单:Soup 生产总价:5.0
菜单:Soup + Vegetables 生产总价:8.0
菜单:Soup + Vegetables + Mutton 生产总价:14.0
菜单:Soup + Vegetables + Mutton + Cola 生产总价:16.0
菜单:Soup + Vegetables + Mutton + Cola + Vegetables 生产总价:19.0 

计算总价时,会从最外层的装璜者,朝着被装璜者的方向,顺次调用每一层的 cost 办法,直到被装璜者为止。

而后再朝着最外层装璜者的方向,顺次计算出每一层的价格,最初得出的价格就是生产总价。

计算过程如下图所示:

我将残缺的 装璜者模式 代码放在了这里,供大家参考。

5,装璜者模式的应用场景

装璜者模式次要用于,在不批改原有类的前提下,动静的批改原有类的性能。

Java JDK 中大量应用了装璜者模式,尤其是 Java I/O 框架。

Java IO 框架的继承关系如下:

退出移动版