再谈装饰者模式
前言
装饰者模式的作用依旧很简单,与适配器模式较为相识,其作用也是扩展或者兼容原有的逻辑,之前的装饰者模式总结,总觉得似乎少了点什么,装饰者模式初探。这篇博客结合一个简单的生活实例来解释装饰者模式
实例
现有一个咖啡店,售卖各种咖啡。但是咖啡不简单是咖啡豆加水,有些顾客会要求加入糖和奶泡。
简单版本
似乎简单的继承就能满足上述的需求,下面贴出所有代码
1、coffee类
/**
* autor:liman
* comment:基础的咖啡类,白开水+咖啡豆,售价20
*/
public class Coffee {
public String getMaterial(){
return "咖啡豆,"+"白开水";
}
public int getPrice(){
return 20;
}
}
2、咖啡加糖——CoffeeWithSugar
/**
* autor:liman
* comment:
*/
public class CoffeeWithSugar extends Coffee{
@Override
public String getMaterial() {
return super.getMaterial()+",糖";
}
@Override
public int getPrice() {
return super.getPrice()+3;
}
}
3、咖啡加糖加奶泡——CoffeeWithSugarAndMilk
/**
* autor:liman
* comment:
*/
public class CoffeeWithSugarAndMilk extends CoffeeWithSugar {
@Override
public String getMaterial() {
return super.getMaterial()+",奶泡";
}
@Override
public int getPrice() {
return super.getPrice()+5;
}
}
三者是很简单的继承关系,测试类如下:
public class CoffeeWithOutDecorateTest {
public static void main(String[] args) {
Coffee coffee = new Coffee();
System.out.println(coffee.getMaterial()+":总售价:"+coffee.getPrice());
CoffeeWithSugar coffeeWithSugar = new CoffeeWithSugar();
System.out.println(coffeeWithSugar.getMaterial()+":总售价:"+ coffeeWithSugar.getPrice());
CoffeeWithSugarAndMilk coffeeWithSugerAndMilk = new CoffeeWithSugarAndMilk();
System.out.println(coffeeWithSugerAndMilk.getMaterial()+":总售价:"+coffeeWithSugerAndMilk.getPrice());
}
}
运行结果:
这样表面上看似乎是满足了需求,如果有客户需要加入双份糖,双份奶泡,我们又得增加新的类,新的类可以继承上述的任意一个扩展类,这样扩展似乎并不合理。
升级版本
1、这里我们将Coffee改成抽象类,毕竟Coffee会有很多变化,有拿铁,有卡布奇洛,而且有的客户需要加入糖和奶泡。不同的Coffee会有不同的表现,所以我们这里很有必要将其抽象化
/**
* autor:liman
* comment:
*/
public abstract class Coffee {
public abstract String getMaterial();
public abstract int getPrice();
}
2、但是Coffee的基础还是有的,至少咖啡豆和白开水是少不了的。所以可以有一个基本的Coffee实现
/**
* autor:liman
* comment:
*/
public class BaseCoffee extends Coffee {
@Override
public String getMaterial() {
return "咖啡豆,"+"白开水";
}
@Override
public int getPrice() {
return 20;
}
}
3、下面才是关键,如果需要加入糖和奶泡,我们这里可以使用装饰者模式,可以先声明一个装饰器,但是装饰器不管怎么装饰都是依旧是Coffee,所以装饰器还是要继承基础的Coffee类。同时为了扩展增强原有的实例需要维护一个Coffee的属性。
/**
* autor:liman
* comment:装饰者的顶层类
*/
public class Decorator extends Coffee{
private Coffee coffee;
public Decorator(Coffee coffee) {
this.coffee = coffee;
}
@Override
public String getMaterial() {
return this.coffee.getMaterial();
}
@Override
public int getPrice() {
return this.coffee.getPrice();
}
}
4、奶泡装饰器和糖类装饰器
奶泡装饰器
/**
* autor:liman
* comment: 奶泡装饰器
*/
public class MilkDecorator extends Decorator {
public MilkDecorator(Coffee coffee) {
super(coffee);
}
@Override
public String getMaterial() {
return super.getMaterial()+",奶泡";
}
@Override
public int getPrice() {
return super.getPrice()+10;
}
}
糖类装饰器
/**
* autor:liman
* comment:加入糖的装饰器
*/
public class SugarDecorator extends Decorator {
public SugarDecorator(Coffee coffee) {
super(coffee);
}
@Override
public String getMaterial() {
return super.getMaterial()+",糖";
}
@Override
public int getPrice() {
return super.getPrice()+5;
}
}
5、测试实例
/**
* autor:liman
* comment:
*/
public class CoffeeDecoratorTest {
public static void main(String[] args) {
//买一杯基础的咖啡
Coffee coffee = new BaseCoffee();
System.out.println("咖啡原料:"+coffee.getMaterial()+"。售价:"+coffee.getPrice());
//给咖啡只加奶泡
Coffee milkCoffee = new MilkDecorator(coffee);
System.out.println("咖啡原料:"+milkCoffee.getMaterial()+"。售价:"+milkCoffee.getPrice());
//给coffee同时加入奶泡和糖
Coffee milkAndSugarCoffee = new SugarDecorator(new MilkDecorator(coffee));
System.out.println("咖啡原料:"+milkAndSugarCoffee.getMaterial()+"。售价:"+milkAndSugarCoffee.getPrice());
}
}
这里加入奶泡和糖的时候,无需单独生成新的类,只需将现有的奶泡装饰器和糖装饰器组合加入即可,由于所有的装饰器都继承至Coffee类,所以构造函数传入的参数也可以直接传入已经存在的装饰实例。上述例子中所有类的关系图如下所示(图中去掉了CoffeeDecoratorTest)
总结
总体来看,升级版的实例似乎变成了横向扩展,而非一味的竖向扩展,相比适配器模式而言装饰者模式似乎又相似之处,都是扩展原有代码的逻辑。