一 引言
不论是学生,还是上班族,好多人压点起床出门总是常事,楼下小摊小店急匆匆的扒拉几个包子一杯豆浆就冲向了地铁或者公车,就这杯小小的豆浆,考究那可就大了,什么大豆豆浆,五谷豆浆,黑芝麻豆浆品种繁多,要是还想加点配料那可更是花样百出,喜爱甜的加点蜂蜜,喜爱吃枣的还能加几粒红枣,竟可能的满足你的须要
其实从这一个豆浆的例子就能看进去,豆浆(好几种,大豆、五谷等等)自身是一个现有的产品,而增加蜂蜜也好,红枣也好,都是属于在现有产品上减少新的性能或者丑化,这就是咱们明天要讲的装璜者模式
进一步说到技术档次上,有时候一些组件外围代码是固定的,然而想不扭转原有构造的根底上,进行肯定的动静扩大,也是用如此的形式
上面接着用一个豆浆例子的具体代码引入其写法,前面再对其实践进行论述
二 代码演示
既然是豆浆,那就属于饮品,首先定义一个形象层面的饮品类
/** * 形象饮品类,被豆浆等子类继承 */public abstract class Drink { private String name; //饮品名称 private float price = 0.0f; public String getName() { return name; } public void setName(String name) { this.name = name; } public float getPrice() { return price; } public void setPrice(float price) { this.price = price; } // 计算费用 public abstract float calcCost();}
既然有了形象的饮品,那么豆浆类就能够呈现了,别忘了实现父类中计算饮品费用的形象办法
/** * 饮品子类:豆浆类 */public class SoyaBeanMilk extends Drink { @Override public float calcCost() { return super.getPrice(); }}
而咱们有各种豆浆,例如黄豆豆浆,五谷豆浆,黑芝麻豆浆
/** * 豆浆子类:黄豆豆浆 */public class SoybeansSoyaBeanMilk extends SoyaBeanMilk { public SoybeansSoyaBeanMilk() { setName("黄豆豆浆"); setPrice(3.0f); }}
/** * 豆浆子类:五谷豆浆 */public class GrainSoyaBeanMilk extends SoyaBeanMilk { public GrainSoyaBeanMilk() { setName("五谷豆浆"); setPrice(5.0f); }}
/** * 豆浆子类:黑芝麻豆浆 */public class BlackSesameSoyaBeanMilk extends SoyaBeanMilk { public BlackSesameSoyaBeanMilk() { setName("黑芝麻豆浆"); setPrice(6.0f); }}
其实当初,咱们曾经能够拿到几种口味的豆浆了,然而为了满足需要,咱们还须要减少一些因人而异的配料,例如蜂蜜和红枣
首先,创立一个装璜类 Decorator ,其艰深意义就是咱们对于所有配料的一个总称,上面会有一些具体的配料子类来继承它
注:这个中央为了重写形容和费用计算的办法,所以要继承 Drink,留神分分明哪些调用的是父类的,哪些是本人的
/** * 装璜类 */public class Decorator extends Drink { private Drink drink; public Decorator(Drink drink) { this.drink = drink; } @Override public String getName() { return drink.getName() + " + " + super.getName() + "(单价" + getPrice() + ")"; } @Override public float calcCost() { return super.getPrice() + drink.calcCost(); }}
上面就是具体的配料子类了
/** * 配料子类:蜂蜜配料 */public class Honey extends Decorator{ public Honey(Drink drink) { super(drink); setName("蜂蜜"); setPrice(2.0f); }}
/** * 配料子类:红枣配料 */public class RedJujube extends Decorator{ public RedJujube(Drink drink) { super(drink); setName("红枣"); setPrice(3.0f); }}
测试一下
public class Test { public static void main(String[] args) { // 点1杯大豆豆浆 Drink drink = new SoybeansSoyaBeanMilk(); System.out.println("购买:" + drink.getName() + " --- 费用: " + drink.calcCost()); // 点1杯大豆豆浆 + 1份蜂蜜 drink = new Honey(drink); System.out.println("购买:" + drink.getName() + " --- 费用: " + drink.calcCost()); // 点1杯大豆豆浆 + 1份蜂蜜 + 1份红枣 drink = new RedJujube(drink); System.out.println("购买:" + drink.getName() + " --- 费用: " + drink.calcCost()); // 点1杯大豆豆浆 + 1份蜂蜜 + 2份红枣 drink = new RedJujube(drink); System.out.println("购买:" + drink.getName() + " --- 费用: " + drink.calcCost()); }}
运行后果:
购买:黄豆豆浆 --- 费用: 3.0
购买:黄豆豆浆 + 蜂蜜(单价2.0) --- 费用: 5.0
购买:黄豆豆浆 + 蜂蜜(单价2.0) + 红枣(单价3.0) --- 费用: 8.0
购买:黄豆豆浆 + 蜂蜜(单价2.0) + 红枣(单价3.0) + 红枣(单价3.0) --- 费用: 11.0
到这里,在根底豆浆品种下面,减少各种配料并且计算总价曾经能够实现了
这就是装璜者模式,上面咱们来讲讲其实践
三 装璜者模式实践
(一) 定义和了解
定义:装璜模式就是在不扭转现有对象构造的状况下,动静地给该对象减少一些职责
就像下面豆浆加配料的例子中,装璜模式就是为曾经存在的内容,增加一些更加丰盛的内容
还有例如当你的零碎须要新的性能的时候,向旧代码中增加一些新的代码,这些新的代码就装璜了原有类的外围职责或次要行为
这些新退出的内容,实质上自只是为了满足一些在特定状况下才会须要的状况,例如并不是所有人都想要加蜂蜜等这些配料,对于这种需要,装璜模式就是一种比拟好的解决方案
从下面代码中也能够看出,当你须要在原有内容(豆浆)的根底上增加装璜内容(配料),只须要把每个要装璜的内容放在一个独自的类中,而后包裹要装璜的对象,当须要执行增加配料这样一种非凡行为的时候,就能够有抉择的进行装璜,例如增加蜂蜜: drink = new Honey(drink);
就是用蜂蜜包裹了下面 new 进去的饮品 drink (这里代表豆浆)
而且,当你想要增加新品种的豆浆的时候,例如红豆豆浆,只有继承豆浆这个父类,而后就能够享受到增加所有配料的权力
(二) 优缺点
长处:
- 无效的把类的外围职责和装璜功能区离开了,而且能够去除相干类中反复的装璜逻辑
- 装璜是继承的无力补充,比继承灵便,动静扩大,即插即用
- 不同顺序排列组合装璜类能够实现不同成果
- 恪守开闭准则
毛病:
- 会减少许多子类,使得程序较简单
(三) 构造
- 形象构件(Component)角色:定义一个形象接口(类)能够给这些对象动静的增加指摘
- 具体构件(ConcreteComponent)角色:实现形象构件,即一个具体的构件
- 形象装璜(Decorator)角色:继承形象构件,并蕴含具体构件的实例,能够通过其子类扩大具体构件的性能,也就是下面配料那个抽象类
- 具体装璜(ConcreteDecorator)角色:实现形象装璜的相干办法,也就是具体的配料,例如蜂蜜,红枣