定义:装饰模式是在不必改变原类文件和使用继承的情况下,动态的扩展一个对象的功能。它是通过创建一个包装对象,也就是装饰来包裹真实的对象。
1,不改变原类文件。
2,不使用继承。
3,动态扩展。
装饰者模式中的4个角色
(1)被装饰者抽象Component:是一个接口或者抽象类,定义最核心的对象,这个类是装饰者的基类,例如Tea接口(2)被装饰者具体实现ConcreteComponent:这是Component接口或抽象类的实现,例如本例中的SuperTea
(3)装饰者Decorator:一般是抽象类,实现Component,它里面必然有一个指向Component的引用,例如本例中AbstractTea
(4)装饰者实现ConcreteDecorator1和ConcreteDecorator2:用来装饰最基本的类,如本例中的RedTea
example
被装饰抽象:
/**
* Description:茶的基类(组件)
* User: lc
* Date: 2017/9/29 0029
* Time: 下午 1:28
*/
public interface Tea {
/**
* 成分
*/
String hasMaterial();
/**
* 价格
*
* @return
*/
BigDecimal cost();
}
/**
* Description:茶的鼻祖实现(主实现)
* User: lc
* Date: 2017/9/30 0030
* Time: 下午 1:07
*/
public class SuperTea implements Tea {
@Override
public String hasMaterial() {
return "白开水";
}
@Override
public BigDecimal cost() {
return BigDecimal.ONE;
}
}
装饰者:
/**
* Description:抽象组件
* User: lc
* Date: 2017/9/30 0030
* Time: 下午 1:10
*/
public abstract class AbstractTea implements Tea {
private Tea tea;
public AbstractTea(Tea tea) {
this.tea = tea;
}
public String hasMaterial() {
return this.tea.hasMaterial();
}
;
public BigDecimal cost() {
return this.tea.cost();
}
}
/**
* Description:红茶
* User: lc
* Date: 2017/9/30 0030
* Time: 下午 1:14
*/
public class ReadTea extends AbstractTea {
public ReadTea(Tea tea) {
super(tea);
}
/**
* 返回红茶组成成分
*
* @return
*/
@Override
public String hasMaterial() {
return "red茶叶、" + super.hasMaterial();
}
/**
* 返回红茶的价格
*
* @return
*/
@Override
public BigDecimal cost() {
return new BigDecimal(3.0).add(super.cost());
}
}
装饰者实现:
/** * Description:绿茶 * User: lc * Date: 2017/9/30 0030 * Time: 下午 1:28 */ public class GreenTea extends AbstractTea { public GreenTea(Tea tea) { super(tea); } /** * 返回绿茶组成成分 * * @return */ @Override public String hasMaterial() { return "green茶叶、" + super.hasMaterial(); } /** * 返回绿茶的价格 * * @return */ @Override public BigDecimal cost() { return new BigDecimal(4.0).add(super.cost()); } }测试:
/**
* Description:
* User: lc
* Date: 2017/9/30 0030
* Time: 下午 1:30
*/
public class Test {
public static void main(String[] args) {
System.out.println("客官:老板来一杯茶。。。。。。");
Tea superTea=new SuperTea();
System.out.println("老板:好嘞,客官。茶成分:"+superTea.hasMaterial()+"--价格:"+superTea.cost()+"元");
System.out.println("客官:老板茶没味道。来杯红茶");
AbstractTea abstractTea=null;
abstractTea=new ReadTea(superTea);
System.out.println("老板:好嘞,客官。红茶成分:"+abstractTea.hasMaterial()+"--价格:"+abstractTea.cost()+"元");
System.out.println("客官:老板有么有别的茶?");
System.out.println("老板:还有绿茶?");
abstractTea=new GreenTea(superTea);
System.out.println("老板:好嘞,客官。绿茶成分:"+abstractTea.hasMaterial()+"--价格:"+abstractTea.cost()+"元");
System.out.println("客官:老板有没有绿红茶?");
System.out.println("老板:你确定要?");
abstractTea=new ReadTea(abstractTea);
System.out.println("老板:好嘞,客官。绿红茶成分:"+abstractTea.hasMaterial()+"--价格:"+abstractTea.cost()+"元");
}
}
客官:老板来一杯茶。。。。。。
老板:好嘞,客官。茶成分:白开水--价格:1元
客官:老板茶没味道。来杯红茶
老板:好嘞,客官。红茶成分:red茶叶、白开水--价格:4元
客官:老板有么有别的茶?
老板:还有绿茶?
老板:好嘞,客官。绿茶成分:green茶叶、白开水--价格:5元
客官:老板有没有绿红茶?
老板:你确定要?
老板:好嘞,客官。绿红茶成分:red茶叶、green茶叶、白开水--价格:8元
总结:
聪明的你,可能已经发现 就2个装饰者实现类,但是老板可以制定出3种茶。就我自己的理解 装饰者模式:就是修饰某个组件。比如本文章的例子Tea/superTea 是主题,而abstractTea以及具体的实现都是,封装了一些的装饰物品。比如随着老板生意越做越好,可能会添加很多种茶,甚至在原有的茶基础上添加一些配料。比如说红茶 (以后可能会出大杯、中杯、小杯等)。
结论:
1.装饰者类要实现真实类同样的接口(abstractTea实现Tea)
2.装饰者类内有一个真实对象的引用(可以通过装饰者类的构造器传入)(abstractTea引用了Tea)
3.装饰类对象在主类中接受请求,将请求发送给真实的对象(相当于已经将引用传递到了装饰类的真实对象)
4.装饰者可以在传入真实对象后,增加一些附加功能(因为装饰对象和真实对象都有同样的方法,装饰对象可以添加一定操作在调用真实对象的方法,或者先调用真实对象的方法,再
添加自己的方法)
5.不用继承,
优点:
复用现有的代码(红绿茶就复用了绿茶以及红茶的代码),增强原有的组件的功能
缺点:
1.衍生出一些列的小对象,导致错中复杂
现成例子:JDK中的装饰者模式
java.io中很多使用了装饰者模式
举个例子:FilterInputStream继承(实现)了InputStream,同时,BufferedInputStream继承了FilterInputStream,
1,被装饰者抽象组件:即最顶层的基类InputStream
2.被装饰者具体实现ConcreteComponent:FileInputStream和FileOutputStream就是它的实现
3.装饰者Decorator:FilterInputStream中有一个InputStream的实例和构造方法传入InputStream对象
protected volatile InputStream in;
protected FilterInputStream(InputStream in) {
this.in = in;
}
4.装饰者实现:在 BufferedInputStream 中有构造方法传入InputStream对象,实现了装饰
public BufferedInputStream(InputStream in, int size) {
super(in);
if (size <= 0) {
throw new IllegalArgumentException("Buffer size <= 0");
}
buf = new byte[size];
}