• 设计模式之装饰者模式


    定义

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

    设计原则

    类应该对扩展开发,对修改关闭。

    第一次设计

    现在我们有一家饮料店,下面是它最开始的设计。
    Beverage是一个抽象类,店内所有提供的饮料都要继承自此类。并且每个子类都要实现cost()来返回饮料的价格。

    很明显,这样使得维护变成了一个噩梦。

    第二次设计

    我们从它的基类入手,给每一个调料代表一个bool值,cost()不再是一个抽象方法,我们在基类中提供它的实现,然后根据调料的布尔值计算它的价格。
    在子类中,覆盖过的cost()会扩展超类的功能,把指定的饮料类型的价钱也加进来(同时加上父类cost()的总价)。
    下面让我们看看这样设计的问题:
    • 调料价格改变需要更改现有代码
    • 出现新的调料,需要加上新的方法和修改cost()方法。
    • 如果顾客需要双份调料怎么办

    开放-关闭原则

    设计原则:类应该对扩展开放,对修改关闭。
    我们的目标是允许类容易扩展,在不修改现有代码的情况下,就可搭配新的行为。这样设计具有弹性可以应对改变,可以接受新的功能来应对改变的需求。

    第三次设计

    下面开始我们的设计:
    我们以饮料为主体,然后在运行时以调料来装饰饮料。
    现在我们需要一杯摩卡和奶泡深焙咖啡。

    1.拿一个深焙咖啡(DarkRoast)对象

    以DarkRoast对象开始。DarkRoast继承自Beverage,并且有一个计算饮料价钱的cost()方法。

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

    建立一个Mocha对象,并用它将DarkRoast对象包起来。
    Mocha对象是一个装饰者,它的类型反映了它所装饰的对象(这里就是Bevarage)。
    反映:就是两个类型一致。
    Mocha也有一个cost()方法。通过多态,也可以把Mocha所包裹的任何Bevarage当成Bevarage。

    3.以奶泡(Whip)对象装饰它

    建立一个Whip装饰者,并用它将Mocha对象包起来。DarkRoast继承自Bevarage,且有一个cost()方法,用来计算饮料价格。
    Whip是一个装饰者,它也反映了DarkRoast类型,并包括cost()方法。

    被Mocha和Whip包起来的DarkRoast对象仍然是一个Bevarage,仍然可以具有DarkRoast的一切行为,包括调用它的cost()方法。

    4.调用cost()方法,并依赖委托将调料的价钱加上去

     现在到了算钱的时候。通过调用最外围装饰者(Whip)的cost()就可以了。Whip对象的cost()会先委托它的装饰的对象(Mocha)计算出价钱,然后再加上奶泡的价钱。

    定义装饰者模式

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

    实现

    类图

    Beverage

    public abstract class Beverage//抽象类
    {
    public string description = "Unknow Beverage";
    public virtual string getDescription()//已经实现
    {
    return description;
    }
    public abstract double cost();//必须在子类中实现
    }

    调料抽象类

    public abstract class ComdimentDecorator : Beverage////抽象装饰者
    {
    public abstract string getDescription();//所有的调料都必须重新实现此方法
    }

    饮料代码

    public class Espresso : Beverage//让Espresso扩展自Beverage,因为Espresso是一种饮料
    {
    public override string getDescription()//设置饮料的描述
    {
    return "Espresso";
    }
    public override double cost()//计算Espresso价格,现在不管调料价格
    {
    return 1.99;
    }
    }
    public class HouseBlend : Beverage//具体组件
    {
    public override string getDescription()
    {
    return "House Blend";
    }
    public override double cost()
    {
    return 0.89;
    }
    }

    调料代码

    public class Mocha : ComdimentDecorator//装饰者
    {
    private Beverage beverage;//用一个实例变量记录饮料,也就是被装饰者
    public Mocha(Beverage beverage)//把饮料当作构造器的参数,在由构造器将此饮料记录在实例变量中
    {
    this.beverage = beverage;
    }
    public override double cost()
    {
    return 0.2 + beverage.cost();
    }
    public override string getDescription()
    {
    return beverage.getDescription() + ",Moche";
    }
    }

    测试

    static void Main(string[] args)
    {
    Beverage beverage = new Espresso();
    beverage = new Mocha(beverage);
    Console.WriteLine(beverage.getDescription() + "$" + beverage.cost());
    
    Beverage beverage2 = new Espresso();//制造一个Espresso对象
    beverage2 = new Mocha(beverage2);//用Mocha装饰它
    beverage2 = new Mocha(beverage2);//用第二杯Mocha装饰它
    beverage2 = new Whip(beverage2);//用Whip装饰它
    Console.WriteLine(beverage2.getDescription() + "$" + beverage2.cost());
    
    Beverage beverage3 = new HouseBlend();
    beverage3 = new Soy(beverage3);
    beverage3 = new Mocha(beverage3);
    beverage3 = new Whip(beverage3);
    beverage3.getDescription();
    Console.WriteLine(beverage3.getDescription() + "$" + beverage3.cost());
    Console.ReadLine();
    }
  • 相关阅读:
    MySql
    Docker
    达观数据
    Python面试题
    用Python构造ARP请求、扫描、欺骗
    git上传简单的命令行分析
    vue2自定义指令的作用
    自定义指令详解 vue
    文档打印 js
    通过Export2Zip实现表格内容下载成为excel文件
  • 原文地址:https://www.cnblogs.com/Tan-sir/p/8205368.html
Copyright © 2020-2023  润新知