1. 引出问题
需求:设计一个图形界面构件库,该构件库可以提供多种构件如窗口、文本框、列表框等。同时需要能够定制构件的显示样式,比如滚动条、黑边框等等,因此需要经常对构件库进行扩展以增强其功能。
如图,提供一个抽象构件类,各个具体构件作为抽象构件类的子类,分别实现display方法以显示不同的构件。同时,针对于不同的构件,设计显示不同样式的子类,如ScrollBarWindow可以在窗口中显示滚动条,BlackBorderWindow可以给窗口加上黑色边框。
但以上设计存在一些问题:
- 系统扩展麻烦。如果一个窗口既要显示滚动条,又要实现黑色边框,那么只能创建一个类,让它同时继承ScrollBarWindow类和BlackBorderWindow类,而在一些编程语言中(如Java)是无法实现多继承的
- 代码重复。图中可以看出,如果多个不同的构建都需要实现相同的样式,如Window和TextBox都需要添加滚动条,那么在Window和TextBox两个类的下面都需要单独写一个ScrollBar类作为子类
- 系统庞大。如果需要新增构件或者新增样式,需要新增的类的个数会随着系统的规模而成倍提升。例如,现在一共有三个构件,如果新增一个样式的话,这个样式需要在三个构建类下面分别进行实现,即在现有情况下,增加一个样式,需要新增三个类。又例如,现在需要新增一个构件,而现有的每个构件都有两种样式,那么在这个新构件下也需要重复实现这些样式类
2. 装饰模式介绍
定义:动态地给一个对象增加一些额外的职责,就增加对象功能来说,装饰模式比生成子类实现更为灵活。
简单地说,装饰模式就是可以在不改变对象本身的前提下,为对象增加新的功能。例如,对于一张照片,可以在不改变照片本身的情况下,为照片配置一个相框,而且可以根据用户不同的需要,为它配置不同的相框,甚至可以在小相框外面再套上一层大相框。
装饰模式包含以下四种角色:
- Component(抽象构件)
- ConcreteComponent(具体构件)
- Decoration(抽象装饰类)
- ConcreteDecoration(具体装饰类)
由于具体构建类和抽象装饰类都实现了相同的抽象构件接口,所以装饰模式可以允许客户用透明的方式给构架附上更多的责任
3. 以装饰模式重构案例
抽象构件类:Component
public abstract class Component {
public abstract void display();
}
具体构件类1:Window
public class Window extends Component{
@Override
public void display() {
System.out.println("显示窗口...");
}
}
具体构件类2:TextBox
public class TextBox extends Component{
@Override
public void display() {
System.out.println("显示文本框...");
}
}
具体构件类3:ListBox
public class ListBox extends Component{
@Override
public void display() {
System.out.println("显示列表框...");
}
}
抽象装饰类:ComponentDecoration
public class ComponentDecoration extends Component{
private Component component;
public ComponentDecoration(Component component) {
this.component = component;
}
@Override
public void display() {
component.display();
}
}
具体装饰类1:ScrollBarDecoration
public class ScrollBarDecoration extends ComponentDecoration{
public ScrollBarDecoration(Component component) {
super(component);
}
@Override
public void display() {
this.setScrollBar();
super.display();
}
private void setScrollBar() {
System.out.println("为组件增加滚动条...");
}
}
具体装饰类2:BlackBorderDecoration
public class BlackBorderDecoration extends ComponentDecoration{
public BlackBorderDecoration(Component component) {
super(component);
}
@Override
public void display() {
this.setBlackBorder();
super.display();
}
private void setBlackBorder() {
System.out.println("为组件增加黑边框...");
}
}
客户端:Client
public class Client {
public static void main(String[] args) {
Component component = new Window(); //定义窗口构件
Component decoratedComponent = new ScrollBarDecoration(component); //定义装饰后的窗口
decoratedComponent.display();
}
}
测试结果如下:
现在如果想为已经添加过滚动条的窗口再添加一个黑边框只需要增加一行代码:
public class Client {
public static void main(String[] args) {
Component component = new Window(); //定义窗口构件
Component decoratedComponent1 = new ScrollBarDecoration(component); //定义装饰后的窗口
Component decoratedComponent2 = new BlackBorderDecoration(decoratedComponent1); //为构件添加黑边框
decoratedComponent2.display();
}
}
测试结果如下:
运用装饰模式重构代码后,如果需要添加新的构件或者新的样式,只要将它们分别作为抽象构件类Component或者抽象装饰类ComponentDecoration的子类进行实现即可,增加了程序的可扩展性。另外,这种模式也避免了构件样式的代码重复性,降低了程序的复杂度。