装饰模式(Decorator Pattern)也称为包装模式(Wrapper Pattern),结构型设计模式之一,其使用一种对客户端透明的方式来动态地扩展对象的功能,同时它也是继承关系的一种替代方案之一。
动态地给一个对象添加一些额外的职责。就增加功能来说,装饰模式相比生成子类更为灵活。
需要透明且动态地扩展类的功能时。
package com.guifa.patterndemo.decoratorpattern;
public abstract class Component {
/**
* 抽象的方法
*/
public abstract void operate();
}
package com.guifa.patterndemo.decoratorpattern;
public class ConcreteComponent extends Component {
@Override
public void operate() {
// 具体逻辑
}
}
package com.guifa.patterndemo.decoratorpattern;
public abstract class Decorator extends Component {
// 持有一个Component对象的引用
private Component component;
public Decorator(Component component) {
this.component = component;
}
@Override
public void operate() {
component.operate();
}
}
package com.guifa.patterndemo.decoratorpattern;
public class ConcreteDecoratorA extends Decorator {
public ConcreteDecoratorA(Component component) {
super(component);
}
@Override
public void operate() {
// 装饰方法A和B既可在父类方法前调用,也可在之后调用
operateA();
super.operate();
operateB();
}
/**
* 自定义的装饰方法A
*/
private void operateA() {
// 装饰方法逻辑
}
/**
* 自定义的装饰方法B
*/
private void operateB() {
// 装饰方法逻辑
}
}
package com.guifa.patterndemo.decoratorpattern;
public class Client {
public static void main(String[] args) {
// 构造被装饰的组件对象
Component component = new ConcreteComponent();
// 根据组件对象构造装饰对象A并调用,此时相当于给组件对象增加装饰者A的功能方法
Decorator decorator = new ConcreteDecoratorA(component);
decorator.operate();
}
}
角色介绍:
以人穿衣服为例,我没将人定义为一个抽象类,将其穿衣服的行为定义为一个抽象方法。
package com.guifa.patterndemo.decoratorpattern;
public abstract class Person {
/**
* Person下有一个穿着的抽象方法
*/
public abstract void dressed();
}
该类其实就是上面我们所提及的抽象组件类,也就是我们需要装饰的原始对象,那么具体装饰谁呢?我们需要一个具体的实现类。
package com.guifa.patterndemo.decoratorpattern;
public class Boy extends Person {
@Override
public void dressed() {
// Boy类下dressed方法的基本逻辑
System.out.println("穿了内衣内裤");
}
}
Boy类继承与Person类,该类仅对Person中的dressed方法作了具体的实现,而Boy类则是我没所要装饰的具体对象,现在需要一个装饰者来装饰我们的这个Boy对象,这里定义一个PersonCloth类来表示人所穿着的衣服。
package com.guifa.patterndemo.decoratorpattern;
public abstract class PersonCloth extends Person {
// 保持一个Person类的引用
protected Person person;
public PersonCloth(Person person) {
this.person = person;
}
@Override
public void dressed() {
// 调用Person类中的dressed方法
person.dressed();
}
}
在PersonCloth类中我们保持了一个对Person类的引用煤科院方便地调用具体被装饰对象中的方法,这也是为什么我们可以在不破坏原类层次结构的情况下为类增加一些功能,我没只需要在被装饰对象的相应方法前或后增加相应的功能逻辑即可。在装饰物只有一个的情况下,可不必声明一个抽象类作为装饰者抽象的提取,仅需定义一个普通的类表示装饰者即可,这里为了定义两种衣服类型,一个类ExpensiveCloth表示高档衣服。
package com.guifa.patterndemo.decoratorpattern;
public class ExpensiveCloth extends PersonCloth {
public ExpensiveCloth(Person person) {
super(person);
}
@Override
public void dressed() {
super.dressed();
dressShirt();
dressLeather();
dressJean();
}
/**
* 穿短袖
*/
private void dressShirt() {
System.out.println("穿件短袖");
}
/**
* 穿皮衣
*/
private void dressLeather() {
System.out.println("穿件皮衣");
}
/**
* 穿牛仔裤
*/
private void dressJean() {
System.out.println("穿件牛仔裤");
}
}
而另一个类CheapCloth表示便宜的衣服。
package com.guifa.patterndemo.decoratorpattern;
public class CheapCloth extends PersonCloth {
public CheapCloth(Person person) {
super(person);
}
@Override
public void dressed() {
super.dressed();
dressShorts();
}
/**
* 穿短裤
*/
private void dressShorts() {
System.out.println("穿件短裤");
}
}
这两个类本质上并没有区别,两者都是为原本Boy类中的dressed方法提供功能扩展,不过这种扩展并非是直接修改原有的方法逻辑或结构,更恰当地说,仅仅是在另一个类中将原有方法和新逻辑进行封装整合而己。最后我们来看看客户类中的调用。
package com.guifa.patterndemo.decoratorpattern;
public class Main {
public static void main(String[] args) {
// new一个Boy
Person person = new Boy();
// 穿上便宜的衣服
PersonCloth clothCheap = new CheapCloth(person);
clothCheap.dressed();
// 穿上比较高档的衣服
PersonCloth clothExpensive = new ExpensiveCloth(person);
clothExpensive.dressed();
}
}
输出结果:
穿了内衣内裤
穿件短裤
穿了内衣内裤
穿件短袖
穿件皮衣
穿件牛仔裤
Process finished with exit code 0
装饰模式和代理模式优点类似,容易将装饰模式看作代理,装饰模式是以对客户端透明的方式扩展对象的功能,是继承关系的一个替代方案;而代理模式则是给一个对象提供一个代理对象,并有代理对象来控制对原有对象的引用。装饰模式应该为所装饰的对象增强功能;代理模式对代理的对象施加控制,但不对对象本身的功能进行增强。