我们都知道,可以使用两种方式给一个类或者对象添加行为:
一是使用继承。继承是给一个类添加行为的比较有效的途径。通过使用继承,可以使得子类在拥有自身方法的同时,还可以拥有父类的方法。但是使用继承是静态的,在编译的时候就已经决定了子类的行为,我们不便于控制增加行为的方式和时机。
二是使用关联。组合即将一个对象嵌入到另一个对象中,由另一个对象来决定是否引用该对象来扩展自己的行为。这是一种动态的方式,我们可以在应用程序中动态的控制。
与继承相比,关联关系的优势就在于不会破坏类的封装性,且具有较好的松耦合性,可以使系统更加容易维护。但是它的缺点就在于要创建比继承更多的对象。
定义
动态地将责任附加到对象上。若要扩展功能,装饰者提供了比继承更有弹性的替代方案。
要点
装饰者与被装饰者拥有共同的超类,继承的目的是继承类型,而不是行为,装饰者包含一个超类的对象,这样,可以在被装饰者行为前或者行为后加上新的行为,甚至取代原有的行为,装饰者会使程序中出现很多小类,增加使用难度。
使用场景
对象由主体+许多可选的部件或者功能构成,使用继承或者接口会产生很多类,且很难扩展。具体表现在:
1、在不影响其他对象的情况下,以动态、透明的方式给单个对象添加职责。
2、需要动态地给一个对象增加功能,这些功能也可以动态地被撤销。 当不能采用继承的方式对系统进行扩充或者采用继承不利于系统扩展和维护时。
装饰者模式UML结构图
Component: 抽象构件。是定义一个对象接口,可以给这些对象动态地添加职责。
ConcreteComponent:具体构件。是定义了一个具体的对象,也可以给这个对象添加一些职责。
Decorator: 抽象装饰类。是装饰抽象类,继承了Component,从外类来扩展Component类的功能,但对于Component来说,是无需知道Decorator存在的。
ConcreteDecorator:具体装饰类,起到给Component添加职责的功能。
示例
现在需要一个汉堡,主体是鸡腿堡,可以选择添加生菜、酱、辣椒等等许多其他的配料。
/**
*@Description抽象构件:汉堡基类
*@Authormoudaen
*@Since 2013年12月24日
*/
publicabstractclass Humburger {
protected String name ="汉堡";
public String getName(){
return name;
}
public abstractdouble cost();
}
/**
*@Description具体组件:鸡腿堡类
*@Authormoudaen
*@Since 2013年12月24日
*/
publicclass ChickenBurgerextends Humburger {
public ChickenBurger() {
name ="鸡腿堡";
}
@Override
public double cost() {
return 8;
}
}
/**
*@Description抽象装饰类:配料的基类
*@Authormoudaen
*@Since 2013年12月24日
*/
publicabstractclass Condimentextends Humburger {
public abstract String getName();
}
/**
*@Description具体装饰类:辣椒
*@Authormoudaen
*@Since 2013年12月24日
*/
publicclass Chilliextends Condiment {
private Humburger humburger;
public Chilli(Humburger humburger) {
// TODOAuto-generated constructor stub
this.humburger = humburger;
}
@Override
public String getName() {
return humburger.getName()+",辣椒";
}
@Override
public double cost() {
return humburger.cost();//你辣椒免费我也免费
}
}
/**
*@Description具体装饰类:生菜
*@Authormoudaen
*@Since 2013年12月24日
*/
publicclass Lettuceextends Condiment {
private Humburger humburger;
public Lettuce(Humburger humburger) {
this.humburger = humburger;
}
@Override
public String getName() {
// TODOAuto-generated method stub
return humburger.getName()+",生菜";
}
@Override
public double cost() {
// TODOAuto-generated method stub
return humburger.cost()+1.0;
}
}
/**
*@Description具体装饰类:辣椒酱
*@Authormoudaen
*@Since 2013年12月24日
*/
publicclass ChilliSauceextends Condiment{
private Humburger humburger;
public ChilliSauce(Humburger humburger) {
this.humburger = humburger;
}
@Override
public String getName() {
return humburger.getName()+",辣椒酱";
}
@Override
public double cost() {
return humburger.cost()+1.5;
}
}
/**
*@Description测试类
*@Authormoudaen
*@Since 2013年12月24日
*/
publicclass Test {
public staticvoid main(String[] args) {
Humburger humburger =new ChickenBurger();
System.out.println(humburger.getName() +"价格:" +humburger.cost());
Lettuce lettuce =new Lettuce(humburger);
System.out.println(lettuce.getName() +"价格:" +lettuce.cost());
Chilli chilli =new Chilli(humburger);
System.out.println(chilli.getName() +"价格:" +chilli.cost());
chilli =new Chilli(lettuce);
System.out.println(chilli.getName() +"价格:" +chilli.cost());
chilli =new Chilli(new ChilliSauce(humburger));
System.out.println(chilli.getName() +"价格:" +chilli.cost());
}
}
输出结果:
鸡腿堡价格:8.0
鸡腿堡,生菜价格:9.0
鸡腿堡,辣椒价格:8.0
鸡腿堡,生菜,辣椒价格:9.0
鸡腿堡,辣椒酱,辣椒价格:9.5
装饰者模式在jdk中的应用
1.java i/o
2.集合类也使用了这种设计模式,我们可以通过这种构造直接将一种Collection类对象包装成另一种
3.java.util.Collections#synchronizedList(List)
装饰者模式优缺点
优点
1、装饰者模式可以提供比继承更多的灵活性
2、可以通过一种动态的方式来扩展一个对象的功能,在运行时选择不同的装饰器,从而实现不同的行为。
3、通过使用不同的具体装饰类以及这些装饰类的排列组合,可以创造出很多不同行为的组合。可以使用多个具体装饰类来装饰同一对象,得到功能更为强大的对象。
4、具体构件类与具体装饰类可以独立变化,用户可以根据需要增加新的具体构件类和具体装饰类,在使用时再对其进行组合,原有代码无须改变,符合“开闭原则”。
缺点
1、会产生很多的小对象,增加了系统的复杂性
2、这种比继承更加灵活机动的特性,也同时意味着装饰模式比继承更加易于出错,排错也很困难,对于多次装饰的对象,调试时寻找错误可能需要逐级排查,较为烦琐。