装饰模式简单介绍
装饰模式也称为包装模式,结构型设计模式之一,其使用一种对客户端透明的方式来动态地扩展对象的功能,同时它也是继承关系的一种替代方案之一。在现实生活中你也可以看见很多装饰模式的例子,或者可以大胆地说装饰模式无处不在,就拿人来说,人需要各式各样的衣着,不管你穿着怎样,但是,对于个人的本身来说是不变的,充其量只是在外面披上一层遮羞物,这就是装饰模式,装饰物也行各不相同但是装饰的对象本质是不变的。
装饰模式的定义
动态地给一个对象添加一些额外的职责。就增加功能来说,装饰模式相比生产子类更为灵活。
装饰模式的使用场景
需要透明且动态地扩展类的功能时。
装饰模式的 UML 类图
角色介绍:
- Component:抽象组件,可以是一个接口或抽象类,其充当的就是被装饰的原始对象。
- ConcreteComponent:组件具体实现类,该类是 Component 类的基本实现,也是我们装饰的具体对象。
- Decorator:抽象装饰者,顾名思义,其承担的职责就是为了装饰我们的组件对象,其内部一定要有一个指向组件对象的引用。在大多数情况下,该类为抽象类,需要根据不同的装饰逻辑实现不同的具体子类。当然装饰逻辑单一,只有一个的情况下我们可以省略该类直接作为具体的装饰者。
- ConcreteDecoratorA、ConcreteDecoratorA:装饰者具体实现类,只是对抽象装饰者做出具体的实现。
根据类图得出的通用的装饰模式的通用模式代码如下:
/**
* 抽象组件类
*/
public abstract class Component {
/**
* 抽象的方法,这个随你做
* 同样地你也可以增加更多的抽象方法
*/
public abstract void operate();
}
/**
* 组件具体实现类
*/
public class ConcreteComponent extends Component {
@Override
public void operate() {
//具体逻辑,这个随你做
}
}
/**
* 抽象装饰者
*/
public class Decorator extends Component {
private Component component; //持有一个 Component 对象的引用
/**
* 必要的构造方法,需要一个 Component 类型的对象
* @param component
*/
public Decorator(Component component) {
this.component = component;
}
@Override
public void operate() {
component.operate();
}
}
/**
* 装饰者具体实现类
*/
public class ConcreteDecoratorA extends Decorator {
public ConcreteDecoratorA(Component component) {
super(component);
}
@Override
public void operate() {
operateA();
super.operate();
operateB();
}
/**
* 自定义装饰方法 A
*/
private void operateA() {
}
/**
* 自定义装饰方法 B
*/
private void operateB() {
}
}
注:ConcreteDecoratorB 和 ConcreteDecoratorA 本质上并无区别只是实现不同,因此 ConcreteDecoratorB 的代码这里不在给出。
/**
* 客户端代码
*/
public class Client {
public static void main() {
//构造被装饰的组件对象
Component component = new ConcreteComponent();
//根据组件对象构建构造装饰者对象A并调用,此时相当于给组件对象增加装饰者A的功能方法
Decorator decorator = new ConcreteDecoratorA(component);
decorator.operate();
}
}
装饰模式的简单实战
还是看前面提到的那个例子,人总是要穿衣服的,我们将人定义为一个抽象类,将其穿衣的行为定义为一个抽象方法:
/**
* 抽象组件类
*/
public abstract class Person {
/**
* Person 下有一个穿着的抽象方法
*/
public abstract void dressed();
}
该类其实就是上面我们所提及的抽象组件类,也就是我们需要装饰的原始对象,那么具体装饰谁呢?我们需要一个具体的实现类。
/**
* 组件具体实现类
*/
public class Boy extends Person {
@Override
public void dressed() {
//Boy 类下的 dressed 方法的基本逻辑
// 只穿了内衣内裤
}
}
Boy 类继承于 Person 类,该类仅对 Person 中的 dressed 方法做了具体的实现,而 Boy 类则是我们所要装饰的具体对象,现在需要一个装饰者来装饰我们的这个 Boy 对象,这里定义一个 PersonCloth 类来表示人所穿着的衣服。
public class PersonCloth extends Person {
protected Person person;
public PersonCloth(Person component) {
this.person = person;
}
@Override
public void dressed() {
person.dressed();
}
}
在 PersonCloth 类中我们保持了一个对 Person 类的引用,可以方便地调用具体被装饰对象中的方法,这也是为什么我们可以在不破坏原类层次结构的情况下为类增加一些功能,我们只需要在被装饰对象的相应方法前或后增加相应的功能逻辑即可。在装饰物只有一个的情况下,可不必声明一个抽象类作为装饰者抽象的提取,仅需定义一个普通的类表示装饰者即可,这里为了表明示例我们定义两种衣服类型,一个类 ExpensiveCloth 表示高档衣服:
public class ExpensiveCloth extends PersonCloth {
public ExpensiveCloth(Person component) {
super(component);
}
/**
* 穿短袖
*/
private void dressShirt() {
//穿件短袖
}
/**
* 串皮衣
*/
private void dressLeather() {
//穿件皮衣
}
/**
* 穿牛仔裤
*/
private void dressJean() {
//穿条牛仔裤
}
@Override
public void dressed() {
super.dressed();
dressShirt();
dressLeather();
dressJean();
}
}
逻辑依旧简单不再多说,而另一个类 CheapCloth 则表示便宜的衣服。
public class CheapCloth extends PersonCloth {
public CheapCloth(Person component) {
super(component);
}
/**
* 穿短裤
*/
private void dressShorts() {
//穿条短裤
}
@Override
public void dressed() {
super.dressed();
dressShorts();
}
}
这两个类本质上并没有区别,两者都是为原本 Boy 类中的 dressed 方法提供功能扩展,不过这种扩展并非是直接修改原有的方法逻辑或结构,而恰当的说,仅仅是在另一个类中将原有方法和新逻辑进行封装整合而已。最后我们来看看客户类中的调用:
public class Client {
public static void main() {
//首先我们要有一个 被装饰物 person 男孩
Person person = new Boy();
//然后为他穿上便宜的衣服
PersonCloth cheapCloth = new CheapCloth(person);
cheapCloth.dressed();
//或者给他穿上比较上档次的衣服
PersonCloth expensiveCloth = new ExpensiveCloth(person);
expensiveCloth.dressed();
}
}