装饰器模式(Decorator Pattern允许向一个现有的对象添加新的功能,同时又不改变其结构。(好比一个房子,可以添加一些家具,养一个宠物,而房子的整体结构是不变的)
Design Patterns - Elements of Reusable Object-Oriented Software(即:设计模式 可复用面向对象软件基础)书中介绍:
意图:动态地给一个对象添加一些额外的职责。就增加功能来说,Decorator模式相比生成子类更为灵活。
别名:包装器Wrapper
优点:装饰类和被装饰类可以独立发展,不会相互耦合,装饰模式是继承的一个替代模式,装饰模式可以动态扩展一个实现类的功能。
缺点:多层装饰比较复杂。
使用场景: 1、扩展一个类的功能。 2、动态增加功能,动态撤销。
适用性:
- 在不影响其他对象的情况下,以动态,透明的方式给单个对象添加职责
- 处理可以撤销的职责
- 当不采用生成子类的方式进行扩充时。一种情况是,可能有大量独立的扩展为支持,每一种组合将产生大量的子类,使得子类数目呈爆炸性增长。另一种情况是因为定义类被隐藏,或类定义不能用于生成子类.
实现:
1.接口一致性:装饰对象的接口必须与他所装饰的Component(组件)的接口是一致的,即所有的ConcreteDecorator(具体装饰者)类必须有一个公共的父类
2. 省略抽象的Decorator类:当仅仅只需要添加一个职责时,没有必要定义一个抽象的Decorator类。通常只需要处理现存的类层次结构而不是设计一个新的系统,此时可以把Decorator向Component转发请求的职责合并到ConcreteDecorator中
3. 保持Component类的简单性:为了保证接口的一致性,组件和装饰必须有一个公共的Decorator父类。即它应集中于定义接口而不是存储数据,对数据表示的定义应延迟到子类中,否则Component类会变得过于复杂和庞大,因而难以大量使用。赋予Component太多的工能也使得,具体的子类有一些他们并不需要的工能的可能性大大增加
4. 改变对象外壳与改变对象内核:即将Decorator看做一个对象的外壳,它可以改变这个对象的行为。另一种方法是改变对象的内核。
1.Component 类充当抽象角色,不应该具体实现。
2、修饰类引用和继承 Component 类,具体扩展类重写父类方法
具体实现:
创建一个接口
public interface Box {
public void goods();
}
创建一个实现Box接口的抽象装饰类
public abstract class Decorator implements Box {
public Box box;
public Decorator(Box box) {
this.box = box;
}
@Override
public void goods() {
box.goods();
}
}
创建实现Box接口的实体类
public class January implements Box {
private List<String> goods = new ArrayList<>();
@Override
public void goods() {
goods.add("一本书");
System.out.println("=======一月盒子里只有一本书=========");
}
public List<String> getGoods() {
return goods;
}
public void setGoods(List<String> goods) {
this.goods = goods;
}
}
创建扩展Decorator类的实体装饰类
public class February extends Decorator{
public February(Box box) {
super(box);
}
public void buyMobilePhone(){
January january = (January) box;
january.getGoods().add("手机");
System.out.println("======二月买了一个手机放进盒子里======");
}
@Override
public void goods() {
//一月的笔记本还在
super.goods();
buyMobilePhone();
}
}
创建一个扩展February的实体装饰类
public class March extends February{
public March(Box box) {
super(box);
}
public void addLaptop(){
January january = (January) box;
january.getGoods().add("笔记本电脑");
System.out.println("======三月买了笔记本电脑放进盒子里======");
}
@Override
public void goods() {
//在二月的基础上加了笔记本电脑
super.goods();
addLaptop();
}
}
测试:
public class TestDecorator {
public static void main(String[] args) {
Box box = new January();
System.out.println("======一月的盒子里面======");
box.goods();
System.out.println("======一月的盒子里面======\n");
System.out.println("======二月的盒子里面======");
Box f = new February(box);
f.goods();
System.out.println("======二月的盒子里面======\n");
System.out.println("======三月的盒子里面======");
Box m = new March(box);
m.goods();
System.out.println("======三月的盒子里面======\n");
}
}
结果:
======一月的盒子里面======
=======一月盒子里只有一本书=========
======一月的盒子里面======
======二月的盒子里面======
=======一月盒子里只有一本书=========
======二月买了一个手机放进盒子里======
======二月的盒子里面======
======三月的盒子里面======
=======一月盒子里只有一本书=========
======二月买了一个手机放进盒子里======
======三月买了笔记本电脑放进盒子里======
======三月的盒子里面======