一、概述
动态地给一个对象增加一些额外的职责(方法)。就扩展功能而言,装饰者模式提供了一种比使用子类更加灵活的替代方案。
装饰模式是一种用于替代继承的技术,它通过一种无须定义子类的方式给对象动态增加职责,使用对象之间的关联关系取代类之间的继承关系。
二、结构与实现
- 结构
(1)Component(抽象构件):
是具体构件类和抽象装饰者类的共同父类。
(2)ConcreteComponent(具体构件类):
抽象构件的子类,装饰者类可以给它增加额外的职责。
(3)Decorator(抽象装饰类):
抽象构件的子类,具体装饰类的父类,用于给具体构件增加职责,但在子类中实现。它维护一个指向抽象构件对象的引用,通过该引用可以调用装饰之前构件对象的方法,并通过其子类扩展该方法,以达到装饰的目的。
(4)ConcreteDecorator(具体装饰类):
负责向构件添加新的职责。每一个具体装饰类都定义了一些新的行为,它可以调用在抽象装饰类中定义的方法。
- 实现
abstract class Component{
abstract void operation();
}
class ConcreteComponent extends Component{
void operation(){
//功能实现
}
}
class Decorator extends 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(component);
}
public void operation(){
super.operation();//调用原有业务方法
addedBehavior();//调用新增业务方法
}
public void addedBehavior(){
//新增业务方法
}
}
三、应用案例
- 分析
窗体、文本框、列表框为具体构件类,滚动条、黑色边框为具体装饰类。
-
类图
-
实现
//抽象构件类
abstract class Component {
public abstract void display();
}
//具体构件类(抽象构件类子类)
class ListBox extends Component {
@Override
public void display() {
System.out.println("显示列表框...");
}
}
class TextBox extends Component {
@Override
public void display() {
System.out.println("显示文本框...");
}
}
class Window extends Component {
@Override
public void display() {
System.out.println("显示窗体...");
}
}
//抽象装饰类(抽象构件子类)
class ComponentDecorator extends Component{
private Component component;
public ComponentDecorator(Component component){
this.component=component;
}
@Override
public void display() {
component.display();
}
}
//具体装饰者类(抽象装饰子类)
class BlackBorderDec extends ComponentDecorator {
public BlackBorderDec(Component component) {
super(component);
}
public void display(){
this.setBlackBorder();
super.display();
}
private void setBlackBorder() {
System.out.println("添加了黑色边框...");
}
}
class ScrollBarDec extends ComponentDecorator {
public ScrollBarDec(Component component) {
super(component);
}
public void display(){
this.setScrollBar();
super.display();
}
private void setScrollBar() {
System.out.println("添加了滚动条...");
}
}
//模拟客户端测试
public class client {
public static void main(String[] args) {
Component component1=new Window();
Component comB=new ScrollBarDec(component1);
Component comBB=new BlackBorderDec(comB);
comWithBlack.display();
}
}
- 测试运行
- 发现
将装饰一次之后的comB对象再次注入另一个具体装饰类,实现第二次装饰,得到了一个经过两次装饰的comBB对象,再调用display()方法就可以得到一个既有滚动条又有黑色边框的窗体了。
四、扩展
透明装饰模式和半透明装饰模式
- 透明装饰模式
在透明装饰模式中要求客户端完全针对抽象编程,装饰模式的透明性要求客户端程序不应该将对象声明为具体构件类型或具体装饰类型,而应该全部声明为抽象构件类型。对客户端而言,具体构件类和具体装饰类对象没有任何区别。(缺点,无法单独调用装饰类的独有功能)
//不应该将对象声明为具体构件类型或具体装饰者类型
Window window=new Window();
//应该声明为抽象构件类型,向上转型
Component window=new Window();
- 半透明装饰模式
用具体装饰类型来定义装饰后的对象,而具体构件类型仍然可以使用抽象构件类型来定义,可以单独调用装饰的独有方法。(缺点:无法多次进行装饰)
//具体构件类型仍然可以使用抽象构件类型来定义
Component window =new Window();
//具体装饰类型来定义装饰后的对象
BlackBorderDec blackBorder=new BlackBorderDec(window);
//可以单独调用
blackBorder.setBlackBorder();
五、总结:
- 特点:
(1)可以动态增加或删除对象的职责,并使需要装饰的具体构件类和具体装饰类可以独立变化,以便增加新的具体构件类和具体装饰类。
(2)减少子类个数,扩展方便,容易维护。
(3)结构主要有抽象构件类,其子类有具体构件类和抽象装饰类,而在抽象装饰类中,其子类则是具体装饰类。
- 适用环境:
(1)在不影响其他对象的情况下以动态透明的方式给单个对象添加职责。
(2)当不能采用继承的方式时。主要有两种情况:子类大爆炸,类已经被定义为不能继承。