论坛首页 Java企业应用论坛

设计模式学习笔记JAVA 02(装饰者模式)—2014_3_24

浏览 6372 次
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
作者 正文
   发表时间:2014-03-24  

************************************

先 喝杯 咖啡,再来个包子

 

***********************************

我觉得 装饰者 模式,就是 一套 致富 模式,中国特色 的摸石头 摸出黄金来。

如果懂 装饰者 模式,就能理解下面的



 

没有学过的,还不能理解。马上就开始学习,


 



 
 
 

 



 



 



 

See, five classes total.

This is definitely the way to go.

 

  • 大小: 20.1 KB
  • 大小: 32.3 KB
  • 大小: 53 KB
  • 大小: 62.3 KB
  • 大小: 31.8 KB
  • 大小: 45 KB
  • 大小: 47.7 KB
   发表时间:2014-03-25  

**************************************

Mr. Decorator Pattern

 

***************************************

we’ll start with a beverage and “decorate” it with the condiments at runtime.

For example, if the customer wants a Dark Roast with Mocha and Whip, then we’ll:

 

  1. Take a DarkRoast object
  2. Decorate it with a Mocha object
  3. Decorate it with a Whip object
  4. Call the cost() method and rely on delegation to add on the condiment costs

Let’s see how this works...



 



 

O k ay, h e re’s w hat we k n o w s o f a r.. .

 

  • Decorators have the same supertype as the objects they decorate.
  • You can use one or more decorators to wrap an object.
  • Given that the decorator has the same supertype as the object it decorates, we can pass around a decorated object in place of the original (wrapped) object.
  • The decorator adds its own behavior either before and/or after delegating to the object it decorates to do the rest of the job.
  • Objects can be decorated at any time, so we can decorate objects dynamically at runtime with as many decorators as we like.
  • 大小: 35.9 KB
  • 大小: 47.3 KB
0 请登录后投票
   发表时间:2014-03-25   最后修改:2014-03-25

********************************

The Decorator Pattern defined

 

**********************************



 

Decorating our Beverages



 
 The four concrete components, one per coffee type.

只列出了 DarkRoast。

 

Let’s start with the Beverage class, which doesn’t need to change from Starbuzz’s original design. Let’s take a look:

public abstract class Beverage {
String description = “Unknown Beverage”;
public String getDescription() {
return description;
}
public abstract double cost();
}

 

Beverage is an abstract class with the two methods getDescription() and cost().

 

getDescription is already implemented for us, but we need to implement cost() in the subclasses.

 

Now that we’ve got our base classes out of the way, let’s implement some beverages. We’ll start with Espresso.
Remember, we need to set a description for the specific beverage and also implement the cost() method.

 

public class Espresso extends Beverage {
public Espresso() {
description = “Espresso”;
}
public double cost() {
return 1.99;
}
}

 

First we extend the Beverage class, since this is a beverage

 

To take care of the description, we set this in the constructor for the class.

Remember the description instance variable is inherited from Beverage.

 

Finally, we need to compute the cost of an Espresso.

We don’t need to worry about adding in condiments in this class, we just need to return the price of an Espresso: $1.99.

 

**********************************************

Beverage is simple enough. Let’s implement the abstract class for the Condiments (Decorator) as well:



 

First, we need to be interchangeable with a Beverage, so we extend the Beverage class

 

public abstract class CondimentDecorator extends Beverage {
public abstract String getDescription();
}

 

We’re also going to require that the condiment decorators all reimplement the getDescription() method.

 



 

Now it’s time to implement the concrete decorators. Here’s Mocha:

 

public class Mocha extends CondimentDecorator {
Beverage beverage;
public Mocha(Beverage beverage) {
this.beverage = beverage;
}
public String getDescription() {
return beverage.getDescription() + “, Mocha”;
}
public double cost() {
return .20 + beverage.cost();
}
}

 

Mocha is a decorator, so we extend CondimentDecorator. Remember, CondimentDecorator extends Beverage.

 

We’re going to instantiate Mocha with a reference to a Beverage using:

(1) An instance variable to hold the beverage we are wrapping.
(2) A way to set this instance variable to the object we are wrapping. Here, we’re going to to pass he beverage we’re wrapping to the decorator’s constructor.

 

We want our description to not only include the beverage - say “Dark Roast” - but also to include each item decorating the beverage, for instance, “Dark Roast, Mocha”. So we first delegate to the object we are decorating to get its description, then append “, Mocha” to that description.

 

Now we need to compute the cost of our beverage with Mocha. First, we delegate the call to the object we’re decorating, so that it can compute the cost; then, we add the cost of Mocha to the result.

 

****************************************

测试一下,

public class StarbuzzCoffee {
public static void main(String args[]) {
Beverage beverage = new Espresso();
System.out.println(beverage.getDescription()
+ “ $” + beverage.cost());
Beverage beverage2 = new DarkRoast();
beverage2 = new Mocha(beverage2);
beverage2 = new Mocha(beverage2);
beverage2 = new Whip(beverage2);
System.out.println(beverage2.getDescription()
+ “ $” + beverage2.cost());
Beverage beverage3 = new HouseBlend();
beverage3 = new Soy(beverage3);
beverage3 = new Mocha(beverage3);
beverage3 = new Whip(beverage3);
System.out.println(beverage3.getDescription()
+ “ $” + beverage3.cost());
}
}

 



 

  • 大小: 48.2 KB
  • 大小: 37.9 KB
  • 大小: 33.3 KB
  • 大小: 37.9 KB
  • 大小: 13.5 KB
0 请登录后投票
   发表时间:2014-03-26  

***************************

对 StarbuzzCoffee

 

简洁清晰

***************************

http://www.cnblogs.com/baochuan/archive/2012/02/28/2371521.html

 

这篇文章写得简洁清晰,用词文雅,非常不错

基本完全引用,哈哈

 

介绍                                                                                                                   

 

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

思维导图                                                                                                           



 

有这样一个项目,做一个餐厅订餐系统。起初的代码结构是这样的。

前面有很多Beverage的继承类,现在遇到的问题是牛奶的价钱上涨了,那么所有相关的 类,我们都要进行调整,比如Milk,SugarAndMilk类,这种类还有很多,我们需要逐个去修改类中的方法——开发人员每次都做这种事情,要疯 了!

所以我们要改变现有的结构。以下的图都是简图,实际的图,可没有这么简单

 



 

 设计问题:

 

1》类数量爆炸,有很多类,难以维护;

 

2》整个设计呆板;

 

3》基类加入的新功能无法使用于子类;

 

 后来经过小组研究决定,我们决定把基础类抽出来,比如,我们把咖啡做成一个单独的类,其他的咖啡,比如牛奶咖啡,甜味咖啡,我们只对材料单独包装成一个类。

 

经过改良的设计:



 

详解

 

1》对于饮品,我们直接继承Beverage类,直接把报价写进饮品类里面;

 

2》而对于一些需要添加调味品的特殊饮品,我们做累加操作。比如,我想要杯奶咖啡,则 总价=咖啡价+奶价

 

3》这样不同的饮料就很容易知道它的价格。

 

 
介绍                                                                                                                  
1.装饰者(Milk)和被装饰者(Coffee)必须是一样的类型。目的是装饰者必须取代被装饰者。

 

2.添加行为:当装饰者和组件组合时,就是在加入新的行为。

 

 

题外话:

 

1.利用继承设计子类行为,是在编译时静态决定的,而且所有的子类都会继承到相同的行为。打个比方,老子想学点功夫,看你小子会太极拳,老子只需要继承你一下 ,老子也就会太极拳了——呵呵,这时老子就变成你儿子了,看来继承是要付出代价的。

 

 

2.组合,我们可以扩展对象的行为,在运行时动态地进行扩展。利用组合我们可以随时把我们当时设计超类时没有想 到的方法加入到对象中,而不用改变现有的代码。打个比方,老子现在没有内力,吸功大法,把和尚,尼姑,道士的内力(行为对象)都吸过来,那在搏斗(运行 时)中,老子可以随时都能使用不同的内力,但也不能胡乱吸内力,否则你就要走火入魔了!

 

 

3.类应该对扩展开放,对修改关闭。如果我们每个部分都用装饰者模式进行设计,那么对于整个框架来说有点浪费, 而且你也加大了代码的难度。那什么时候使用这种模式呢?我们一般用于经常改变的地方。那我们又怎么知道哪些是经常改变的地方呢?这个就需要我们的经验和你 对所处行业的了解。建议大家平时多看点例子。

 

 

4.装饰模式为设计注入弹性,但同时会在设计中加入大量的小类,这偶尔会导致别人不容易了解这种设计。

 

 

5.在使用装饰者模式的时候,对插入的的装饰者要特别小心。因为装饰者模式依赖某种特定的类型(Beverage)。

 

 
  • 大小: 18.1 KB
  • 大小: 35.1 KB
  • 大小: 28.2 KB
0 请登录后投票
   发表时间:2014-03-26  

***************************************

再看一个例子

 

红草莓(餐厅)--装饰者模式

**************************************

http://www.cnblogs.com/UpThinking/archive/2010/04/13/1710719.html

 

背景废话                                                                                                          

 

    11:30超出了,哥们几个(当然还有几个女的,比如:“全总”,“鞠总”。),脚步依旧。沿着熟悉的路线,穿过那漫长的十字路口,奔着我们的食物(抽象 类),当然这个抽象类包含了计算价格这个方法了getCost(),不过如果某一天红草莓发慈悲,可以我们这些劳苦大众,免费给我们吃,当然是非常愿意的 啦,冲向红草莓(新型餐厅)。

    来到红草莓楼下,几位女同志比较喜欢在一楼吃那些什么砂锅,面条什么的(具体的食物,实现了食物这个抽象类的getCost()方法,因为不管怎么样,都是要收费的),介于男同胞一直以来体力消耗都比较厉害,所以那些东西基本上吃不饱,所以就到二楼吃饭。

   先来看看把,首先说女同志吧,女同志特别爱挑剔,吃个砂锅,要往里面加入一些配料,比如说,金针菇啊,海带啊,油豆腐啊,豆腐干啊,反正很多,跟每个人的口味有关。

好吧,那就先“全总”先来:我要一个砂锅,里面+金针菇+海带。OK,

全总的这个砂锅类:

      砂锅金针菇海带 extends 食物 {getCost() {return 砂锅4元+金针菇2元+海带1元}}

鞠总的砂锅类:

      砂锅金针菇 extends 食物{getCost() {return 砂锅4元+金针菇2元}}

接下来,公司又来了一堆MM。我要,砂锅+海带,我要砂锅+油豆腐,我要砂锅+豆腐干,我要……,我要……,发挥想象她到底要啥东西啊???

OH,my lady gaga,每个MM的食物一个类,要“类爆炸”了,神呐救救我吧。

      这还不够,二楼的man还没说话呢,我要米饭+大排+小排+牛排(绝对的肉食动物),我要米饭+鲤鱼……又是一堆……

神啊。装饰者模式披着超人的披风,头戴性感黑丝袜,飞到了红草莓餐厅。

      大家不要慌,我,装饰者模式先来介绍下自己(装饰者模式类图):

 



 

 

测试一下,

public class Decorator {

    public static void main(String[] args) {



    食物 ricedan=new 米饭();

    System.out.println(ricedan.getName()+"  ¥"+ricedan.getCost());

    

    食物 rice = new 米饭();

    rice = new 大排(rice);    

    rice = new 香干(rice);

    System.out.println(rice.getName() + "  ¥" + rice.getCost());

    食物 tajines=new 砂锅();

    tajines=new 香干(tajines);

    System.out.println(tajines.getName()+"  ¥"+tajines.getCost());

    }

}

 



 

 

 

  • 大小: 77.7 KB
  • 大小: 40.3 KB
  • 大小: 16.1 KB
0 请登录后投票
   发表时间:2014-03-26  

*****************************

一个有趣

 

但感觉出错的例子

*****************************

http://blog.csdn.net/hongqishi/article/details/7407595

 

作者的一些废话                                                                                                                                

      动态地给一个对象添加一些额外的职责。就增加功能来说,Decorator模式相比生成子类更为灵活。
  适用性
      1.在不影响其他对象的情况下,以动态、透明的方式给单个对象添加职责。
      2.处理那些可以撤消的职责。
      3.当不能采用生成子类的方法进行扩充时。
    参与者
      1.Component
      定义一个对象接口,可以给这些对象动态地添加职责。
      2.ConcreteComponent
      定义一个对象,可以给这个对象添加一些职责。
      3.Decorator
      维持一个指向Component对象的指针,并定义一个与Component接口一致的接口。
      4.ConcreteDecorator
      向组件添加职责。

 

执行效果,



 

这个肯定不是装饰者模式,先不说类图不一致,而且 类的顺序不能颠倒或重复。

但是蛮有趣的。

 

代码

public class DecoratorEntry {  
  
    public static void main(String[] args) {  
        OldLady oldLady = new OldLady(new Mother(new Maiden(new LittleGirl(new Fetus()))));  
        oldLady.look();  
    }  
}

 

public interface IFemale {  
    public void look();  
} 

 

//胎儿  
public class Fetus implements IFemale {  
  
    public void look() {  
        System.out.println("受孕之后,胎儿开始发育,就像一个花生那么大点.");  
    }  
}

 

//小女孩  
public class LittleGirl implements IFemale{  
    Fetus fetus;  
    public LittleGirl(Fetus fetus){  
        this.fetus = fetus;  
    }  
    public void look() {  
        fetus.look();  
        System.out.println("出生之后,小女孩子一点点长大,天真烂漫,清纯可爱.");  
    }  
}

 

//少女  
public class Maiden implements IFemale {  
    LittleGirl littleGirl;  
    public Maiden(LittleGirl littleGirl){  
        this.littleGirl = littleGirl;  
    }  
    public void look() {  
        littleGirl.look();  
        System.out.println("少女长大成人,楚楚动人,少女情怀总是诗.");  
    }  
  
} 

 

//母亲  
public class Mother implements IFemale {  
    Maiden maiden;  
    public Mother(Maiden maiden){  
        this.maiden = maiden;  
    }  
    public void look() {  
        maiden.look();  
        System.out.println("遇到喜欢的人坠入爱河,结婚生子,散发着成熟的魅力.");  
    }  
  
}  

 

//老妇人  
public class OldLady implements IFemale {  
    Mother mother;  
    public OldLady(Mother mother){  
        this.mother = mother;  
    }  
    public void look() {  
        mother.look();  
        System.out.println("子女渐渐长大,自己一天天老去,皱纹爬满脸庞,直至回归尘土.");  
    }  
  
}  

 

 

  • 大小: 19.9 KB
0 请登录后投票
   发表时间:2014-03-26  

******************************************

C# 的例子

 

还是饮料

******************************************

http://www.cnblogs.com/BoyXiao/archive/2010/05/04/1727383.html

(基本全抄)

首先来看一个例子:                                                            

 

比如,饮料可以分为很多种类,而这里我取一个咖啡,那么这个咖啡呢,有多种形式的,

比如有加糖了的咖啡,有加奶的咖啡,也有加热了的咖啡,也有加了冰块的咖啡。

而各个顾客的选择却是不同的,比如,有的客户是要加糖的咖啡,而有的客户却是要加冰的咖啡,

也就是需求是各种各样的,那么如何实现这种情况呢?

先来看一种愚笨的做法,那就是你可以通过继承来实现,


 

 

虽然上面的做法确实可以提供加糖加冰的咖啡,但是,这种方法也太拙劣了吧,

如果我要加糖和加热的咖啡呢?如果我还有更多的需求呢?

如果我新增饮料巧克力奶茶,然后其中也可以加糖,加奶,加热,加冰呢?

如果使用上面的方式来完成上面提到的种种需求的话?

My God !!!

其实从上面的例子中反映出来的问题是,

确实可以通过继承来实现扩展(比如,由咖啡扩展成了加了糖的咖啡),

但是并不一定说使用继承就可以达到弹性设计。

 

为了解决上面出现的问题,一种新的设计模式应运而生,那就是装饰者模式。         

 

装饰者模式动态的给一个对象添加一些责任(也就是功能),若要扩展功能的话,

装饰者提供了比继承更有弹性的替代方案,因为装饰者模式比生成子类更加灵活。

首先来看一下装饰者模式的结构图



 

 

下面就来解释装饰者模式了                                             

 

装饰者模式呢,其实可以看做是一种在已有功能上动态添加新的功能的一种方式,

在不用装饰者模式的前提下,如果要在已有的功能上添加新功能,一般都是可以使用继承的,

但是,继承的缺点呢,在上面的咖啡的例子中也暴露的很明显,同时,使用继承的话,

添加功能不是动态的,因为子类完全继承了父类,

而使用装饰者模式的话,您可以在客户端按照需求一个一个的包装对象,

通过包装对象来添加新功能,

这样便实现了动态添加新功能,

 

比如,我可以对 Component 通过 ConcreteDecoratorA 来包装一个 State 状态,

或者是通过 ConcreteDecoratorB 来包装一个新的行为(功能)Behavior ,

这样便实现了功能的动态添加。

又比如上面的咖啡的例子,就可以使用装饰者模式来实现,我先是对咖啡使用加糖来包装,

这样就可以得到加糖的咖啡,而后再使用冰来包装加了糖的咖啡,这样就可以得到加了糖,

并且加了冰的咖啡。

其实光这样说是感受不到多少装饰者模式带来的优点的

下面就将上面提到的咖啡这个例子写出来看看吧,这样理解起来便清晰多了

 

 

测试一下,



 

using System;
using Decorator;

namespace DecoratorTest
{
    class Program
    {
        static void Main(string[] args)
        {
            Coffee coffee = new Coffee("咖啡一号");
            //给咖啡加糖,也就是使用糖来装饰咖啡
            Sugar sugar = new Sugar(coffee);
            //给加了糖的咖啡加热,也就是使用加热来装饰咖啡
            Hot hot = new Hot(sugar);
            //显示出当前咖啡的状态
            hot.ShowDrink();

            Console.WriteLine();

            coffee = new Coffee("咖啡二号");
            sugar = new Sugar(coffee);
            //给加了糖的咖啡加奶
            Milk milk = new Milk(sugar);
            //给加了糖和奶的咖啡加冰块
            Ice ice = new Ice(milk);
            ice.ShowDrink();

            Console.ReadLine();
        }
    }
}

 

 

  • 大小: 54.4 KB
  • 大小: 120 KB
  • 大小: 114.8 KB
  • 大小: 8.6 KB
0 请登录后投票
   发表时间:2014-03-26  

**********************************

还是吃饭

 

装饰者模式 用 吃饭、喝饮料来形象化 是非常容易理解的

************************************

http://www.cnblogs.com/zzy0471/articles/1324762.html

 

这个作者居然没有说废话,直接就是代码



 

namespace DecoratePattern
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.Write("顾客:来一盘饺子\r\n");
            Console.Read(); Console.Read();

            Dumpling dumpling = new Dumpling();
            Console.Write("服务生:"+dumpling.Describe()+" "+dumpling.Cast().ToString()+"元人民币\r\n");
            Console.Read(); Console.Read();

            Console.Write("顾客:等等,再给我拿点醋\r\n");
            Console.Read(); Console.Read();

            Vinegar vingarDumpling = new Vinegar(dumpling);//用“醋”修饰饺子
            Console.Write("服务生:" + vingarDumpling.Describe() + " " + vingarDumpling.Cast().ToString() + "元人民币\r\n");
            Console.Read(); Console.Read();

            Console.Write("顾客:什么,太黑了,醋也要钱,那酱油我也要点\r\n");
            Console.Read(); Console.Read();

            Soy soyVingarDumpling = new Soy(vingarDumpling);//用“酱油”修饰那盘加了醋的饺子
            Console.Write("服务生:" + soyVingarDumpling.Describe() + " " + soyVingarDumpling.Cast().ToString() + "元人民币\r\n");
            Console.Read(); Console.Read();

            Console.Write("顾客:啊,酱油不是和醋算在一起的?\r\n");
            Console.Read(); Console.Read();
            Console.Write("----------顾客吃光了饺子----------\r\n");
            Console.Read(); Console.Read();

            Console.Write("顾客:也太少了点,再来一盘馄饨吧,还有加醋和酱油\r\n");
            Console.Read(); Console.Read();

            Soy soyVingarWonton=new Soy(new Vinegar(new Wonton()));
            Console.Write("服务生:" + soyVingarWonton.Describe() + " " + soyVingarWonton.Cast().ToString() + "元人民币\r\n");
            Console.Read(); Console.Read();

            Console.Write("顾客:cow,再也不来这里吃饭了\r\n");
            Console.Read(); Console.Read();


        }
    }

//省略其它类
}

 

 

  • 大小: 9.4 KB
0 请登录后投票
   发表时间:2014-03-27  

****************************

弯弯的月亮

 

****************************

http://www.dotblogs.com.tw/pin0513/archive/2010/01/04/12779.aspx

 

台湾人对 head first design pattern 中 装饰者模式的 理解,

基本就是翻译,并且把喝咖啡 改成 吃早餐。

 

不过,非常详细。

 

   1: //寫各種配料的程式碼

   2:    //實踐具體的裝飾者

   3:    public class cheese : CondimentDecorator  //起司是一個裝飾者,所以繼承自抽象的裝飾者類別

   4:    {

   5:        Breakfast breakfast;
   6:  
   7:        public cheese(Breakfast b)

   8:        {

   9:            breakfast = b;

  10:        }

  11:  
  12:        public override string getDescription()

  13:        {

  14:            return breakfast.getDescription() + ",起司";

  15:        }
  16:  
  17:        public override double cost()

  18:        {

  19:            return 5 + breakfast.cost();

  20:        }

  21:    }

  22:  
  23:  

  24:  
  25:    public class ham : CondimentDecorator  //火腿是一個裝飾者,所以繼承自抽象的裝飾者類別

  26:    {

  27:        Breakfast breakfast;

  28:  
  29:        public ham(Breakfast b)

  30:        {

  31:            breakfast = b;

  32:        }

  33:  
  34:        public override string getDescription()

  35:        {

  36:            return breakfast.getDescription() + ",火腿";

  37:        }

  38:  
  39:        public override double cost()

  40:        {

  41:            return 10 + breakfast.cost();

  42:        }

  43:    }

  44:    //玉米或生菜、其他配料,可依上兩例實作程式碼。

  45:  

 

 

 

0 请登录后投票
   发表时间:2014-03-27  

********************************

电锯惊魂

 

设计模式中渗透着暴力文化

********************************

http://www.cs.sjsu.edu/~pearce/modules/cases/uw/decorator.htm

先看,执行效果,


 

流程做得很棒,而且循序渐进,感觉是目前为止最不错的 装饰者模式 资料,压轴绰绰有余。

 

背景

Ultimate Warrior (UW) is a prototype of a computer game in which characters battle each other in tournaments for the title of Ultimate Warrior.

 

When an attacker attacks a victim, the health of each is decremented.

A character is killed when his/her health reaches 0.

An implementation of UW 1.0 can be found in:

 

期间 是如何 想到 利用 装饰者模式 的(很详细) 省略,

 


 

One of the advantages of the Decorator Pattern is that decorators can form chains that terminate with the non-decorator implementation.

For example, the Crusty character in Tournament.main is actually a chain of decorators. Here's how he attacks character c2:



 

This means characters can mix and match their attacking methods. This isn't possible with the Strategy Pattern.

The Strategy Pattern allows us to change the guts of an object. The Decorator Pattern allows us to change the skin of an object.

 

 

But we cheated! In order to use the Decorator Pattern we needed to make several minor modifications in the Character class. What were they?

What do we do when a class like Character provides the required functionality, but doesn't implement the required interface, ICharacter?

The answer to this common problem is to use the Adapter Design Pattern.

An adapter implements the required interface by delegating to or calling methods inherited from the adaptee.

Here's our design using a class adapter:



 

  • 大小: 29.2 KB
  • 大小: 38.6 KB
  • 大小: 28.5 KB
  • 大小: 50.3 KB
0 请登录后投票
论坛首页 Java企业应用版

跳转论坛:
Global site tag (gtag.js) - Google Analytics