1.定义
动态地给一个对象添加一些额外的职责。就增加功能来说,装饰模式相比生成子类更灵活。
2.装饰模式的使用场景
- 需要扩展一个类的功能,或给一个类增加附加功能
- 需要动态地给一个对象增加功能,这些功能可以动态的撤销
- 需要为一批的兄弟类进行改装或者加装功能,当然是首选装饰模式
3.例子
我买了一辆车,这辆车会跑。
那么用代码抽象上面这句话就成了这样:
package _11DecoratorPattern;
public interface ICar {
public void run();
}
package _11DecoratorPattern;
public class MyCar implements ICar {
@Override
public void run() {
System.out.println("我的车跑到了七十码");
}
}
在我开车的时候,突然觉得要是我的车有放音乐的功能就好了。
那么我们去修改MyCar实现类吗?基于开闭原则,我想这不是一个好方案。
这让我想起来了或许参考以前的代理模式能实现这个功能:
package _11DecoratorPattern;
public class MyMusicCar implements ICar {
private ICar car;
public MyMusicCar(ICar car)
{
this.car = car;
}
@Override
public void run()
{
System.out.println("最炫民族风正在响起……");
car.run();
}
}
好了,上面这个代码就是装饰模式。
是不是很简单?可能你要有疑问了:这不是代理模式吗?
首先要说的是,装饰模式就是代理模式的一个特殊应用,两者的共同点是都具有相同的接口,不同点是代理模式注重于对代理过程的控制(比如上面的代码如果是代理模式实现会在run里面判断油量是否充足,如果不充足,它代理的车子将不能调用run方法),而装饰模式则是对类的功能进行加强或减弱,它着重于类的功能变化。
4.装饰模式的四个组件
- Component抽象构件:一个接口或者抽象类,就是定义我们最核心的对象,也就是原始的对象。
- ConcreteComponent具体构件:是最核心、最原始、最基本的接口或抽象类的实现,你要修饰的就是它。
- Decorator抽象装饰角色:一般是一个抽象类,在它的属性里必然有一个private变量指向Component抽象构件
- 具体装饰角色
5.装饰模式的通用代码
package _11DecoratorPattern;
// 抽象构件
public abstract class Component {
// 抽象的方法
public abstract void operation();
}
package _11DecoratorPattern;
// 具体构件
public class ConcreteComponent extends Component {
// 具体实现
@Override
public void operation() {
System.out.println("do some thing");
}
}
package _11DecoratorPattern;
// 抽象装饰类,如果只需要一个装饰,那么直接在operation方法中实现,不需要别的装饰类,否则,继续看下面的装饰类
public abstract class Decorator extends Component {
private Component component;
// 通过构造函数传递被修饰者
public Decorator(Component component)
{
this.component = component;
}
// 委托给被修饰者执行
@Override
public void operation() {
this.component.operation();
}
}
package _11DecoratorPattern;
public class ConcreteDecorator1 extends Decorator {
public ConcreteDecorator1(Component component) {
super(component);
}
// 定义自己的修饰方法
private void method2() {
System.out.println("method2 修饰");
}
// 重写父类的修饰方法
@Override
public void operation() {
super.operation();
this.method2();
}
}
package _11DecoratorPattern;
public class ConcreteDecorator2 extends Decorator {
public ConcreteDecorator2(Component component) {
super(component);
}
// 定义自己的修饰方法
private void method1() {
System.out.println("method1 修饰");
}
// 重写父类的修饰方法
@Override
public void operation() {
this.method1();
super.operation();
}
}
package _11DecoratorPattern;
// 场景类
public class Client {
public static void main(String[] args) {
Component component = new ConcreteComponent();
// 第一次被修饰
component = new ConcreteDecorator1(component);
// 第二次被修饰
component = new ConcreteDecorator2(component);
component.operation();
}
}
结果输出:
method1 修饰
do some thing
method2 修饰
do some thing
method2 修饰
6.装饰模式的优点
- 装饰类和被装饰类可以独立发展,而不会相互耦合。换句话说,Component类无需知道Decorator类,Decorator类是从外部扩展Component类的功能,而Decorator也不不用知道具体的构件。
- 装饰模式是继承关系的一个替代方案。我们看装饰类Decorator,不关装饰多少层,返回的对象还是Component,实现的还是is-a的关系。
- 装饰模式可以动态地扩展一个实现类的功能。
7.装饰模式的缺点
多层的装饰是比较复杂的。为什么会负责呢?你想想看,就像剥洋葱一样,你剥到了最后才发现是最里层的装饰出现了问题,想象一下工作了吧。因此,尽量减少装饰类的数量,以便降低系统的复杂度。