1. 装饰模式概述
装饰模式可在不改变对象本身功能的基础上给对象增加额外的新行为,现实生活中如墙壁贴壁画,增加美观;装饰模式是一种替代继承的技术,通过无须定义子类的方式给对象动态增加职责;在装饰模式中引入装饰类,在装饰类中既可以调用待装饰的原有类的方法,还可以增加新的方法,扩充原有类的功能;
1) 定义:动态地给一个对象增加一些额外的职责,在增加对象功能方面比生成子类的实现方式更灵活.
2) 结构图:
3)角色分析
1. Component(抽象构件):声明了在具体构件中实现的业务方法,使客户端以一致的方式处理未被装饰的对象以及装饰之后的对象,实现客户端透明的操作
2. ConcreteComponent(具体构件):抽象构件的子类,用于定义具体的构件对象,装饰器可以给它增加额外的方法
3. Decorator(抽象装饰类):用户给具体构件增加职责,但是具体职责在其子类中实现,维护一个指向抽象构件对象的引用,通过该引用可以调用装饰之前构件对象的方法 ,并通过其子类扩展该方法,达到装饰的目的
4. ConcreteDecorator(具体装饰类):它是抽象装饰类的子类,每一个具体装饰类定义了一些新的行为,可以调用抽象装饰类的方法,并可以以增加新的方法扩充对象的行为.
4) 核心代码
class Decorator implements Component {
private Component component; //维持一个对抽象构件对象的引用
//注入一个抽象构件类型的对象
public Decorator(Component component) {
this.component = component;
}
public void operation(){
component.operation(); //调用原有业务方法
}
}
class ConcreteDecorator extends Decorator{
public ConcreteDecorator(Component component) {
super.operation(); //调用原有业务的方法
addedBehavior(); //调用新增业务方法
}
public void addedBehavior(){...}
}
由于在抽象装饰类Decorator中注入的是Component类型的对象,因此可以将一个具体构件对象注入其中,通过具体装饰类进行装饰;还可以将一个已经装饰过的Decorator子类的对象再注入其中进行多次装饰,从而对原有功能进行多次扩展.
2. 案例
1) 需求
2) 解决方案
3) 代码实现
package com.zach.decorator;
//抽象界面构件类:抽象构件类
public abstract class Component {
public abstract void display();
}
//窗体类:具体构件类
public class Window extends Component{
@Override
public void display() {
System.out.println("显示窗体");
}
}
//文本框类:具体构件类
public class TextBox extends Component{
@Override
public void display() {
System.out.println("显示文本框");
}
}
//列表框类:具体构件类
public class ListBox extends Component{
@Override
public void display() {
System.out.println("显示列表框");
}
}
//构件装饰类:抽象装饰类
public class ComponentDecorator extends Component {
private Component component; //维持抽象构件对象的引用
//注入抽象构件类型的对象
public ComponentDecorator(Component component){
this.component = component;
}
@Override
public void display() {
component.display();
}
}
//滚动条装饰类:具体装饰类
public class ScrollBarDecorator extends ComponentDecorator {
public ScrollBarDecorator(Component component) {
super(component);
}
public void display() {
this.setScrollBar();
super.display();
}
public void setScrollBar() {
System.out.println("为构件增加滚动条");
}
}
//黑色边框装饰类:具体装饰类
public class BlackBorderDecorator extends ComponentDecorator {
public BlackBorderDecorator(Component component) {
super(component);
}
public void display() {
this.setScrollBar();
super.display();
}
public void setScrollBar() {
System.out.println("为构件增加黑色边框!");
}
}
//客户端
public class Client {
public static void main(String args[]) {
Component component = new Window(); //定义具体构件
Component componentSB = new ScrollBarDecorator(component);//定义装饰后的构件
//具有滚动条的窗体
componentSB.display();
//装饰之后的对象继续注入到另一个装饰类中,进行第二次装饰
Component componentBB = new BlackBorderDecorator(componentSB);
System.out.println("=========================");
componentBB.display();
}
}
运行结果
为构件增加滚动条
显示窗体
=========================
为构件增加黑色边框!
为构件增加滚动条
显示窗体
3. 透明装饰模式与半透明装饰模式
1) 若客户端想单独调用某个具体装饰者新增的方法,这种形式的装饰模式为半透明装饰模式,标准的装饰模式要求客户端完全针对抽象编程客户端程序不应该将对象声明为具体构件类型或具体类型,应全为抽象构件;这就是透明装饰模式
2) 半透明模式案例
解决方案

//抽象装饰类 class Decorator implments Document { private Document document; public Decorator(Document document) { this.document = document; } public void display() { document.display(); } } //具体装饰类 class Approver extends Decorator { public Approver(Document document) { super(document); System.out.println("增加审批功能"); } public void approve() { System.out.println("审批文件"); } } |
Approver 类继承了抽象装饰类Decorator的display()方法,同时新增了业务方法approve(),这两个方法是独立的,没有任何调用关系
客户端调用这两个方法
Document doc = new PurchaseRequest(); Approver newDoc = new Approver(doc); newDoc.display(); //调用原有业务方法 newDoc.approve(); //调用新增业务方法 |
半透明的装饰模式设计简单灵活,但最大的缺点是不能实现对同一个对象的多次装饰,且客户端需要区别对待装饰之前的对象和装饰之后的对象
4. 装饰模式注意事项
1) 尽量保持装饰类的接口与被装饰类的接口相同,对于客户端而言可以一致对待装饰之前和之后的对象;
2) 尽量保持具体构件类ConcreteComponent是一个轻便的类,可通过装饰类对其进行扩展
3) 如果只有一个具体构件类,那么抽象装饰类可以作为该具体构件类的直接子类
去掉了抽象构件类的装饰模式
5. 装饰模式总结
装饰模式可以动态地增加或删除对象的职责,并使得需要装饰的具体构件类和具体装饰类可以独立变化,便于增加新的具体构件类和具体装饰类;在Java IO中输入流和输出流的设计,javax.swing包中一些图形界面构件功能的增强等地方都运用了装饰模式
1. 主要优点
1) 对于扩展一个对象的具体功能,装饰模式相比类的继承不会导致类的个数急剧增加;
2) 可以通过一种动态的方式来扩展一个对象的功能,通过配置文件可以在运行时选择不同的具体装饰类,实现不同的行为
3) 可以对一个对象进行多次装饰,通过使用不同的具体装饰类以及这些装饰的排列组合,可以创造出不同的行为的组合;
4) 具体构件类与具体装饰类可以独立变化,可以根据需要增加新的具体构件类和具体装饰类,原有类库代码无须改变,符合开闭原则;
2. 主要缺点
1) 会产生很多相互连接方式不同的小对象,数量一多会影响程序性能
2) 装饰模式比继承更容易出错,排错也困难;
3. 适用场景
1) 在不影响其他对象的情况下,以动态,透明的方式给单个对象添加职责
2) 当不能采用继承的方式对系统进行扩展或采用继承不利于系统扩展和维护时使用装饰模式,不能采用继承的情况:第一类是系统中存在大量独立的扩展,为支持每一种扩展或扩展之间的组合将产生大量的子类;第二类是因为类已经定义为不能被继承(如Java中final类)