设计模式23---设计模式之装饰模式(Decorator)(结构型)

时间:2023-03-09 20:39:22
设计模式23---设计模式之装饰模式(Decorator)(结构型)

1.装饰模式讲解

1.1定义

动态的给一个对象添加一些额外的职责。就增加功能来说,装饰模式比生成子类更加灵活。

1.2装饰模式要点

透明的给一个对象增加功能,换句话说就是要给一个对象增加功能,但是不能让这个对象知道,也就是不能去改动这个对象。

面向对象设计的一个基本规则就是:尽量使用对象组合,而不是使用对象继承。

装饰者和被装饰对象有相同的超类型。

可以用一个或多个装饰者包装一个对象。

装饰者可以在所委托被装饰者的行为之前或之后,加上自己的行为,以达到特定的目的。

对象可以在任何时候被装饰,所以可以在运行时动态的,不限量的用你喜欢的装饰者来装饰对象。 

装饰模式中使用继承的关键是想达到装饰者和被装饰对象的类型匹配,而不是获得其行为。

装饰者一般对组件的客户是透明的,除非客户程序依赖于组件的具体类型。在实际项目中可以根据需要为装饰者添加新的行为,做到“半透明”装饰者。

适配器模式的用意是改变对象的接口而不一定改变对象的性能,而装饰模式的用意是保持接口并增加对象的职责。

1.3装饰模式的结构图和说明

设计模式23---设计模式之装饰模式(Decorator)(结构型)


抽象构件(Component)角色:给出一个抽象接口,以规范准备接收附加责任的对象。


具体构件(Concrete Component)角色:定义一个将要接收附加责任的类。


装饰(Decorator)角色:持有一个构件(Component)对象的实例,并定义一个与抽象构件接口一致的接口。


具体装饰(Concrete Decorator)角色:负责给构件对象添加上附加的责任。

1.4装饰模式示例代码

package demo21.decorator.example3;
/**
* 组件对象的接口,可以给这些对象动态的添加职责
*/
public abstract class Component {
/**
* 示例方法
*/
public abstract void operation();
}
***************************************************************************************************
package demo21.decorator.example3;
/**
* 具体实现组件对象接口的对象
*/
public class ConcreteComponent extends Component { public void operation() {
//相应的功能处理
}
}
**************************************************************************************************
package demo21.decorator.example3;
/**
* 装饰器接口,维持一个指向组件对象的接口对象,
* 并定义一个与组件接口一致的接口
*/
public abstract class Decorator extends Component {
/**
* 持有组件对象
*/
protected Component component;
/**
* 构造方法,传入组件对象
* @param component 组件对象
*/
public Decorator(Component component) {
this.component = component;
} public void operation() {
//转发请求给组件对象,可以在转发前后执行一些附加动作
component.operation();
}
}
**************************************************************************************************
package demo21.decorator.example3;
/**
* 装饰器的具体实现对象,向组件对象添加职责
*/
public class ConcreteDecoratorA extends Decorator {
public ConcreteDecoratorA(Component component) {
super(component);
}
/**
* 添加的状态
*/
private String addedState; public String getAddedState() {
return addedState;
} public void setAddedState(String addedState) {
this.addedState = addedState;
} public void operation() {
//调用父类的方法,可以在调用前后执行一些附加动作
//在这里进行处理的时候,可以使用添加的状态
super.operation();
}
}
**************************************************************************************************
package demo21.decorator.example3; /**
* 装饰器的具体实现对象,向组件对象添加职责
*/
public class ConcreteDecoratorB extends Decorator {
public ConcreteDecoratorB(Component component) {
super(component);
}
/**
* 需要添加的职责
*/
private void addedBehavior() {
//需要添加的职责实现
}
public void operation() {
//调用父类的方法,可以在调用前后执行一些附加动作
super.operation();
addedBehavior();
}
}

1.5装饰模式的本质

动态结合

1.6装饰模式的优缺点

优点:

比继承更加灵活

更容易复用功能

简化高层定义


缺点:

会产生很多细粒度对象

1.7何时选用

如果需要以动态透明的方式给对象添加职责,可以使用装饰模式

如果不适合使用子类来进行扩展的时候,可以考虑使用装饰模式

2.举例说明装饰模式

最最常见的例子就是java中的I/O数据流了,大家有时间可以看看他的继承实现结构图

假如你刚刚买了一辆汽车,是一个裸车,他不符合你的自己的个性化的要求,比如说外观不好看,轮胎不好,发动机马力不足等等。那么你就需要对外表进行装饰,换换颜色,把车里面增加一些坐垫。那么,这里就需要用到我们刚刚学过的装饰模式。


实例代码如下,代码注释的很清楚,我就不再赘述了

2.1对应Component

package demo21.decorator.example1;

/**
* 组件对象---车
*
* @author 王玉超
*
*/
public abstract class Car {
/**
* 行驶功能
*/
public abstract void run();
}

2.2对应ConcreteComponent

package demo21.decorator.example1;

/**
* 具体的车,假如是奥迪
*
* @author 王玉超
*
*/
public class AudiCar extends Car { @Override
public void run() {
System.out.println("奥迪行驶在马路上...");
} }

2.3对应Decorator

package demo21.decorator.example1;

/**
* 装饰器接口,维持一个指向组件对象的接口对象, 并定义一个与组件接口一致的接口,这是是装饰的接口
*/
public abstract class Decorator extends Car {
/**
* 持有组件对象
*/
private Car car; /**
* 构造方法,传入组件对象
*
* @param car
* 组件对象
*/
public Decorator(Car car) {
this.car = car;
} @Override
public void run() {
// 转发给component组件对象car
this.car.run();
}
}

2.4对应 ConcreteDecoratorA

package demo21.decorator.example1;

/**
* 装饰器的具体实现对象,向组件对象添加职责 ,这个是添加颜色的车
*/
public class AddColorCar extends Decorator {
public AddColorCar(Car car) {
super(car);
} /**
* 添加的状态,更改外观
*/
private String face; public void setFace(String face) {
this.face = face;
} public void addColor() {
System.out.println("我添加了颜色" + this.face);
} @Override
public void run() {
// 调用父类的方法,可以在调用前后执行一些附加动作
// 在这里进行处理的时候,可以使用添加的状态
super.run(); this.addColor();
}
}

2.5对应 ConcreteDecoratorB

package demo21.decorator.example1;

/**
* 装饰器的具体实现对象,向组件对象添加职责 ,这个是添加坐垫的车
*/
public class AddSeatCar extends Decorator {
public AddSeatCar(Car car) {
super(car);
} /**
* 添加的状态,添加坐垫
*/
private String seat; public void setSeat(String seat) {
this.seat = seat;
} public void addSeat() {
System.out.println("我添加了坐垫" + this.seat);
} @Override
public void run() {
// 调用父类的方法,可以在调用前后执行一些附加动作
// 在这里进行处理的时候,可以使用添加的状态
super.run(); this.addSeat();
}
}

2.6客户端使用

package demo21.decorator.example1;

public class Client {
public static void main(String[] args) {
// 被装饰的对象
Car car = new AudiCar();
// 装饰者对象
Decorator colorCar = new AddColorCar(car);
((AddColorCar) colorCar).setFace("红色"); Decorator seatCar = new AddSeatCar(colorCar);
((AddSeatCar) seatCar).setSeat("夏天清凉坐垫"); // 汽车跑起来吧
seatCar.run();
}
}

如何看完以后,还是不明白,我建议读者看看java I/O数据流里面的各种类,以及他们之间的关系,或许就明白了。