结构型模式之扩展系统功能(装饰模式)

完整解决方案

为了让系统具有更好的灵活性和可扩展性,克服继承复用所带来的问题,Sunny公司开发人员使用装饰模式来重构图形界面构件库的设计,其中部分类的基本结构如图所示:

在图中,Component充当抽象构件类,其子类Window、TextBox、ListBox充当具体构件类,Component类的另一个子类ComponentDecorator充当抽象装饰类,ComponentDecorator的子类ScrollBarDecorator和BlackBorderDecorator充当具体装饰类。完整代码如下所示:

//抽象界面构件类:抽象构件类,为了突出与模式相关的核心代码,对原有控件代码进行了大改动
abstract class Component {
      public abstract void display();
}
//窗体类:具体构件类
 class Window extends Component {
        public void display() {
            System.out.println("显示窗体!");
        }
}
//文本框类:具体构件类
class TextBox extends Component {
        public void display() {
            System.out.println("显示文本框!");
        }
}
 //列表框类:具体构件类
class ListBox extends Component {
        public void display() {
            System.out.println("显示列表框!");
        }
}
 //构件装饰类:抽象装饰类
class ComponentDecorator extends Component {
        private Component component;    //维持对抽象构件类型对象的引用

        public ComponentDecorator(Component component)    //注入抽象构件
        {
            this.component = component;
        }

        public void display() {
            component.display();
        }
 }
//滚动条装饰类:具体装饰类
class ScrollBarDecorator extends ComponentDecorator {
        public ScrollBarDecorator(Component component) {
            super(component);
        }

        public void display() {
            this.setScrollBar();
            super.display();
        }

        public void setScrollBar() {
            System.out.println("为构件增加滚动条!");
        }
}
//黑色边框装饰类:具体装饰类
class BlackBorderDecorator extends ComponentDecorator {
        public BlackBorderDecorator(Component component) {
            super(component);
        }

        public void display() {
            this.setBlackBorder();
            super.display();
        }

        public void setBlackBorder() {
            System.out.println("为构件增加黑色边框!");
        }
}

编写如下客户端测试代码:

class Client {
        public static void main(String args[]) {
            Component component, componentSB;    //使用抽象构件定义
            component = new Window();
            // 定义具体构件
            componentSB = new ScrollBarDecorator(component);
            componentSB.display();
        }
}

编译并运行程序,输出结果如下:

为构件增加滚动条! 
显示窗体!

在客户端代码中,我们先定义了一个Window类型的具体构件对象component,然后将component作为构造函数的参数注入到具体装饰类ScrollBarDecorator中,得到一个装饰之后对象componentSB,再调用componentSB的display()方法后将得到一个有滚动条的窗体。如果我们希望得到一个既有滚动条又有黑色边框的窗体,不需要对原有类库进行任何修改,只需将客户端代码修改为如下所示:

class Client {
        public static void main(String args[]) {
            Component component, componentSB, componentBB; //全部使用
            component = new Window();
            componentSB = new ScrollBarDecorator(component);
            componentBB = new BlackBorderDecorator(componentSB);
            componentBB.display();
        }
}

编译并运行程序,输出结果如下:

为构件增加黑色边框! 
为构件增加滚动条! 
显示窗体!

我们可以将装饰了一次之后的componentSB对象注入另一个装饰类BlackBorderDecorator中实现第二次装饰,得到一个经过两次装饰的对象componentBB,再调用componentBB的display()方法即可得到一个既有滚动条又有黑色边框的窗体。

如果需要在原有系统中增加一个新的具体构件类或者新的具体装饰类,无须修改现有类库代码,只需将它们分别作为抽象构件类或者抽象装饰类的子类即可。使用装饰模式之后将大大减少了子类的个数,让系统扩展起来更加方便,而且更容易维护,是取代继承复用的有效方式之一。

装饰模式总结

装饰模式降低了系统的耦合度,可以动态增加或删除对象的职责,并使得需要装饰的具体构件类和具体装饰类可以独立变化,以便增加新的具体构件类和具体装饰类。在软件开发中, 装饰模式应用较为广泛,例如在JavaIO中的输入流和输出流的设计、javax.swing包中一些图形界面构件功能的增强等地方都运用了装饰模式。

装饰模式的主要优点如下:

(1) 对于扩展一个对象的功能,装饰模式比继承更加灵活性,不会导致类的个数急剧增加。

(2) 可以通过一种动态的方式来扩展一个对象的功能,通过配置文件可以在运行时选择不同的具体装饰类,从而实现不同的行为。

(3) 可以对一个对象进行多次装饰,通过使用不同的具体装饰类以及这些装饰类的排列组合, 可以创造出很多不同行为的组合,得到功能更为强大的对象。

(4) 具体构件类与具体装饰类可以独立变化,用户可以根据需要增加新的具体构件类和具体装饰类,原有类库代码无须改变,符合“开闭原则”。

装饰模式的主要缺点如下:

(1) 使用装饰模式进行系统设计时将产生很多小对象,这些对象的区别在于它们之间相互连接的方式有所不同,而不是它们的类或者属性值有所不同,大量小对象的产生势必会占用更多的系统资源,在一定程序上影响程序的性能。

(2) 装饰模式提供了一种比继承更加灵活机动的解决方案,但同时也意味着比继承更加易于出错,排错也很困难,对于多次装饰的对象,调试时寻找错误可能需要逐级排查,较为繁琐。

在以下情况下可以考虑使用装饰模式:

(1) 在不影响其他对象的情况下,以动态、透明的方式给单个对象添加职责。

(2) 当不能采用继承的方式对系统进行扩展或者采用继承不利于系统扩展和维护时可以使用装饰模式。不能采用继承的情况主要有两类:第一类是系统中存在大量独立的扩展,为支持每一种扩展或者扩展之间的组合将产生大量的子类,使得子类数目呈爆炸性增长;第二类是因为类已定义为不能被继承(如Java语言中的final类)。

原文地址:https://www.cnblogs.com/47Gamer/p/13809548.html