本文作者:包子也沉默

Head First设计模式——装饰者模式

包子也沉默 3年前 (2019-10-25) ( 10-25 ) 1756 0条评论
摘要: dDescription(){Console.WriteLine("深焙咖啡");}publicoverridefloatCost(){Milk=true;Suger=true;return1.1f+base.Cost();}}     这种方式比之前的继承的确好了许多,不过还是有如下几个问题:1、调料价格改变会使我们改变现有代码。2、需要添加新的

前言:对于设计模式我们有时候在想是否有必要,因为实际开发中我们没有那么多闲工夫去套用这么多设计模式,也没有必要为了模式而模式。

quot;c/=a的值为:",c)c=2c%=aprint("c%=a的值为:",c)c**=aprint("c**=a的值为:",c)c//=apr

通常这些模式会引入新的抽象层,增加代码的复杂度,但是当我们掌握了这些设计模式,

sBeverage{//牛奶publicboolMilk{get;set;}//糖publicboolSuger{get;set;}//摩卡publicboolMocha{get;set;}publi

在系统中比较棘手或者需要以后修改扩展的地方采用了合适的设计模式会让我们的系统易于扩展维护甚至工作变得轻松很多。

为+=是代表改变了变量,相当于重新生成了一个变量,把操作后的结果赋予这个新生成的变量。但是在Python中,变量是以内容为基准而不是像Java中以变量名为基准,所以只要你的数字内容是5,不管你起什么名

对于这一点我深有体会,有时候设计的比较好的功能模块在后来客户改变需求的时候变得很容易且方便添加修改。

咖啡饮品价格也不一样。咖啡是需要按照基础价格+调料组合计算价格的,那按照继承的方式我们抽象一个饮料基类Beverage,Beverage拥有Cost抽象方法用于计算价格。其他咖啡饮品继承Beverag

但是如果比较糟糕偷懒的方式会让我们对自己的代码修改变得害怕,害怕客户提需求,害怕去动自己的代码。

tion()+"+Suger";}publicoverridefloatCost(){returnbeverage.Cost()+2;}}编写测试结果:   总结装饰者模

所以对于框架或者这些设计模式我们可以不用过度使用,但是要用的时候能有储备或者脑袋里面会闪现出对应的解决方案。

print("c//=a的值为:",c)运行结果如下:c=a+b的值为:30c+=a的值为:40c*=a的值为:400c/=a的值为:40.0c%=a的值为:2c**=a的值为:1

这样不光会提升我们的编码兴趣对我们开发的产品也更有信心。

licabstractvoidDescription();publicvirtualfloatCost(){floatprice=0;if(Milk){price+=1;}if(Suger){pric

本篇就聚焦Head First设计模式中的装饰者模式进行学习和总结。

什么名字,这个变量的ID是相同的,同时也就说明了Python中一个变量可以以多个名称访问。这样的设计逻辑决定了Python中数字类型的值是不可变的,例如:a和b都是5,当你改变了a时,b也会跟着变,这

咖啡店结算案例

咖啡店卖各种咖啡,根据调料不一样就会有不同价格的咖啡饮品可选,那么对应的咖啡饮品价格也不一样。

age实现Cost方法根据自己的调料计算价格。publicabstractclassBeverage{publicabstractvoidDescription();publicabstractflo

咖啡是需要按照基础价格+调料组合计算价格的,那按照继承的方式我们抽象一个饮料基类Beverage,Beverage拥有Cost抽象方法用于计算价格。

者模式说明:动态地将责任附加到对象上。若要扩展功能,装饰者提供了比继承更有弹性的替代方案。画出本例中装饰者模式类图以便理解和记忆 人生苦短,我选Python前文传送门小白学Python(1)

其他咖啡饮品继承Beverage实现Cost方法根据自己的调料计算价格。

有相同的标识其实这个输出结果可以看出来,在Python,如果两个数值一样的变量,Python并不会在内存中重新开辟内存空间,而是会复用已有的内存空间。示例代码本系列的所有代码小编都会放在代码管理仓库G

    public abstract class Beverage {
        public abstract void Description();
        public abstract float Cost();
    }

    public class DarkRoast : Beverage
    {
        public override void Description()
        {
            //深焙咖啡
        }
        public override float Cost()
        {
           //各种调料价格计算
        }
    }

对于继承的方式,其他子类都要去实现一遍价格计算无法复用,如果子类比较多那么继承的子类会“爆炸”,新增加子类对于我们来说就是一项重复工作。

ger,用变量记录被装饰者,在构造函数里面设置被装饰者。publicclassMilk:CondimentDecorator{//用变量记录被装饰者Beveragebeverage;publicMil

如果说某一种调料的价格变化我们还得去每个子类里面改变价格。

)if"a"notinstr:print("a不在字符串str中")else:print("a在字符串str中")输出结果:a在字符串str中a在字符串

那我们针对变化的调料部分是不是让他们按照实际需求组合,在子类中确定自己加哪些调料就行了,似乎这种方式会减少重写和维护难度。

erage基类,GetDescription返回描述,Cost由子类自己实现定价。 publicabstractclassBeverage{stringdescription="Unkonw

按照这个思路将Beverage改造,将是否有调料定义成变量,然后Cost方法不再抽象而是提供实现。

们其实是Python教程,就不扯离散数学了,有兴趣的同学请自行百度吧。and:逻辑“与”运算符,只有当and两边都是真,结果才是真or:逻辑“或”运算符,只要当or一边的是真的,结果就是真not:逻辑

    public abstract class Beverage
    {
        //牛奶
        public bool Milk { get; set; }
        //糖
        public bool Suger { get; set; }
        //摩卡
        public bool Mocha { get; set; }
        public abstract void Description();
        public virtual float Cost()
        {
            float price = 0;
            if (Milk)
            {
                price += 1;
            }
            if (Suger)
            {
                price += 2;
            }
            if (Mocha)
            {
                price += 3;
            }
            return price;
        }
    }

    public class DarkRoast : Beverage
    {
        public override void Description()
        {
            Console.WriteLine("深焙咖啡");
        }

        public override float Cost()
        {
            Milk = true;
            Suger = true;
            return 1.1f+base.Cost();
        }
    }

  

 

blicoverridefloatCost(){Milk=true;Suger=true;return1.1f+base.Cost();}}     这种方式比之前的继承

assMilk:CondimentDecorator{//用变量记录被装饰者Beveragebeverage;publicMilk(Beveragebeverage){this.beverage=be

的变量,Python并不会在内存中重新开辟内存空间,而是会复用已有的内存空间。示例代码本系列的所有代码小编都会放在代码管理仓库Github和Gitee上,方便大家取用。示例代码-Github示例代码-

指定的序列中没有找到值返回True,否则返回False。前面的文章尚未介绍列表和元祖,下面的示例仅使用字符串来做演示:str="asdfghjkl"if"a"ins

介绍了算术运算符和比较运算符。本篇我们继续介绍其余的几个:赋值运算符逻辑运算符成员运算符身份运算符赋值运算符Python中,使用=号表示赋值。当然,Python还提供了其他赋值方式,直接帮我们简化了写

atCost(){//各种调料价格计算}}对于继承的方式,其他子类都要去实现一遍价格计算无法复用,如果子类比较多那么继承的子类会“爆炸”,新增加子类对于我们来说就是一项重复工作

等效于c=ca/=除法赋值运算符,c/=a等效于c=c/a%=取模赋值运算符,c%=a等效于c=c%a**=幂赋值运算符,c=a等效于c=ca//=取整除赋值运算符,c//=a等效于c=c//a上面的

 

blicoverridefloatCost(){Milk=true;Suger=true;return1.1f+base.Cost();}}     这种方式比之前的继承

assMilk:CondimentDecorator{//用变量记录被装饰者Beveragebeverage;publicMilk(Beveragebeverage){this.beverage=be

的变量,Python并不会在内存中重新开辟内存空间,而是会复用已有的内存空间。示例代码本系列的所有代码小编都会放在代码管理仓库Github和Gitee上,方便大家取用。示例代码-Github示例代码-

指定的序列中没有找到值返回True,否则返回False。前面的文章尚未介绍列表和元祖,下面的示例仅使用字符串来做演示:str="asdfghjkl"if"a"ins

介绍了算术运算符和比较运算符。本篇我们继续介绍其余的几个:赋值运算符逻辑运算符成员运算符身份运算符赋值运算符Python中,使用=号表示赋值。当然,Python还提供了其他赋值方式,直接帮我们简化了写

atCost(){//各种调料价格计算}}对于继承的方式,其他子类都要去实现一遍价格计算无法复用,如果子类比较多那么继承的子类会“爆炸”,新增加子类对于我们来说就是一项重复工作

等效于c=ca/=除法赋值运算符,c/=a等效于c=c/a%=取模赋值运算符,c%=a等效于c=c%a**=幂赋值运算符,c=a等效于c=ca//=取整除赋值运算符,c//=a等效于c=c//a上面的

 

blicoverridefloatCost(){Milk=true;Suger=true;return1.1f+base.Cost();}}     这种方式比之前的继承

assMilk:CondimentDecorator{//用变量记录被装饰者Beveragebeverage;publicMilk(Beveragebeverage){this.beverage=be

的变量,Python并不会在内存中重新开辟内存空间,而是会复用已有的内存空间。示例代码本系列的所有代码小编都会放在代码管理仓库Github和Gitee上,方便大家取用。示例代码-Github示例代码-

指定的序列中没有找到值返回True,否则返回False。前面的文章尚未介绍列表和元祖,下面的示例仅使用字符串来做演示:str="asdfghjkl"if"a"ins

介绍了算术运算符和比较运算符。本篇我们继续介绍其余的几个:赋值运算符逻辑运算符成员运算符身份运算符赋值运算符Python中,使用=号表示赋值。当然,Python还提供了其他赋值方式,直接帮我们简化了写

atCost(){//各种调料价格计算}}对于继承的方式,其他子类都要去实现一遍价格计算无法复用,如果子类比较多那么继承的子类会“爆炸”,新增加子类对于我们来说就是一项重复工作

等效于c=ca/=除法赋值运算符,c/=a等效于c=c/a%=取模赋值运算符,c%=a等效于c=c%a**=幂赋值运算符,c=a等效于c=ca//=取整除赋值运算符,c//=a等效于c=c//a上面的

这种方式比之前的继承的确好了许多,不过还是有如下几个问题:

多。对于这一点我深有体会,有时候设计的比较好的功能模块在后来客户改变需求的时候变得很容易且方便添加修改。但是如果比较糟糕偷懒的方式会让我们对自己的代码修改变得害怕,害怕客户提需求,害怕去动自己的代码。

1、调料价格改变会使我们改变现有代码。

GetDescription()+"+Milk";}publicoverridefloatCost(){returnbeverage.Cost()+1;}}publicclassSuger:Condi

2、需要添加新的调料,我们就需要添加新的方法并改变Beverage基类的Cost方法。

isb:print("a和b有相同的标识")else:print("a和b没有相同的标识")ifid(a)==id(b):print("a和b有相同的

3、某些调料可能并不适合其他饮品,例如茶还要继承这些不属于它的调料。

工作。如果说某一种调料的价格变化我们还得去每个子类里面改变价格。那我们针对变化的调料部分是不是让他们按照实际需求组合,在子类中确定自己加哪些调料就行了,似乎这种方式会减少重写和维护难度。按照这个思路将

4、如果顾客需要双份糖,Cost方法就会失效。

了写法,如+=,-=,没有编程经验的同学可能会比较懵逼。其实小编当年在学Java的时候第一次接触也是很懵的,这里其实无需懵逼,简单理解a+=1其实就是a=a+1。这只是一种简化写法。题外话(建:有编程

接下来我们就用"装饰者模式"来更好的设计该案例。

码。所以对于框架或者这些设计模式我们可以不用过度使用,但是要用的时候能有储备或者脑袋里面会闪现出对应的解决方案。这样不光会提升我们的编码兴趣对我们开发的产品也更有信心。本篇就聚焦HeadFirst设计

认识装饰者模式

这里有一个设计模式很重要的设计原则:类应该对扩展开放,对修改关闭(开闭原则)

dimentDecorator{//用变量记录被装饰者Beveragebeverage;publicSuger(Beveragebeverage){this.beverage=beverage;}pu

装饰模式同样遵循开闭原则。

相同的标识")else:print("a和b没有相同的标识")#修改变量b的值b=30ifaisb:print("a和b有相同的标识")else:pr

对于咖啡店来说,主体是饮料不变,变化的是调料。我们以饮料为主体,其他调料来“装饰”饮料。比如顾客想要加了Mocha(摩卡)的 DarkRoast(深焙咖啡),我们要做的是:

turn1.1f;}}装饰类我们定义一个抽象基类Condiment(调料)并继承Beverage。为什么要定义一个抽象装饰基类,因为装饰类可能需要抽象出其他方法,而且因为我们用装饰类去装饰了被装饰者后

1、制造一个DarkRoast对象

alse)#True成员运算符成员运算符用来判断在指定的序列中有没有找到目标值,这个序列可以是字符串、列表和元祖。in:如果在指定的序列中找到值返回True,否则返回False。notin:如果在指定

2、以Mocha(摩卡)对象装饰它

该案例。认识装饰者模式这里有一个设计模式很重要的设计原则:类应该对扩展开放,对修改关闭(开闭原则)装饰模式同样遵循开闭原则。对于咖啡店来说,主体是饮料不变,变化的是调料。我们以饮料为主体,其他调料来&

3、调用Cost()方法,并依赖委托(非C# delegate,只是概念)将调料的价钱加上去

quot;c%=a的值为:",c)c**=aprint("c**=a的值为:",c)c//=aprint("c//=a的值为:",c)运行结果如下:c

利用装饰者模式

首先我们修改主体饮料Beverage基类,GetDescription 返回描述,Cost由子类自己实现定价。

者后我们本身也应该变成可已让别人装饰的类。所以装饰类继承CondimentDecorator,CondimentDecorator继承的是Beverage。此例暂未加入装饰类拥有的属性和方法publi

 

blicoverridefloatCost(){Milk=true;Suger=true;return1.1f+base.Cost();}}     这种方式比之前的继承

assMilk:CondimentDecorator{//用变量记录被装饰者Beveragebeverage;publicMilk(Beveragebeverage){this.beverage=be

的变量,Python并不会在内存中重新开辟内存空间,而是会复用已有的内存空间。示例代码本系列的所有代码小编都会放在代码管理仓库Github和Gitee上,方便大家取用。示例代码-Github示例代码-

指定的序列中没有找到值返回True,否则返回False。前面的文章尚未介绍列表和元祖,下面的示例仅使用字符串来做演示:str="asdfghjkl"if"a"ins

介绍了算术运算符和比较运算符。本篇我们继续介绍其余的几个:赋值运算符逻辑运算符成员运算符身份运算符赋值运算符Python中,使用=号表示赋值。当然,Python还提供了其他赋值方式,直接帮我们简化了写

atCost(){//各种调料价格计算}}对于继承的方式,其他子类都要去实现一遍价格计算无法复用,如果子类比较多那么继承的子类会“爆炸”,新增加子类对于我们来说就是一项重复工作

等效于c=ca/=除法赋值运算符,c/=a等效于c=c/a%=取模赋值运算符,c%=a等效于c=c%a**=幂赋值运算符,c=a等效于c=ca//=取整除赋值运算符,c//=a等效于c=c//a上面的

    public abstract class Beverage
    {
        string description = "Unkonwn Beverage";
        public virtual string GetDescription() {
            return description;
        }
        public abstract float Cost();
    }

深焙咖啡类

来“装饰”饮料。比如顾客想要加了Mocha(摩卡)的DarkRoast(深焙咖啡),我们要做的是:1、制造一个DarkRoast对象2、以Mocha(摩卡)对象装饰它3、调用C

    public class DarkRoast : Beverage
    {
        public DarkRoast() {
            description = "深焙咖啡";
        }
        public override float Cost()
        {
            return 1.1f;
        }
    }

装饰类我们定义一个抽象基类Condiment(调料)并继承Beverage。

:c=a+b的值为:30c+=a的值为:40c*=a的值为:400c/=a的值为:40.0c%=a的值为:2c**=a的值为:1024c//=a的值为:102逻辑运算符逻辑运算符有三种,分别是与、或、

为什么要定义一个抽象装饰基类,因为装饰类可能需要抽象出其他方法,而且因为我们用装饰类去装饰了被装饰者后 我们本身也应该变成可已让别人装饰的类。所以装饰类继承CondimentDecorator,CondimentDecorator继承的是Beverage。此例暂未加入装饰类拥有的属性和方法

    public abstract class CondimentDecorator:Beverage
    {
        
    }

我们再实现两个装饰类:Mik和Suger,用变量记录被装饰者,在构造函数里面设置被装饰者。

以以多个名称访问。这样的设计逻辑决定了Python中数字类型的值是不可变的,例如:a和b都是5,当你改变了a时,b也会跟着变,这当然不是我们希望的。Python中的赋值表达式可见下表:运算符描述=赋值

    public class Milk : CondimentDecorator
    {
        //用变量记录被装饰者
        Beverage beverage;
        public Milk(Beverage beverage) {
            this.beverage = beverage;
        }

        public override string GetDescription()
        {
            return beverage.GetDescription() + "+Milk";
        }
        public override float Cost()
        {
            return beverage.Cost() + 1;
        }
    }

    public class Suger : CondimentDecorator
    {
        //用变量记录被装饰者
        Beverage beverage;
        public Suger(Beverage beverage)
        {
            this.beverage = beverage;
        }

        public override string GetDescription()
        {
            return beverage.GetDescription() + "+Suger";
        }
        public override float Cost()
        {
            return beverage.Cost() + 2;
        }
    }

编写测试结果:

tclassBeverage{publicabstractvoidDescription();publicabstractfloatCost();}publicclassDarkRoast:Bever

 

blicoverridefloatCost(){Milk=true;Suger=true;return1.1f+base.Cost();}}     这种方式比之前的继承

assMilk:CondimentDecorator{//用变量记录被装饰者Beveragebeverage;publicMilk(Beveragebeverage){this.beverage=be

的变量,Python并不会在内存中重新开辟内存空间,而是会复用已有的内存空间。示例代码本系列的所有代码小编都会放在代码管理仓库Github和Gitee上,方便大家取用。示例代码-Github示例代码-

指定的序列中没有找到值返回True,否则返回False。前面的文章尚未介绍列表和元祖,下面的示例仅使用字符串来做演示:str="asdfghjkl"if"a"ins

介绍了算术运算符和比较运算符。本篇我们继续介绍其余的几个:赋值运算符逻辑运算符成员运算符身份运算符赋值运算符Python中,使用=号表示赋值。当然,Python还提供了其他赋值方式,直接帮我们简化了写

atCost(){//各种调料价格计算}}对于继承的方式,其他子类都要去实现一遍价格计算无法复用,如果子类比较多那么继承的子类会“爆炸”,新增加子类对于我们来说就是一项重复工作

等效于c=ca/=除法赋值运算符,c/=a等效于c=c/a%=取模赋值运算符,c%=a等效于c=c%a**=幂赋值运算符,c=a等效于c=ca//=取整除赋值运算符,c//=a等效于c=c//a上面的

 

blicoverridefloatCost(){Milk=true;Suger=true;return1.1f+base.Cost();}}     这种方式比之前的继承

assMilk:CondimentDecorator{//用变量记录被装饰者Beveragebeverage;publicMilk(Beveragebeverage){this.beverage=be

的变量,Python并不会在内存中重新开辟内存空间,而是会复用已有的内存空间。示例代码本系列的所有代码小编都会放在代码管理仓库Github和Gitee上,方便大家取用。示例代码-Github示例代码-

指定的序列中没有找到值返回True,否则返回False。前面的文章尚未介绍列表和元祖,下面的示例仅使用字符串来做演示:str="asdfghjkl"if"a"ins

介绍了算术运算符和比较运算符。本篇我们继续介绍其余的几个:赋值运算符逻辑运算符成员运算符身份运算符赋值运算符Python中,使用=号表示赋值。当然,Python还提供了其他赋值方式,直接帮我们简化了写

atCost(){//各种调料价格计算}}对于继承的方式,其他子类都要去实现一遍价格计算无法复用,如果子类比较多那么继承的子类会“爆炸”,新增加子类对于我们来说就是一项重复工作

等效于c=ca/=除法赋值运算符,c/=a等效于c=c/a%=取模赋值运算符,c%=a等效于c=c%a**=幂赋值运算符,c=a等效于c=ca//=取整除赋值运算符,c//=a等效于c=c//a上面的

 总结

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

在字符串str中")else:print("a在字符串str中")输出结果:a在字符串str中a在字符串str中身份运算符身份运算符用于比较两个对象的存储单元。is:is是判断

画出本例中装饰者模式类图以便理解和记忆

实现定价。 publicabstractclassBeverage{stringdescription="UnkonwnBeverage";publicvirtualstringGetDes

 

blicoverridefloatCost(){Milk=true;Suger=true;return1.1f+base.Cost();}}     这种方式比之前的继承

assMilk:CondimentDecorator{//用变量记录被装饰者Beveragebeverage;publicMilk(Beveragebeverage){this.beverage=be

的变量,Python并不会在内存中重新开辟内存空间,而是会复用已有的内存空间。示例代码本系列的所有代码小编都会放在代码管理仓库Github和Gitee上,方便大家取用。示例代码-Github示例代码-

指定的序列中没有找到值返回True,否则返回False。前面的文章尚未介绍列表和元祖,下面的示例仅使用字符串来做演示:str="asdfghjkl"if"a"ins

介绍了算术运算符和比较运算符。本篇我们继续介绍其余的几个:赋值运算符逻辑运算符成员运算符身份运算符赋值运算符Python中,使用=号表示赋值。当然,Python还提供了其他赋值方式,直接帮我们简化了写

atCost(){//各种调料价格计算}}对于继承的方式,其他子类都要去实现一遍价格计算无法复用,如果子类比较多那么继承的子类会“爆炸”,新增加子类对于我们来说就是一项重复工作

等效于c=ca/=除法赋值运算符,c/=a等效于c=c/a%=取模赋值运算符,c%=a等效于c=c%a**=幂赋值运算符,c=a等效于c=ca//=取整除赋值运算符,c//=a等效于c=c//a上面的

原文:https://www.cnblogs.com/SunSpring/p/11735959.html
分享到:

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏

发表评论

快捷回复:

评论列表 (有 0条评论, 1756人围观) 参与讨论