装饰者模式是动态地将责任附加到对象上。若要扩展功能,装饰者提供了比继承更有弹性的替代方案。
假设咱们有一个需求,是给一家饮料店作一个计算各类饮料价格的功能。听起来很简单,咱们建立一个抽象父类Beverages,description用来描述饮料名字,price方法用来计算饮料的价格。ide
public abstract class Beverages { private String description; public String getDescription() { return description; } public abstract double price(); }
每种饮料咱们都建立一个子类去继承父类,赋值给description,而且重写price方法来设置本身的价格。看上去很完美,咱们运行一下看看结果。函数
public class Coffee extends Beverages { public Coffee(String description){ this.description = description; } @Override public String getDescription() { return description; } @Override public double price() { return 3.5; } } public static void main(String[] args){ Coffee coffee = new Coffee("咖啡"); System.out.println(coffee.getDescription()); System.out.println(coffee.price()); }
结果:
咖啡
3.5this
可是问题来了,饮料店里不单单只有一种饮料,还有可乐、七喜、奶茶其余各类各样的饮料,难道咱们要每一个都建立一个子类吗?好吧,就算你以为几十种饮料还不算多,那若是各类饮料直接再进行搭配呢?咖啡加牛奶、咖啡加巧克力、加糖、不加糖,由于各类配料的不一样价格和描述也不一样,个人天呀,难道要建立几百个类吗?这个时候咱们忽然想到了不是能够用继承来解决这个问题吗?那咱们就来试一下。设计
咱们改造一下Beverages类,把要加的配料用boolean值声明,若是须要添加配料就调用set方法设置为true,在price方法中计算的时候来判断哪一些配料添加了须要计算价格。code
public class Beverages { public String description; public boolean milk; public boolean sugar; public double milkPrice = 1.5; public double sugarPrice = 0.5; public boolean isMilk() { return milk; } public void setMilk(boolean milk) { this.milk = milk; } public boolean isSugar() { return sugar; } public void setSugar(boolean sugar) { this.sugar = sugar; } public String getDescription() { return description; } public double price(){ double basePrice = 0.0; if (isMilk()){ basePrice += milkPrice; } if (isSugar()){ basePrice += sugarPrice; } return basePrice; } }
而后咱们在子类计算价格的时候加上父类中计算好的配料的价格。对象
public class CoffeeWithMilk extends Beverages { public CoffeeWithMilk(String description){ this.description = description; } @Override public double price() { return 3.0 + super.price(); } }
咱们运行看一下,咖啡加牛奶加糖,结果没有问题。继承
CoffeeWithMilk coffeeWithMilk = new CoffeeWithMilk("咖啡加牛奶加糖"); coffeeWithMilk.setMilk(true); coffeeWithMilk.setSugar(true); System.out.println(coffeeWithMilk.getDescription()); System.out.println(coffeeWithMilk.price());
结果:
咖啡加牛奶加糖
5.0ip
这样一来咱们解决了新建无数个子类的问题,可是咱们发现单纯继承的作法仍是有太多弊端,好比说若是咱们想要新添加配料,咱们得修改在父类Beverages中添加新的调料字段,还要修改price方法,这严重违反了开发-关闭的设计原则,类应该对扩展开发,对修改关闭。并且有些饮料和配料是没办法搭配的,例如啤酒加糖,可是子类仍是会继承到这些配料,而且若是是要同一份配料要加双份又该怎么改呢?因此单纯继承的方法仍是不行。这时候咱们就要使用到装饰者模式。开发
首先咱们建立抽象父类Beverages,这个父类只是饮料的父类,不是配料的父类。咱们建立一个Cola类直接继承它。get
public abstract class Beverages { public String description; public String getDescription() { return description; } public abstract double price(); } public class Cola extends Beverages { public Cola(String description) { this.description = description; } @Override public double price() { return 2.0; } }
如今就缺配料的部分的代码了,咱们再建立一个配料的抽象类Seasonings,咱们让它继承Berverages类,而且声明了一个Berverages的引用和抽象的getDescription方法,这里咱们稍后再做解释。
public abstract class Seasonings extends Beverages{ public Beverages beverages; public abstract String getDescription(); }
接下来咱们看看配料的实现类怎么写。咱们让他继承了父类Seasonings,构造函数接收父类Beverages类型,其实也就是要被装饰的对象,也就是各类各样须要加配料的饮料。而后咱们重写了getDescription,咱们在传进来的那个Beverages对象的基础上添加名字,price方法也是同理。这样其实就在给传进来的对象外面作了一层装饰,也就是给饮料添加了配料。
public class Ice extends Seasonings { public Ice(Beverages beverages) { this.beverages = beverages; } @Override public String getDescription() { return beverages.getDescription() + "加冰"; } @Override public double price() { return beverages.price() + 0.2; } }
运行一下,看看结果。没有问题,能够搭配成功。
Cola cola = new Cola("可乐"); Beverages ice = new Ice(cola); System.out.println(ice.getDescription()); System.out.println(ice.price());
结果:
可乐加冰
2.2
咱们还想再添加别的配料,再建立一个新的配料。
public class Sugar extends Seasonings { public Sugar(Beverages beverages) { this.beverages = beverages; } @Override public String getDescription() { return beverages.getDescription() + "加糖"; } @Override public double price() { return 0.5 + beverages.price(); } }
咱们在原来搭配好的基础上再进行装饰,结果也是没有问题的。这个时候就要提到为何咱们的Seasonings类要继承Beverages类了,由于每一个配料的实现类里有一个Beverages类型的引用,这样咱们才能够对它的子类进行装饰,咱们让Seasonings也继承Beverages那它的子类也是Beverages类型,咱们就能够想装饰几层就装饰几层。
Beverages ice = new Ice(cola); System.out.println(ice.getDescription()); System.out.println(ice.price()); Beverages sugar = new Sugar(ice); System.out.println(sugar.getDescription()); System.out.println(sugar.price());
结果:
可乐加冰
2.2
可乐加冰加糖
2.7
在Java的类库中就有不少实际应用到了装饰模式,好比BufferedInputStream就能够用来装饰FileInputStream,提供更增强大的功能。
总结:装饰模式就是装饰者和被装饰者都具备相同的超类,装饰者拿到被装饰者的引用以后,在调用被装饰者的方法的同时再加上本身的新功能,从而实现了功能的增长,也不须要修改原来的代码。并且由于是相同的超类,因此能够装饰不少层。