【问题标题】:Understand the "Decorator Pattern" with a real world example [closed]用一个真实的例子来理解“装饰器模式”[关闭]
【发布时间】:2016-08-16 00:13:43
【问题描述】:

我正在研究 GOF 中记录的 装饰器模式

请帮我理解装饰器模式。有人可以举一个在现实世界中有用的用例示例吗?

【问题讨论】:

标签: decorator design-patterns


【解决方案1】:

装饰器模式实现了动态添加的单一目标 对任何对象的责任。

考虑一个比萨店的案例。在比萨店,他们将出售几种比萨,他们还将在菜单中提供配料。现在想象这样一种情况,如果比萨店必须为比萨饼和配料的每种组合提供价格。即使有四种基本的比萨饼和 8 种不同的浇头,应用程序也会疯狂地维护所有这些比萨饼和浇头的具体组合。

装饰器模式来了。

根据装饰器模式,您将实现浇头作为装饰器,比萨饼将由这些浇头的装饰器装饰。实际上,每个顾客都想要他想要的配料,最终的账单金额将由基础比萨饼和额外订购的配料组成。每个浇头装饰师都会知道它正在装饰的比萨饼及其价格。 Topping 对象的GetPrice() 方法将返回披萨和浇头的累积价格。

编辑

这是上面解释的代码示例。

public abstract class BasePizza
{
    protected double myPrice;

    public virtual double GetPrice()
    {
        return this.myPrice;
    }
}

public abstract class ToppingsDecorator : BasePizza
{
    protected BasePizza pizza;
    public ToppingsDecorator(BasePizza pizzaToDecorate)
    {
        this.pizza = pizzaToDecorate;
    }

    public override double GetPrice()
    {
        return (this.pizza.GetPrice() + this.myPrice);
    }
}

class Program
{
    [STAThread]
    static void Main()
    {
        //Client-code
        Margherita pizza = new Margherita();
        Console.WriteLine("Plain Margherita: " + pizza.GetPrice().ToString());

        ExtraCheeseTopping moreCheese = new ExtraCheeseTopping(pizza);
        ExtraCheeseTopping someMoreCheese = new ExtraCheeseTopping(moreCheese);
        Console.WriteLine("Plain Margherita with double extra cheese: " + someMoreCheese.GetPrice().ToString());

        MushroomTopping moreMushroom = new MushroomTopping(someMoreCheese);
        Console.WriteLine("Plain Margherita with double extra cheese with mushroom: " + moreMushroom.GetPrice().ToString());

        JalapenoTopping moreJalapeno = new JalapenoTopping(moreMushroom);
        Console.WriteLine("Plain Margherita with double extra cheese with mushroom with Jalapeno: " + moreJalapeno.GetPrice().ToString());

        Console.ReadLine();
    }
}

public class Margherita : BasePizza
{
    public Margherita()
    {
        this.myPrice = 6.99;
    }
}

public class Gourmet : BasePizza
{
    public Gourmet()
    {
        this.myPrice = 7.49;
    }
}

public class ExtraCheeseTopping : ToppingsDecorator
{
    public ExtraCheeseTopping(BasePizza pizzaToDecorate)
        : base(pizzaToDecorate)
    {
        this.myPrice = 0.99;
    }
}

public class MushroomTopping : ToppingsDecorator
{
    public MushroomTopping(BasePizza pizzaToDecorate)
        : base(pizzaToDecorate)
    {
        this.myPrice = 1.49;
    }
}

public class JalapenoTopping : ToppingsDecorator
{
    public JalapenoTopping(BasePizza pizzaToDecorate)
        : base(pizzaToDecorate)
    {
        this.myPrice = 1.49;
    }
}

【讨论】:

  • 有点不喜欢这种模式。也许这就是例子。我在 OOD 方面遇到的主要问题是浇头不是比萨饼。询问浇头的披萨价格,这对我来说并不合适。这是一个非常周到和详细的例子,所以我并不是要为此敲你。
  • @TomW 我认为部分问题在于命名。所有“Topping”类都应称为“PizzaWith”。例如,“PizzaWithMushrooms”。
  • 从另一个角度来看,这甚至不接近“真实世界”。在现实世界中,您不应该在每次需要在菜单中添加新的浇头(或更改价格)时重新编译。浇头(通常)存储在数据库中,因此使上述示例无用。
  • ^ 这个。我认为这是在研究这种模式时一直困扰我的问题。如果我是一家软件公司并编写披萨店软件,我不想每次都重新编译和重新发布。我想在后端的表中添加一行,或者可以轻松满足他们的要求的东西。说得好,@Stelios Adamantidis。我猜模式最大的优势在于修改 3rd-party 类。
  • 这是一个不好的例子的原因是你没有通过使用装饰器模式来解决真正的问题。 “火腿蘑菇披萨”不是“一些蘑菇,下面有一个(火腿和披萨)”。不,它是一种含有以下成分的比萨饼:[火腿、蘑菇]。如果你正在编写一个真实世界的应用程序,你只会让整个事情变得比它需要的更复杂。我很想看到一个用这种模式解决真正问题的例子。
【解决方案2】:

装饰器只是子类化的组合替代品。 每个人都提到的关于这个主题的原始书中的常见示例是文本处理应用程序。

假设你写了一个段落。您将其突出显示为黄色。你把一个句子斜体。你将斜体句子的一半加粗,下一个句子的一半也加粗。您增加其中一个斜体和粗体字母的字体大小。您更改了一半突出显示部分的字体样式,其中一些进入斜体部分,有些则没有...

所以我要问你如何实现该功能。你从一个简单的、未装饰的字母的类开始。你接下来要做什么?

我假设您不会使用子类化。您将需要如此复杂、错综复杂的多重继承层次结构来实现我所描述的所有组合以及更多,子类化和多重继承将是荒谬的。我认为这不需要解释。

您可能会建议将所有这些属性打包到您的信函对象中。用于定义字体样式、大小、突出显示、粗体、斜体的属性,不胜枚举。您可以添加到字母对象的每一种属性,您在字母类中都有一个属性。

那么这种基于属性的方法有什么问题呢?

  1. 现在你的类很臃肿,它占用了大量的内存。它具有与它相关的所有这些不必要的属性,大多数它永远不会使用。大多数字母只是……字母。没有装饰。
  2. 你的字母类的数据被以一种完全暴露的方式使用,你的类只是一个美化的结构。对于所有这些属性,您都有一堆 getter 和 setter。外部代码访问这些设置器并修改对象的图形外观。您的对象和外部代码之间存在紧密耦合。
  3. 所有东西都打包在一个地方,它不是模块化的。它只是一个臃肿的、相互关联的代码包。在处理您的字母对象的外部代码中也是如此。

从根本上说,这是一个面向对象设计、适当封装和关注点分离的问题。

现在,让我们想当然地认为我们想使用更好的 OO 设计原则。我们想使用封装,我们想保持外部代码和我们的字母类之间的松散耦合。我们希望最小化我们的字母对象内存占用。如何...?我们不能使用子类化...

所以我们使用装饰器,这是面向对象设计的一种组合方法——它与自上而下的子类化方法有点相反。您可以在运行时用更多功能包装这些字母对象,并在它们之上构建。

这就是装饰器模式 - 它是子类化的组合替代方案。在我们的示例中,您将装饰器添加到需要突出显示的字母对象。您可以以任意数量的方式组合任意数量的装饰器,并将它们包裹在给定的字母周围。装饰器界面始终是透明的,因此您仍然从外部将这些字母视为相同。

任何时候您需要以任意和可重组的方式增强功能,请考虑这种方法。多重继承会遇到各种各样的问题,它只是不可扩展。

【讨论】:

    【解决方案3】:

    示例 - 场景 - 假设您正在编写一个加密模块。这种加密可以使用 DES - 数据加密标准加密明文文件。同样,在系统中,您可以将加密作为 AES - 高级加密标准。此外,您可以组合加密 - 首先是 DES,然后是 AES。或者您可以先使用 AES,然后使用 DES。

    讨论-你将如何应对这种情况?您不能继续创建此类组合的对象 - 例如 - AES 和 DES - 总共 4 个组合。因此,您需要有 4 个单独的对象。随着加密类型的增加,这将变得复杂。

    解决方案 - 继续构建堆栈 - 根据需要组合 - 在运行时。 这种堆栈方法的另一个优点是您可以轻松展开它。

    这是解决方案 - 用 C++ 编写。

    首先,您需要一个基类 - 堆栈的基本单元。您可以将其视为堆栈的基础。在本例中,它是明文文件。让我们始终遵循多态性。首先制作这个基本单元的接口类。这样,您可以随心所欲地实现它。此外,在包含此基本单元时,您无需考虑依赖关系。

    这里是接口类-

    class IclearData
    {
    public:
    
        virtual std::string getData() = 0;
        virtual ~IclearData() = 0;
    };
    
    IclearData::~IclearData()
    {
        std::cout<<"Destructor called of IclearData"<<std::endl;
    }
    

    现在,实现这个接口类-

    class clearData:public IclearData
    {
    private:
    
        std::string m_data;
    
        clearData();
    
        void setData(std::string data)
            {
                m_data = data;
            }
    
    public:
    
        std::string getData()
        {
            return m_data;
        }
    
        clearData(std::string data)
        {
            setData(data);
        }
    
        ~clearData()
        {
            std::cout<<"Destructor of clear Data Invoked"<<std::endl;
        }
    
    };
    

    现在,让我们创建一个装饰器抽象类 - 可以扩展它以创建任何类型的风格 - 这里的风格是加密类型。这个装饰器抽象类与基类相关。因此,装饰器“是”一种接口类。因此,您需要使用继承。

    class encryptionDecorator: public IclearData
    {
    
    protected:
        IclearData *p_mclearData;
    
        encryptionDecorator()
        {
          std::cout<<"Encryption Decorator Abstract class called"<<std::endl;
        }
    
    public:
    
        std::string getData()
        {
            return p_mclearData->getData();
        }
    
        encryptionDecorator(IclearData *clearData)
        {
            p_mclearData = clearData;
        }
    
        virtual std::string showDecryptedData() = 0;
    
        virtual ~encryptionDecorator() = 0;
    
    };
    
    encryptionDecorator::~encryptionDecorator()
    {
        std::cout<<"Encryption Decorator Destructor called"<<std::endl;
    }
    

    现在,让我们创建一个具体的装饰器类 - 加密类型 - AES -

    const std::string aesEncrypt = "AES Encrypted ";
    
    class aes: public encryptionDecorator
    {
    
    private:
    
        std::string m_aesData;
    
        aes();
    
    public:
    
        aes(IclearData *pClearData): m_aesData(aesEncrypt)
        {
            p_mclearData = pClearData;
            m_aesData.append(p_mclearData->getData());
        }
    
        std::string getData()
            {
                return m_aesData;
            }
    
        std::string showDecryptedData(void)
        {
            m_aesData.erase(0,m_aesData.length());
            return m_aesData;
        }
    
    };
    

    现在,假设装饰器类型是 DES -

    const std::string desEncrypt = "DES Encrypted ";
    
    class des: public encryptionDecorator
    {
    
    private:
    
        std::string m_desData;
    
        des();
    
    public:
    
        des(IclearData *pClearData): m_desData(desEncrypt)
        {
            p_mclearData = pClearData;
            m_desData.append(p_mclearData->getData());
        }
    
        std::string getData(void)
            {
                return m_desData;
            }
    
        std::string showDecryptedData(void)
        {
            m_desData.erase(0,desEncrypt.length());
            return m_desData;
        }
    
    };
    

    让我们编写一个客户端代码来使用这个装饰器类 -

    int main()
    {
        IclearData *pData = new clearData("HELLO_CLEAR_DATA");
    
        std::cout<<pData->getData()<<std::endl;
    
    
        encryptionDecorator *pAesData = new aes(pData);
    
        std::cout<<pAesData->getData()<<std::endl;
    
        encryptionDecorator *pDesData = new des(pAesData);
    
        std::cout<<pDesData->getData()<<std::endl;
    
        /** unwind the decorator stack ***/
        std::cout<<pDesData->showDecryptedData()<<std::endl;
    
        delete pDesData;
        delete pAesData;
        delete pData;
    
        return 0;
    }
    

    您将看到以下结果 -

    HELLO_CLEAR_DATA
    Encryption Decorator Abstract class called
    AES Encrypted HELLO_CLEAR_DATA
    Encryption Decorator Abstract class called
    DES Encrypted AES Encrypted HELLO_CLEAR_DATA
    AES Encrypted HELLO_CLEAR_DATA
    Encryption Decorator Destructor called
    Destructor called of IclearData
    Encryption Decorator Destructor called
    Destructor called of IclearData
    Destructor of clear Data Invoked
    Destructor called of IclearData
    

    这是 UML 图 - 它的类表示。如果您想跳过代码并专注于设计方面。

    【讨论】:

    • 这个例子不是更适合strategy pattern吗?
    • @exexzian 是的,我的学生一直向我建议解决此类问题的策略列表,这对我来说也是最干净的解决方案。
    • 不,使用策略模式你不能组合加密方法。因此,您必须为每个可能的组合创建一个策略类。
    【解决方案4】:

    这是一个向现有对象动态添加新行为的简单示例,或装饰器模式。由于 Javascript 等动态语言的性质,这种模式成为语言本身的一部分。

    // Person object that we will be decorating with logging capability
    var person = {
      name: "Foo",
      city: "Bar"
    };
    
    // Function that serves as a decorator and dynamically adds the log method to a given object
    function MakeLoggable(object) {
      object.log = function(property) {
        console.log(this[property]);
      }
    }
    
    // Person is given the dynamic responsibility here
    MakeLoggable(person);
    
    // Using the newly added functionality
    person.log('name');

    【讨论】:

    • 简单而精确!很好的例子!
    • 我认为装饰模式的概念在这里不适用。实际上它根本不是一个模式!是的,您正在运行时添加一个新方法。并且可能在switch 或简单的if 中,您可以声称这是一个为类动态添加行为的好例子。但是,我们至少需要两个类来定义装饰器和装饰对象这种模式。
    • @Zich 我知道我的示例中没有装饰器,但是通过添加一个用作装饰器的函数很容易解决这个问题。但是在我的示例中有一个装饰对象。该模式是否明确指出您需要两个
    【解决方案5】:

    让我们以 PubG 为例。突击步枪最适合使用 4 倍变焦,当我们使用它时,我们还需要补偿器和抑制器。它将减少后坐力并减少射击声音和回声。我们需要实现这个功能,允许玩家购买他们最喜欢的枪和配件。玩家可以购买枪支或部分配件或全部配件,并收取相应费用。

    让我们看看这里是如何应用装饰器模式的:

    假设有人想购买带有上述所有三个配件的 SCAR-L。

    1. 拿一个 SCAR-L 的对象
    2. 用 4 倍变焦对象装饰(或添加)SCAR-L
    3. 用抑制器对象装饰 SCAR-L
    4. 用压缩器对象装饰 SCAR-L
    5. 调用成本方法,让每个对象委托添加成本 使用配件成本法

    这将导致这样的类图:

    现在,我们可以有这样的类:

    public abstract class Gun {     
        private Double cost;    
        public Double getCost() {           
            return cost;        
           }    
        }
    
    public abstract class GunAccessories extends Gun {  }
    
    public class Scarl extends Gun {    
        public Scarl() {            
            cost = 100;
            }   
         }
    
    public class Suppressor extends GunAccessories {        
        Gun gun;        
        public Suppressor(Gun gun) {            
        cost = 5;           
        this.gun = gun;     
        }               
        public double getCost(){            
            return cost + gun.getCost();
        }
    }
    
    public class GunShop{   
        public static void main(String args[]){         
        Gun scarl = new Scarl();                
        scarl = new Supressor(scarl);
        System.out.println("Price is "+scarl.getCost());
        }      
    }
    

    我们也可以类似地添加其他配件来装饰我们的枪。

    参考:

    https://nulpointerexception.com/2019/05/05/a-beginner-guide-to-decorator-pattern/

    【讨论】:

    • 我觉得这个例子并不能证明装饰器的复杂性。让每支枪都有一个附件列表,然后通过对附件的成本求和来计算枪支的成本会更简单
    【解决方案6】:

    前段时间我将代码库重构为使用装饰器模式,因此我将尝试解释用例。

    假设我们有一组服务,根据用户是否获得特定服务的许可,我们需要启动服务。

    所有服务都有一个通用接口

    interface Service {
      String serviceId();
      void init() throws Exception;
      void start() throws Exception;
      void stop() throws Exception;
    }
    

    预重构

    abstract class ServiceSupport implements Service {
      public ServiceSupport(String serviceId, LicenseManager licenseManager) {
        // assign instance variables
      }
    
      @Override
      public void init() throws Exception {
        if (!licenseManager.isLicenseValid(serviceId)) {
           throw new Exception("License not valid for service");
        }
        // Service initialization logic
      }
    }
    

    如果你仔细观察,ServiceSupport 依赖于LicenseManager。但是为什么要依赖LicenseManager呢?如果我们需要不需要检查许可证信息的后台服务怎么办。在当前情况下,我们将不得不以某种方式训练 LicenseManager 以返回 true 以获得后台服务。 这种方法对我来说似乎不太好。根据我的说法,许可证检查和其他逻辑是相互正交的。

    所以 装饰器模式 来拯救,这里开始使用 TDD 重构。

    后期重构

    class LicensedService implements Service {
      private Service service;
      public LicensedService(LicenseManager licenseManager, Service service) {
        this.service = service;
      }
    
      @Override
      public void init() {
        if (!licenseManager.isLicenseValid(service.serviceId())) {
          throw new Exception("License is invalid for service " + service.serviceId());
        }
        // Delegate init to decorated service
        service.init();
      }
    
      // override other methods according to requirement
    }
    
    // Not concerned with licensing any more :)
    abstract class ServiceSupport implements Service {
      public ServiceSupport(String serviceId) {
        // assign variables
      }
    
      @Override
      public void init() {
        // Service initialization logic
      }
    }
    
    // The services which need license protection can be decorated with a Licensed service
    Service aLicensedService = new LicensedService(new Service1("Service1"), licenseManager);
    // Services which don't need license can be created without one and there is no need to pass license related information
    Service aBackgroundService = new BackgroundService1("BG-1");
    

    要点

    • 代码的凝聚力变得更好
    • 单元测试变得更容易,因为在测试 ServiceSupport 时不必模拟许可
    • 不需要通过对后台服务的任何特殊检查来绕过许可
    • 职责分工合理

    【讨论】:

      【解决方案7】:

      装饰器模式实现了为任何对象动态添加职责的单一目标。

      Java I/O 模型基于装饰器模式。

      【讨论】:

        【解决方案8】:

        装饰者:

        1. 在运行时向对象添加行为。继承是实现这一功能的关键,这既是这种模式的优点也是缺点。
        2. 它增强了界面的行为
        3. 装饰器可以被视为只有一个组件的退化复合体。但是,装饰器增加了额外的职责 - 它不适合对象聚合。
        4. Decorator 类声明了与 LCD(最低类分母)接口的组合关系,并且该数据成员在其构造函数中初始化。
        5. 装饰器旨在让您无需子类化即可向对象添加职责

        详情请参阅sourcemaking 文章。

        装饰器(抽象):它是一个抽象类/接口,它实现了组件接口。它包含组件接口。在没有这个类的情况下,你需要很多 ConcreteDecorators 的子类来实现不同的组合。组件的组合减少了不必要的子类。

        JDK 示例:

        BufferedInputStream bis = new BufferedInputStream(new FileInputStream(new File("a.txt")));
        while(bis.available()>0)
        {
                char c = (char)bis.read();
                System.out.println("Char: "+c);;
        }
        

        查看下面的 SE 问题,了解 UML 图和代码示例。

        Decorator Pattern for IO

        有用的文章:

        journaldev

        wikipedia

        装饰器模式的真实例子:VendingMachineDecorator已经解释了@

        When to Use the Decorator Pattern?

        Beverage beverage = new SugarDecorator(new LemonDecorator(new Tea("Assam Tea")));
        beverage.decorateBeverage();
        
        beverage = new SugarDecorator(new LemonDecorator(new Coffee("Cappuccino")));
        beverage.decorateBeverage();
        

        在上面的示例中,茶或咖啡(饮料)已由糖和柠檬装饰。

        【讨论】:

          【解决方案9】:

          装饰器模式通过链接该对象的其他类似子类来帮助您更改或配置对象的功能。

          最好的例子是 java.io 包中的 InputStream 和 OutputStream 类

              File file=new File("target","test.txt");
              FileOutputStream fos=new FileOutputStream(file);
              BufferedOutputStream bos=new BufferedOutputStream(fos);
              ObjectOutputStream oos=new ObjectOutputStream(bos);
          
          
              oos.write(5);
              oos.writeBoolean(true);
              oos.writeBytes("decorator pattern was here.");
          
          
          //... then close the streams of course.
          

          【讨论】:

          • 在这种情况下,调用链从 ObjectOutputStream 开始,然后一直到 File 类,然后 File 类返回值,然后其他三个子类将它们全部相加,最后, ObjectOutputStream 方法的值返回,对吗?
          【解决方案10】:

          维基百科上有一个关于用滚动条装饰窗口的例子:

          http://en.wikipedia.org/wiki/Decorator_pattern

          这是另一个非常“真实世界”的“团队成员、团队领导和经理”示例,它说明了装饰器模式无法用简单的继承来替代:

          https://zishanbilal.wordpress.com/2011/04/28/design-patterns-by-examples-decorator-pattern/

          【讨论】:

          • 紫山比拉尔链接很棒——我见过的最好的例子
          【解决方案11】:

          Decorator Design Pattern: 这种模式有助于在运行时修改对象的特征。它为对象提供不同的风味,并让我们可以灵活地选择我们想要在该风味中使用的成分。

          现实生活中的例子: 假设您在航班上有一个主舱座位。现在您可以选择座椅的多种便利设施。每个便利设施都有与之相关的成本。现在如果用户选择Wifi和高级食物,他/她将被收取座位+wifi+高级食物的费用。

          在这种情况下,装饰器设计模式真的可以帮助我们。访问上面的链接,了解更多关于装饰器模式和一个现实生活示例的实现。

          【讨论】:

            【解决方案12】:

            装饰器模式让您可以动态地向对象添加行为。

            让我们举个例子,您需要构建一个计算不同种类汉堡价格的应用。您需要处理汉堡的不同变体,例如“大”或“加奶酪”,每个汉堡都具有相对于基本汉堡的价格。例如。加奶酪的汉堡加 10 美元,大汉堡加 15 美元,等等。

            在这种情况下,您可能很想创建子类来处理这些问题。我们可以在 Ruby 中将其表示为:

            class Burger
              def price
                50
              end
            end
            
            class BurgerWithCheese < Burger
              def price
                super + 15
              end
            end
            

            在上面的例子中,BurgerWithCheese 类继承自 Burger,并重写了 price 方法,将 $15 加到超类中定义的价格上。您还将创建一个 LargeBurger 类并定义相对于 Burger 的价格。但您还需要为“大”和“加奶酪”的组合定义一个新类。

            现在如果我们需要提供“汉堡配薯条”会发生什么?我们已经有 4 个类来处理这些组合,我们需要再添加 4 个来处理 3 个属性的所有组合——“大”、“奶酪”和“薯条”。我们现在需要 8 节课。添加另一个属性,我们需要 16。这将增长为 2^n。

            相反,让我们尝试定义一个 BurgerDecorator 来接收一个 Burger 对象:

            class BurgerDecorator
              def initialize(burger)
                self.burger = burger
              end
            end
            
            class BurgerWithCheese < BurgerDecorator
              def price
                self.burger.price + 15
              end
            end
            
            burger = Burger.new
            cheese_burger = BurgerWithCheese.new(burger)
            cheese_burger.price   # => 65
            

            在上面的示例中,我们创建了一个 BurgerDecorator 类,BurgerWithCheese 类从该类继承。我们还可以通过创建 LargeBurger 类来表示“大”变化。现在我们可以在运行时定义一个带有奶酪的大汉堡:

            b = LargeBurger.new(cheese_burger)
            b.price  # => 50 + 15 + 20 = 85
            

            还记得如何使用继承来添加“with fries”变体将涉及添加 4 个以上的子类吗?使用装饰器,我们只需创建一个新类 BurgerWithFries 来处理新的变体并在运行时处理它。每个新属性只需要更多的装饰器来覆盖所有排列。

            PS。这是我写的一篇关于using the Decorator Pattern in Ruby的文章的简短版本,如果你想找到更详细的例子,可以阅读。

            【讨论】:

              【解决方案13】:

              什么是 Java 中的装饰器设计模式。

              GoF 书籍(Design Patterns: Elements of Reusable Object-Oriented Software, 1995, Pearson Education, Inc. Publishing as Pearson Addison Wesley)中对装饰器模式的正式定义表明您可以,

              “动态地为对象附加额外的职责。装饰器 为扩展功能提供一种灵活的替代子类的方法。”

              假设我们有一个比萨饼,我们想用鸡肉玛萨拉、洋葱和马苏里拉奶酪等配料来装饰它。让我们看看如何在 Java 中实现它......

              演示如何在 Java 中实现装饰器设计模式的程序。

              Pizza.java:

              <!-- language-all: lang-html -->
              
              package com.hubberspot.designpattern.structural.decorator;
              
              public class Pizza {
              
              public Pizza() {
              
              }
              
              public String description(){
                  return "Pizza";
              }
              
              }
              
              
              
              package com.hubberspot.designpattern.structural.decorator;
              
              public abstract class PizzaToppings extends Pizza {
              
              public abstract String description();
              
              }
              
              package com.hubberspot.designpattern.structural.decorator;
              
              public class ChickenMasala extends PizzaToppings {
              
              private Pizza pizza;
              
              public ChickenMasala(Pizza pizza) {
                  this.pizza = pizza;
              }
              
              @Override
              public String description() {
                  return pizza.description() + " with chicken masala, ";
              }
              
              }
              
              
              
              package com.hubberspot.designpattern.structural.decorator;
              
              public class MozzarellaCheese extends PizzaToppings {
              
              private Pizza pizza;
              
              public MozzarellaCheese(Pizza pizza) {
                  this.pizza = pizza;
              }
              
              @Override
              public String description() {
                  return pizza.description() + "and mozzarella cheese.";
              }
              }
              
              
              
              package com.hubberspot.designpattern.structural.decorator;
              
              public class Onion extends PizzaToppings {
              
              private Pizza pizza;
              
              public Onion(Pizza pizza) {
                  this.pizza = pizza;
              }
              
              @Override
              public String description() {
                  return pizza.description() + "onions, ";
              }
              
              }
              
              
              
              package com.hubberspot.designpattern.structural.decorator;
              
              public class TestDecorator {
              
              public static void main(String[] args) {
              
                  Pizza pizza = new Pizza();
              
                  pizza = new ChickenMasala(pizza);
                  pizza = new Onion(pizza);
                  pizza = new MozzarellaCheese(pizza);
              
                  System.out.println("You're getting " + pizza.description());
              
              }
              
              }
              

              【讨论】:

                【解决方案14】:

                值得注意的是,Java i/o 模型基于装饰器模式。这个阅读器在那个阅读器之上的分层......是一个真实世界的装饰器示例。

                【讨论】:

                • 在真正的公共 API 中还有其他例子吗?这是我唯一知道的。
                • 似乎自然界中的所有包装函数都内置了某种装饰器模式,我认为是这样吗?
                • 好例子!!
                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2012-07-24
                • 2021-06-22
                • 2011-09-15
                • 2013-12-06
                相关资源
                最近更新 更多