现在的位置: 首页 > 综合 > 正文

Java 设计模式 装饰者模式

2016年06月15日 ⁄ 综合 ⁄ 共 5641字 ⁄ 字号 评论关闭
装饰者模式定义:

           动态地将责任附加到对象上。若要扩展功能,装饰者提供了比继承更加有弹性的替代方案。

         在设计的时候,往往要给一个对象的功能进行一些修饰,对功能进行拓展和增强,以满足我们的需求。

 案例分析:

         举个例子,最近流行歌曲比赛,什么《中国好声音》,《中国好歌曲》什么的,对于一个参赛者而言,就是唱歌,定义类如下:

public class Competitor {

    public void sing()

    {

        System.out.println("我唱唱唱......");

    }

}

         但是这个是远远不够的,你有一个好嗓门,也是需要包装的,一个裸唱、没有伴奏、没有伴舞的参赛者去比赛哪有什么竞争力啊!于是,我们要对这个参赛者进行包装,在他唱歌的时候加上舞蹈:

public class CompetitorDecorator1 extends Competitor {
    public void sing()
    {
        this.dance();
        super.sing();
    }
    private void dance()
    {
        System.out.println("偏偏起舞....");
    }
}

          加上舞蹈还不行,还要在唱歌的时候播放合拍的背景音乐:

public class CompetitorDecorator2 extends Competitor {
    public void sing()
    {
        this.playMusic();
        super.sing();
    }
    private void playMusic()
    {
        System.out.println("播放背景音乐......");
    }
}

          现在的选手都打感情牌,唱完歌后,介绍自己有多不幸,什么没爹啦,没娘啦,早年多不幸啊,自己多努力啊……诸如此类,把评委和观众搞得稀里哗啦的,然后评分就蹭蹭地上去了:

publicclass CompetitorDecorator3 extends Competitor {
    public void sing()
    {
        super.sing();
        this.introduceBackground();
    }
    private void introduceBackground()
    {
        System.out.println("悲惨背景介绍,博取同情,赢感情牌....");
    }
}
public class CompetitorDecorator3 extends Competitor {
    public void sing()
    {
        super.sing();
        this.introduceBackground();
    }
    private void introduceBackground()
    {
        System.out.println("悲惨背景介绍,博取同情,赢感情牌....");
    }
}

          以上对对象的装饰实现,是通过继承父类的方式,重写父类的方法,加上自己的装饰,强化相应的功能。

          现在,我想将播放背景音乐和跳舞这两个装饰都用上,该怎么用呢? 相信很多人都会有这样的一个方法:继承CompetitorDecorator1,重写sing() 方法,将playMusic()方法加进去啊

如下所示:

public class CompetitorDecorator4 extends CompetitorDecorator1 {
    @Override
    public void sing()
    {
        this.playMusic();
        super.sing();
    }
    private void playMusic()
    {
        System.out.println("播放背景音乐......");
    }
}

 

            是的,这样确实可以完成相应的功能。但是有没有觉得怪怪的呢?

           1.我们在CompetitorDecorator2内已经有了 playMusic() 代码片段,我们在CompetitorDecorator4内有和它完全一样的playMusic()代码片段,这个是不合适的,也是不可取的!

           2.如果这个选手比较有才,有五花八门的各种装饰,我们为此要增加相应多的装饰类;如果他在这方面有很深的造诣,把这些装饰发挥的淋漓尽致,能把这些装饰融合到一块,这些五花八门的装饰随机组合,这得要衍生多少个子类啊!!!!!

           我们一般性地算一下,如果有不同的装饰功能 N 个,那么,如果我们将这N个随机组合,将有:

         

           也就是说做多可以产生sum 个子类,相信学过排列组合的人知道这是个什么概念,另外有多么庞大的代码重复……

 

 案例重构

             无论怎样,我们不应该采用上面的形式!

            如果修饰者修饰过后,可以将这个修饰过的结果传给下一个修饰者,这样的话,就不需要定义这个么多的组合类了,只需要定义相应类型的修饰者就可以了。如果有10 个修饰者的话,只需要定义10 个修饰者的类,然后在实现的时候我们根据需求添加到相应的修饰就可以了。

 

           从上面的的三个装饰来看,无非是在原有 sing() 方法的基础上添加一些辅助的修饰的功能。

           在上面的继承机制上,sing方法在装饰者定义的时候已经是一个固定功能的了。如果可以将这个sing()方法变为动态的,即运行时确定的行为,然后再在修饰的时候加上相应的修饰就可以了。

          一个类要要动态绑定某个对象的行为,是持有相应的对象引用,然后在运行时根据这个引用绑定的具体对象,体现出不同的行为,这个就是动态绑定。

          依照上面定义的规则,我们可以通过下面来实现,定义一个Decorator类,持有一个Competitor对象,只有当运行的时候动态绑定到sing() 方法。然后在这个sing() 方法的基础上进行装饰,加入相应的辅助、增强功能。

public class CompetitorDecorator extends Competitor{

    private Competitor competitor;

    public CompetitorDecorator(Competitor competitor)
    {
        this.competitor= competitor;
    }
    public void sing()
    {
        competitor.sing();
    }
}

 

          这样的话,如果我们在创建Decorator对象的时候,如果传入的Competitor 是已经被修饰过的,即sing()方法是被重写过的,那么我们就可以在此基础上再添新的修饰了。

我们重构上面的三个修饰类如下:

  1. 舞蹈修饰类:
    public class CompetitorDecoratorA extends CompetitorDecorator {
        public CompetitorDecoratorA(Competitor competitor) {
            super(competitor);
        }
        public void sing()
        {
            this.dance();
            super.sing();
        }
        private void dance()
        {
            System.out.println("偏偏起舞....");
        }
    
    }
    

 

   2.伴奏修饰类:

public class CompetitorDecoratorB extends CompetitorDecorator {
    public CompetitorDecoratorB(Competitor competitor) {
        super(competitor);
    }
    public void sing()
    {
        this.playMusic();
        super.sing();
    }
    private void playMusic()
    {
        System.out.println("播放背景音乐......");
    }
}

 

        3. 身世介绍类:

           

public class CompetitorDecoratorC extends CompetitorDecorator {
    public CompetitorDecoratorC(Competitor competitor) {
        super(competitor);
    }
    publi cvoid sing()
    {
        super.sing();
        this.introduceBackground();
    }
    private void introduceBackground()
    {
        System.out.println("悲惨背景介绍,博取同情,赢感情牌....");
    }
}
  1. Client 类
    public class Client {
    
        public static void main(String[] args) {
    
            Competitor com = new Competitor();
    
            // 加入了跳舞修饰,decorator 的sing() 方法中被动态添加了dance 修饰
    
            CompetitorDecorator decorator = new CompetitorDecoratorA(com);
    
            // 在上面修饰的基础上,加入背景音乐修饰
    
            decorator = new CompetitorDecoratorB(decorator);
    
            // 在上面修饰的基础上,加上悲惨的身世介绍
    
            decorator = new CompetitorDecoratorC(decorator);
    
            decorator.sing();
    
            System.out.println("满场尖叫,满分通过!!!!!");
    
        }
    
    }
    

    程序运行结果:

 

通过这种装饰方式,我们可以很方便地根据具体情况增加修饰者,并且不会产生多余的子类,整个过程很灵活,不像单纯使用类继承的方式会产生很多子类。

装饰者模式

目的

  • 给对象动态地添加附加功能。
  • 装饰者提供了一个灵活的拓展子类功能的备选方案。

结构

 

  • Component :定义了一个可以被动态添加功能的接口
  • Decorator :持有一个Component对象的引用,并且定义了一个和Component保持一致的接口。
  • ConcreteDecorator :Component添加功能的角色。

应用

  • 给一个对象动态地、透明地添加职能,即:不影响其他对象。
  • 动态添加的职能能够被取消。
  • when extension by subclassing is impractical. Sometimes a large number of independent extensions are possible and would produce an explosion of subclasses to support every combination. Or a class
    definition may be hidden or otherwise unavailable for subclassing.
    当通过子类继承拓展功能不可行时。有时候大规模的独立拓展,为了支持每个组合而导致子类爆炸式的增长时。或者是。。。

Consequences

  • 比静态继承更具灵活性.The Decorator pattern provides a more flexible way to add responsibilities to objects than can be had with static
    (multiple) inheritance. With decorators responsibilities can be added and removed at run-time simply by attaching and detaching them.
  • Avoids feature-laden classes high up in the hierarchy.Decorator offers a pay-as-you-go approach to adding responsibilities. Instead of trying to support all foreseeable features in a complex,
    customizable class, you can define a simple class and add functionality incrementally with Decorator objects. Functionality can be composed from simple pieces. As a result, an application needn't pay for features it doesn't use.
  • A decorator and its component aren't identical.A decorator acts as a transparent enclosure. But from an object identity point of view, a decorated component is not identical to the component
    itself. Hence you shouldn't rely on object identity when you use decorators.
  • Lots of little objects会产生很多小对象.A design that uses Decorator often results in systems
    composed of lots of little objects that all look alike. The objects differ only in the way they are interconnected, not in their class or in the value of their variables. Although these systems are easy to customize by those who understand them, they can be
    hard to learn and debug.
    在系统中采用Decorator设计模式的设计会产生很多看上去类似的小对象。它们之间仅仅是相互交互的地方不同,而不是在类的内部或者是变量的值不同。虽然说对于了解系统内的人而言,方便系统定制,但是他们很难学习和调试。

抱歉!评论已关闭.