1、 装饰者模式:动态地将责任附加到对象上。若要扩展功能,装饰者提供了比继承更加有弹性的替代方案。
2、组合和继承的区别
继承。继承是给一个类添加行为的比较有效的途径。通过使用继承,可以使得子类在拥有自身方法的同时,还可以拥有父类的方法。但是使用继承是静态的,在编译的时候就已经决定了子类的行为,我们不便于控制增加行为的方式和时机。
组合。组合即将一个对象嵌入到另一个对象中,由另一个对象来决定是否引用该对象来扩展自己的行为。这是一种动态的方式,我们可以在应用程序中动态的控制。
与继承相比,组合关系的优势就在于不会破坏类的封装性,且具有较好的松耦合性,可以使系统更加容易维护。但是它的缺点就在于要创建比继承更多的对象。
3、装饰者模式的优缺点
优点
1、装饰者模式可以提供比继承更多的灵活性
2、可以通过一种动态的方式来扩展一个对象的功能,在运行时选择不同的装饰器,从而实现不同的行为。
3、通过使用不同的具体装饰类以及这些装饰类的排列组合,可以创造出很多不同行为的组合。可以使用多个具体装饰类来装饰同一对象,得到功能更为强大的对象。
4、具体构件类与具体装饰类可以独立变化,用户可以根据需要增加新的具体构件类和具体装饰类,在使用时再对其进行组合,原有代码无须改变,符合“开闭原则”。
缺点
1、会产生很多的小对象,增加了系统的复杂性
2、这种比继承更加灵活机动的特性,也同时意味着装饰模式比继承更加易于出错,排错也很困难,对于多次装饰的对象,调试时寻找错误可能需要逐级排查,较为烦琐。
4、装饰者的使用场景
1、在不影响其他对象的情况下,以动态、透明的方式给单个对象添加职责。
2、需要动态地给一个对象增加功能,这些功能也可以动态地被撤销。 当不能采用继承的方式对系统进行扩充或者采用继承不利于系统扩展和维护时。
6、示例:
公司门口有一个小摊卖手抓饼和肉夹馍的,有时候中午不想吃饭就会去光顾一下那个小摊,点了手抓饼之后往往还可以在这个基础之上增加一些配料,例如煎蛋,火腿片等等,每个配料的价格都不一样,不管你怎么配配料,最终价格是手抓饼基础价加上每一种所选配料价格的总和。小摊的价格单如下:
如何使用一种设计模式来处理价格计算的问题呢,或许我们可以试试装饰者模式,因为在这里,主体是手抓饼和肉夹馍,而配料则是装饰者,我先用UML类图来描述一下类之间的协作关系。
public abstract class Pancake {
public String desc = "我不是一个具体的煎饼";
public String getDesc() {
return desc;
}
public abstract double price();
}
public class TornCake extends Pancake {
public TornCake() {
desc = "手抓饼";
}
@Override
public double price() {
return 4;
}
}
public class Roujiamo extends Pancake {
public Roujiamo() {
desc = "肉夹馍";
}
@Override
public double price() {
return 6;
}
}
public abstract class Condiment extends Pancake {
public abstract String getDesc();
}
public class FiredEgg extends Condiment {
private Pancake pancake;
public FiredEgg(Pancake pancake) {
this.pancake = pancake;
}
@Override
public String getDesc() {
return pancake.getDesc() + ", 煎蛋";
}
@Override
public double price() {
return pancake.price() + 2;
}
}
public class Ham extends Condiment {
private Pancake pancake;
public Ham(Pancake pancake) {
this.pancake = pancake;
}
@Override
public String getDesc() {
return pancake.getDesc() + ", 火腿片";
}
@Override
public double price() {
return pancake.price() + 1.5;
}
}
MeatFloss类和Cucumber类跟FiredEgg很相似,我就不一一列出来了,最后看看测试类:
public class MyTest {
@Test
public void test() {
Pancake tornCake = new TornCake();
//手抓饼基础价
System.out.println(String.format("%s ¥%s", tornCake.getDesc(), tornCake.price()));
Pancake roujiamo = new Roujiamo();
roujiamo = new FiredEgg(roujiamo);
roujiamo = new Ham(roujiamo);
roujiamo = new MeatFloss(roujiamo);
roujiamo = new Cucumber(roujiamo);
//我好饿
System.out.println(String.format("%s ¥%s", roujiamo.getDesc(), roujiamo.price()));
}
}
在Java源码中典型的装饰者模式就是java I/O, 其实装饰者模式也有其缺点,就是产生了太多的装饰者小类,有类爆炸的趋势。