设计模式之装饰者模式
简介
装饰模式是在不使用继承和不改变原类文件的情况下,动态的扩展一个对象的功能。它是通过创建一个包装对象,也就是装饰来包裹真实的对象。
注意其中三点:
1,不改变原类文件。
2,不使用继承。
3,动态扩展。
优点:
-
装饰者模式与继承关系的目的都是要扩展对象的功能,但是装饰者可以提供比继承更多的灵活性。
-
通过使用不同的具体装饰类以及这些装饰类的排列组合,设计师可以创造出很多不同行为的组合。
组成角色:
- Component(抽象组件又叫被装饰对象的基类)
定义一个对象接口,可以给这些对象动态地添加职责。
- ConcreteComponent(具体组件又叫具体被装饰对象)
定义一个对象,可以给这个对象添加一些职责。
- Decorator(装饰者抽象类)
维持一个指向Component实例的引用,并定义一个与Component接口一致的接口。
- ConcreteDecorator(具体装饰者)
具体的装饰对象,给内部持有的具体被装饰对象,增加具体的职责。
具体例子:
如对一杯咖啡进行加料,有奶、糖、巧克力等,咖啡也分不同品种卡布奇诺、摩卡等
结构就看类图
Drink是最终成品,包含了装饰的类和被装饰类,再往下细分不同被装饰的类和细节装饰的类。
我们先分析出最终产品是顾客拿到的成品,成品类要有价格属性和描述加料的方法。
再往下分装饰类和被被装饰类,关键就是如何保证成品类的原有属性加上装饰类装饰。
装饰过程就是类似一个递归的过程。
在最开始的时候,对象是Drink子类Coffee的子类
Drink bm = new Mocha();
接着开始装饰后变为Drink子类Decoratord的子类
bm = new Milk(bm);
在milk类中可以用构造方法默认修饰,也可以调用其方法修饰。参数bm已经存于Decorator类的Drink属性中了,再次调用时
bm = new Milk(bm);
这个bm就不是最开始的Blue_Mountain类对象了,而是Decorator子类Milk类的对象了,由于装饰类Coffee和被装饰类Decorator都是Drink子类,所以参数没有问题。
在展示时
System.out.println(bm.getDes()+" "+bm.cost());
这个bm第一次调用getDes()方法是将本次装饰元素“牛奶”输出,并调用父类的Drink属性对象的getDes()方法,由于这个属性也是Milk类的对象所以就产生了类似递归的情况,由上文可知第二getDes()方法内的Drink属性不再是Milk对象了,而是最开始的Blue_Mountain类对象,在这个getDes()中不再调用getDes()方法于是就结束了递归。
getDes()方法
public String getDes(){ return super.getDes()+" "+super.getPrice()+"&&"+this.drink.getDes();}
结果
牛奶 4.0&&牛奶 4.0&&摩卡咖啡 15.0
牛奶 4.0(第一次getDes(),此时的this.drink对象是Milk类的)&&牛奶 4.0(第二次getDes(),此时的this.drink对象是Mocha类的)&&摩卡咖啡 15.0
下面附上完整代码:
public abstract class Drink {
public String des;
private double price = 0;
public Drink() {}
public String getDes() { return des;}
public void setDes(String des) {this.des = des;}
public double getPrice() {return price;}
public void setPrice(double price) { this.price = price;}
public double cost(){return price;}
}
public class Coffee extends Drink{
public double cost(){return super.getPrice();}
}
public class Decorator extends Drink {
private Drink drink;
public Decorator(Drink drink) {this.drink = drink;}
public double cost(){return super.getPrice()+this.drink.cost();}
public String getDes({
returnsuper.getDes()+""+super.getPrice()+"&&"+this.drink.getDes();
}
}
public class Milk extends Decorator {
public Milk(Drink drink) {
super(drink);
this.setDes("牛奶");
this.setPrice(4.0);
}
}
public class Mocha extends Coffee{
public Mocha() {
super.setDes("摩卡咖啡");
super.setPrice(7.0);
}
}