设计模式:装饰者模式

  装饰者模式么,在生活中我们是经常接触的。比如像我们这么快节奏的生活,好多都是早上去买煎饼。一般我们会这么说:“来一个粗粮煎饼,加两个鸡蛋加一根肠

或者:“来个山东煎饼,只加土豆丝”等等。“煎饼” 就是这个么个有弹性的对象,面饼是不变的,其它的像鸡蛋,肠什么的者在装饰面饼。这个也是我们编程时的一个设计原则

对扩展开放,对修改关闭。(在最后我会给出C# 和C++ 两种代码示例)

  装饰者模式和“煎饼”差不多,它可以动态地将责任("鸡蛋“、“肠”...)附加到对象(”煎饼“)上。若要扩展功能,装饰者提供了比继承更有弹性的替代方案。

  我们看一下装饰者模式的类图:

看这个类图我们可以总结一下装饰者模式的特点:

 1.被装饰对象和装饰品有相同的接口。这样装饰品和被装饰对象就可以相互交互。

 2.装饰品对象包含一个被装饰对象的引用。

让我们做一个煎饼吧:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace DecoratorDemo
{
    [Flags]
    public enum BcType
    {
        None = 0,       // 0
        SdCakeType,     // 山东煎饼
        EggType,        // 蛋
        SausageType     // 肠        
    }


    public interface IBatterCake
    {
        List<BcType> Type();
        double Coast();
    }

    public class NoneBatterCake : IBatterCake
    {
        public List<BcType> Type()
        {
            return new List<BcType> {BcType.None};
        }

        public double Coast()
        {
            return 0;
        }
    }

    /// <summary>
    /// 山东煎饼
    /// </summary>
    public class SdBatterCake : IBatterCake
    {
        public List<BcType> Type()
        {
            return new List<BcType> {BcType.SdCakeType};
        }

        public double Coast()
        {
            return 3.5;
        }
    }

    /// <summary>
    /// 鸡蛋
    /// </summary>
    public class Egg : IBatterCake
    {
        private readonly IBatterCake _batterCake = new NoneBatterCake();

        public Egg(IBatterCake mMoney)
        {
            _batterCake = mMoney;
        }

        public List<BcType> Type()
        {
            List<BcType> types = _batterCake.Type();
            types.AddRange(new List<BcType>() { BcType.EggType });
            return types;
        }

        public double Coast()
        {
            return 1 + _batterCake.Coast();
        }
    }

    /// <summary>
    /// 肠
    /// </summary>
    public class Sausage : IBatterCake
    {
        private readonly IBatterCake _batterCake = new NoneBatterCake();

        public Sausage(IBatterCake mMoney)
        {
            _batterCake = mMoney;
        }

        public List<BcType> Type()
        {
            List<BcType> types = _batterCake.Type();
            types.AddRange(new List<BcType>() { BcType.SausageType });
            return types;
        }

        public double Coast()
        {            
            return 2 + _batterCake.Coast();
        }
    }

    /// <summary>
    /// 计算花费
    /// </summary>
    public class CalculateMoney : IBatterCake
    {
        private readonly IBatterCake _batterCake = new NoneBatterCake();
        public CalculateMoney(IBatterCake mMoney)
        {
            _batterCake = mMoney;
        }

        public List<BcType> Type()
        {
            return new List<BcType> {BcType.None};
        }

        public string Description()
        {
            List<BcType> types = _batterCake.Type();

            int eggs = 0, sausage = 0,battercakies = 0;
            for (int index = 0, count = types.Count(); index < count; index++)
            {
                if (types[index] == BcType.SdCakeType)
                    battercakies++;

                if (types[index] == BcType.EggType)
                    eggs++;

                if (types[index] == BcType.SausageType)
                    sausage++;
            }
            StringBuilder description = new StringBuilder();
            if (battercakies > 0)
                description.Append("煎饼").Append(battercakies).Append("个").Append("\n");

            if (eggs > 0)
                description.Append("鸡蛋").Append(eggs).Append("个").Append("\n");

            if (sausage > 0)
                description.Append("烤肠").Append(sausage).Append("个").Append("\n");

            return description.ToString();
        }

        public double Coast()
        {
            return _batterCake.Coast();
        }
    }


    public class Program
    {
        private static void Main(string[] args)
        {
            // 一个山东煎饼
            IBatterCake sdBatterCake = new SdBatterCake();
            // 一个鸡蛋
            IBatterCake egg1 = new Egg(sdBatterCake);
            // 再来个鸡蛋
            IBatterCake egg2 = new Egg(egg1);
            // 再来个肠
            IBatterCake sausage = new Sausage(egg2);

            CalculateMoney calculateMoney = new CalculateMoney(sausage);

            Console.WriteLine(calculateMoney.Description());
            Console.WriteLine("共花费 : {0}", calculateMoney.Coast());
            Console.ReadLine();
        }
    }
}

看一下结果:

c++代码:

#include <iostream>
#include <string>
#include <list>

using namespace std;
enum class BcType
{
    None = 0,        // 0
    SdCakeType,        // 山东煎饼
    EggType,        // 蛋
    SausageType        // 肠    
};

class IBatterCake
{
public:
    virtual ~IBatterCake() {}
    virtual list<BcType> type() const = 0;
    virtual double coast() const = 0;
};

class NoneBatterCake : public IBatterCake
{
public:
    NoneBatterCake() {}
    virtual ~NoneBatterCake() { }

    virtual list<BcType> type() const override
    {
        return list<BcType>{ BcType::None };
    }

    virtual double coast() const override
    {
        return 0;
    }

};

/// <summary>
/// 山东煎饼
/// </summary>
class SdBatterCake : public IBatterCake
{
public:
    SdBatterCake() {}
    virtual ~SdBatterCake() { }
    virtual list<BcType> type() const override
    {
        return list<BcType>{ BcType::SdCakeType };
    }

    virtual double coast() const override
    {
        return 3.5;
    }
};

/// <summary>
/// 鸡蛋
/// </summary>
class Egg : public IBatterCake
{
public:
    Egg() {}    
    Egg(IBatterCake *batter)
    {
        if (m_batter)
            delete m_batter;

        m_batter = batter;
    }

    ~Egg()
    {
        delete m_batter;
    }

    virtual list<BcType> type() const override
    {
        list<BcType> ls = m_batter->type();
        ls.push_back(BcType::EggType);
        
        return ls;
    }

    virtual double coast() const override
    {
        return 3.5 + m_batter->coast();
    }

private:
    IBatterCake *m_batter = new NoneBatterCake();
};

/// <summary>
/// 肠
/// </summary>
class Sausage: public IBatterCake
{
public:
    Sausage() {}    
    Sausage(IBatterCake *batter)
    {
        if (m_batter)
            delete m_batter;

        m_batter = batter;
    }

    ~Sausage()
    {
        delete m_batter;
    }

    virtual list<BcType> type() const override
    {
        list<BcType> ls = m_batter->type();
        ls.push_back(BcType::SausageType);
        
        return ls;
    }

    virtual double coast() const override
    {
        return 2 + m_batter->coast();
    }

private:
    IBatterCake *m_batter = new NoneBatterCake();
};


/// <summary>
/// 计算花费
/// </summary>
class CalculateMoney : public IBatterCake
{
public:    
    CalculateMoney(IBatterCake *batter)
    {
        if (m_batter)
            delete m_batter;

        m_batter = batter;
    }
    ~CalculateMoney()
    {
        delete m_batter;
    }

    virtual list<BcType> type() const override
    {
        return list<BcType>{ BcType::None };
    }

    string description()
    {
        list<BcType> types = m_batter->type();

        int eggs = 0, sausage = 0, battercakies = 0;

        for (const BcType &type : types) {
            if (type == BcType::SdCakeType)
                battercakies++;

            if (type == BcType::EggType)
                eggs++;

            if (type == BcType::SausageType)
                sausage++;
        }

        string description;
        if (battercakies > 0)
            description.append("煎饼").append(std::to_string(battercakies)).append("个\n");

        if (eggs > 0)
            description.append("加").append(std::to_string(eggs)).append("个鸡蛋\n");

        if (sausage > 0)
            description.append("加").append(std::to_string(sausage)).append("个烤肠\n");

        return description;
    }

    virtual double coast() const override
    {
        return m_batter->coast();
    }
private:
    IBatterCake *m_batter = new NoneBatterCake();
};

int main()
{
    IBatterCake *sdBatterCake = new SdBatterCake();

    IBatterCake *egg1 = new Egg(sdBatterCake);
    
    IBatterCake *egg2 = new Egg(egg1);

    IBatterCake *sausage = new Sausage(egg2);

    CalculateMoney *calculateMoney = new CalculateMoney(sausage);
    cout << calculateMoney->description();

    delete calculateMoney;
    cin.get();
    return 0;
}

结果是一样的

以下情况使用Decorator模式

1. 需要扩展一个类的功能,或给一个类添加附加职责。

2. 需要动态的给一个对象添加功能,这些功能可以再动态的撤销。

3. 需要增加由一些基本功能的排列组合而产生的非常大量的功能,从而使继承关系变的不现实。

4. 当不能采用生成子类的方法进行扩充时。一种情况是,可能有大量独立的扩展,为支持每一种组合将产生大量的子类,使得子类数目呈爆炸性增长。另一种情况可能是因为类定义被隐藏,或类定义不能用于生成子类。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏LhWorld哥陪你聊算法

Hadoop源码篇--Reduce篇

Reduce文件会从Mapper任务中拉取很多小文件,小文件内部有序,但是整体是没序的,Reduce会合并小文件,然后套个归并算法,变成一个整体有序的文件。

28210
来自专栏SeanCheney的专栏

《Pandas Cookbook》第09章 合并Pandas对象

25510
来自专栏码匠的流水账

聊聊storm TridentTopology的构建

storm-core-1.2.2-sources.jar!/org/apache/storm/trident/TridentTopology.java

19330
来自专栏一个会写诗的程序员的博客

禅与 JavaScript 编程艺术, Zen and The Art of JavaScript Programming禅与 JavaScript 编程艺术

Zen and The Art of JavaScript Programming

12610
来自专栏码匠的流水账

聊聊storm TridentTopology的构建

storm-core-1.2.2-sources.jar!/org/apache/storm/trident/TridentTopology.java

11920
来自专栏菩提树下的杨过

恶心的0.5四舍五入问题

四舍五入是财务类应用中常见的需求,按中国人的财务习惯,遇到0.5统一向上进位,但是c#与java中默认的却不是这样。 见c#代码: 1 stat...

226100
来自专栏恰童鞋骚年

数据结构基础温故-1.线性表(下)

在上一篇中,我们了解了单链表与双链表,本次将单链表中终端结点的指针端由空指针改为指向头结点,就使整个单链表形成一个环,这种头尾相接的单链表称为单循环链表,简称循...

10720
来自专栏高性能服务器开发

(三)dict哈希结构3

/* This function performs just a step of rehashing, and only if there are * no...

28680
来自专栏函数式编程语言及工具

SDP(9):MongoDB-Scala - data access and modeling

    MongoDB是一种文件型数据库,对数据格式没有硬性要求,所以可以实现灵活多变的数据存储和读取。MongoDB又是一种分布式数据库,与传统关系数据库不同...

39640
来自专栏GIS讲堂

geotools中shp和geojson格式的相互转换

1K30

扫码关注云+社区

领取腾讯云代金券