zoukankan      html  css  js  c++  java
  • 设计模式:装饰模式

    装饰模式  


    一、引子 
        装饰模式?肯定让你想起又黑又火的家庭装修来。其实两者在道理上还是有很多相像的 
    地方。家庭装修无非就是要掩盖住原来实而不华的墙面,抹上一层华而不实的涂料,让生活 
    多一点色彩。而墙还是那堵墙,他的本质一点都没有变,只是多了一层外衣而已。 
        那设计模式中的装饰模式,是什么样子呢? 


    二、定义与结构 
        装饰模式(Decorator)也叫包装器模式(Wrapper)。GOF 在《设计模式》一书中给出 
    的定义为:动态地给一个对象添加一些额外的职责。就增加功能来说,Decorator 模式相比 
    生成子类更为灵活。 
        让我们来理解一下这句话。我们来设计“ 门”这个类。假设你根据需求为“ 门”类作了如下 
    定义: 
                                        Door 

                                        open() 
                                        close() 
                                        lock() 


        现在,在系统的一个地方需要一个能够报警的Door,你来怎么做呢?你或许写一个Door 
    的子类AlarmDoor,在里面添加一个子类独有的方法alarm()。嗯,那在使用警报门的地方 
    你必须让客户知道使用的是警报门,不然无法使用这个独有的方法。而且,这个还违反了 
    Liskov 替换原则。 
        也许你要说,那就把这个方法添加到Door 里面,这样不就统一了?但是这样所有的门 
    都必须有警报,至少是个“哑巴”警报。而当你的系统仅仅在一两个地方使用了警报门,这明 
    显是不合理的——虽然可以使用缺省适配器来弥补一下。 
        这时候,你可以考虑采用装饰模式来给门动态的添加些额外的功能。 
        下面我们来看看装饰模式的组成,不要急着去解决上面的问题,到了下面自然就明白了! 
    1)  抽象构件角色(Component):定义一个抽象接口,以规范准备接收附加责任的对象。 
    2)  具体构件角色(Concrete Component):这是被装饰者,定义一个将要被装饰增加功能的 
        类。 
    3)  装饰角色(Decorator):持有一个构件对象的实例,并定义了抽象构件定义的接口。 
    4)  具体装饰角色(Concrete Decorator):负责给构件添加增加的功能。 
        看下装饰模式的类图: 

                                    Compone 
                                        nt 


                                   defaultMethod() 


           ConcreteComponent                       Decorator 


          defaultMethod()                         defaultMethod() 


                               ConcreteDecoratorA            ConcreteDecoratorB 
                               addedState : int              addedState : int 


                               defaultMethod()               defaultMethod() 
                               addedBehaviour()              addedBehaviour() 
                               setState()                    setState() 
                               getState()                    getState() 


        图中ConcreteComponent 可能继承自其它的体系,而为了实现装饰模式,他还要实现 
    Component  接口。整个装饰模式的结构是按照组合模式来实现的——两者都有类似的结构 
    图,都基于递归组合来组织可变数目的对象。但是两者的目的是截然不同的,组合 
     (Composite)模式侧重通过递归组合构造类,使不同的对象、多重的对象可以“一视同仁”; 
    而装饰(Decorator)模式仅仅是借递归组合来达到定义中的目的。 


    三、举例 
        这个例子还是来自我最近在研究的JUnit,如果你对JUnit 还不太了解,可以浏览一些 
    关于Junit 的文章。不愧是由GoF 之一的Erich   Gamma 亲自开发的,小小的东西使用了N 
    种的模式在里面。下面就来看看JUnit 中的装饰模式。 
        在JUnit 中,TestCase 是一个很重要的类,允许对其进行功能扩展。 
        在junit.extensions 包中,TestDecorator、RepeatedTest 便是对TestCase 的装饰模式 
    扩展。下面我们将它们和上面的角色对号入座。 

                                       Test 


                        TestCase                TestDecorator 


                                                 RepeatedTest 


    呵呵,看看源代码吧,这个来的最直接! 
     //这个就是抽象构件角色 


     public interface Test { 
         /** 
           * Counts the number of test cases that will be run by this test. 
           */ 
         public abstract int countTestCases(); 
         /** 
           * Runs a test and collects its result in a TestResult instance. 
           */ 
         public abstract void run(TestResult result); 



    //具体构件对象,但是这里是个抽象类 


    public abstract class TestCase extends Assert implements Test { 
          …… 
          public int countTestCases() { 
               return 1; 
          } 
          …… 
          public TestResult run() { 
               TestResult result= createResult(); 
               run(result); 
               return result; 
          } 
          public void run(TestResult result) { 
               result.run(this); 
          } 
          …… 



       //装饰角色 


       public class TestDecorator extends Assert implements Test { 
              //这里按照上面的要求,保留了一个对构件对象的实例 


              protected Test fTest; 


              public TestDecorator(Test test) { 
                   fTest= test; 
              } 
              /** 
               * The basic run behaviour. 
               */ 
              public void basicRun(TestResult result) { 
                   fTest.run(result); 
              } 
              public int countTestCases() { 
                   return fTest.countTestCases(); 
              } 
              public void run(TestResult result) { 
                   basicRun(result); 
              } 
              public String toString() { 
                   return fTest.toString(); 
              } 
              public Test getTest() { 
                   return fTest; 
              } 
       } 


        //具体装饰角色,这个类的增强作用就是可以设置测试类的执行次数 


    public class RepeatedTest extends                TestDecorator { 
        private int fTimesRepeat; 


        public RepeatedTest(Test test, int repeat) { 
             super(test); 
             if (repeat < 0) 
                   throw new IllegalArgumentException("Repetition count must be > 0"); 
             fTimesRepeat= repeat; 
        } 
        //看看怎么装饰的吧 


        public int countTestCases() { 
             return super.countTestCases()*fTimesRepeat; 
        } 
        public void run(TestResult result) { 

            for (int i= 0; i < fTimesRepeat; i++) { 
                if (result.shouldStop()) 
                    break; 
                super.run(result); 
            } 
         } 
         public String toString() { 
            return super.toString()+"(repeated)"; 
         } 
      } 
        使用的时候,就可以采用下面的方式: 


        TestDecorator test = new RepeatedTest(new TestXXX() , 3); 
        让我们在回想下上面提到的“ 门”的问题,这个警报门采用了装饰模式后,可以采用下面 
    的方式来产生。 


        DoorDecorator alarmDoor = new AlarmDoor(new Door()); 


    四、应用环境 
         GOF 书中给出了以下使用情况: 
    1)  在不影响其他对象的情况下,以动态、透明的方式给单个对象添加职责。 
    2)  处理那些可以撤消的职责。 
    3)  当不能采用生成子类的方法进行扩充时。一种情况是,可能有大量独立的扩展,为支持 
        每一种组合将产生大量的子类,使得子类数目呈爆炸性增长。另一种情况可能是因为类 
        定义被隐藏,或类定义不能用于生成子类。 
        来分析下JUnit 的使用是属于哪种情况。首先实现了比静态继承更加灵活的方式,动态 
    的增加功能。试想为Test  的所有实现类通过继承来增加一个功能,意味着要添加不少的功 
    能类似的子类,这明显是不太合适的。 
        而且,这就避免了高层的类具有太多的特征,比如上面提到的带有警报的抽象门类。 


    五、透明和半透明 
        对于面向接口编程,应该尽量使客户程序不知道具体的类型,而应该对一个接口操作。 
    这样就要求装饰角色和具体装饰角色要满足Liskov 替换原则。像下面这样: 


        Component c = new ConcreteComponent(); 
        Component c1 = new ConcreteDecorator(c); 
        JUnit 中就属于这种应用,这种方式被称为透明式。而在实际应用中,比如java.io 中往 
    往因为要对原有接口做太多的扩展而需要公开新的方法(这也是为了重用)。所以往往不能 
    对客户程序隐瞒具体的类型。这种方式称为“半透明式”。 


        在java.io 中,并不是纯装饰模式的范例,它是装饰模式、适配器模式的混合使用。 


    六、其它 
        采用Decorator 模式进行系统设计往往会产生许多看上去类似的小对象,这些对象仅仅 
    在他们相互连接的方式上有所不同,而不是它们的类或是它们的属性值有所不同。尽管对于 
    那些了解这些系统的人来说,很容易对它们进行定制,但是很难学习这些系统,排错也很困 
    难。这是GOF 提到的装饰模式的缺点,你能体会吗?他们所说的小对象我认为是指的具体 

    装饰角色。这是为一个对象动态添加功能所带来的副作用。 

    下载:

    http://download.csdn.net/detail/undoner/5335717

    深入浅出设计模式-中文版

    http://www.lsoft.cn

    LSOFT.CN (琅软中国) - 创造玉石般的软件,完美的用户体验!


  • 相关阅读:
    Proj THUDBFuzz Paper Reading: The Art, Science, and Engineering of Fuzzing: A Survey
    Proj THUDBFuzz Paper Reading: A systematic review of fuzzing based on machine learning techniques
    9.3 付费代理的使用
    11.1 Charles 的使用
    第十一章 APP 的爬取
    10.2 Cookies 池的搭建
    10.1 模拟登录并爬取 GitHub
    11.5 Appium 爬取微信朋友圈
    11.4 Appium 的基本使用
    11.3 mitmdump 爬取 “得到” App 电子书信息
  • 原文地址:https://www.cnblogs.com/wuyida/p/6301020.html
Copyright © 2011-2022 走看看