正确使用装饰器

Posted

技术标签:

【中文标题】正确使用装饰器【英文标题】:Correct use of a decorator 【发布时间】:2011-12-15 17:33:52 【问题描述】:

我有一个 Products 课程。现在我想向我的网站添加某种折扣模块,该模块应该与 Products 类交互。

目前我能想到的唯一解决方案是使用某种装饰器模式来包裹产品类,这样它就可以改变产品的价格。

像这样:

class Product 
    function price() 
        return 10;
    


class ProductDiscountDecorator 
    private $product;

    function __construct($product) 
        $this->product = $product;
    

    function price() 
        return $this->product->price()*0.8;
    


$product = new ProductDiscountDecorator(new Product());
echo $product->price();

这是折扣,应在网站的每个页面上调整价格。所以每个使用 Product 类的页面也应该添加装饰器。我能想到解决这个问题的唯一方法是使用自动添加这个装饰器的工厂。

$product = $factory->get('product'); // returns new ProductDiscountDecorator(new Product());

它可能会起作用,但我觉得我在这里误用了装饰器模式。

你们对此有什么想法吗?你会如何实现这样的东西?

【问题讨论】:

【参考方案1】:

装饰器是一种选择。另一种选择是使用策略模式来计算折扣

示例

class Product

    protected $discountStrategy;
    protected $price;

    public function __construct(DiscountStrategy $discountStrategy)
    
        $this->discountStrategy = $discountStrategy;
    

    public function getPrice()
    
        return $this->discountStrategy->applyDiscount($this->price);
    

然后将各种可能的折扣封装到自己的类别中:

interface DiscountStrategy 

    public function applyDiscount($cent);


class NoDiscount implements DiscountStrategy

    public function applyDiscount($cent)
    
        return $cent;
    


class PremiumDiscount implements DiscountStrategy

    public function applyDiscount($cent)
    
        return $cent - $cent * 0.8;
    

您仍然会使用工厂来组装产品,但策略更精细,您也可以在其他情况下重复使用它们,例如,即使产品本身没有,一些忠实客户也可以获得折扣有折扣。

【讨论】:

有趣,我会看看 :)【参考方案2】:

总体来说还不错。

为了遵守良好的 OOP 设计原则,我还将定义一个“产品”接口

即。

interface IProduct  public function getPrice(); 

并在 Product 和 ProductDiscountDecorator 上实现它

class Product implements IProduct  ... 
class ProductDiscountDecorator implements IProduct  ... 

【讨论】:

以上是关于正确使用装饰器的主要内容,如果未能解决你的问题,请参考以下文章

装饰器模式是在这种情况下使用的正确模式吗

装饰器复习

Django - 将参数传递给 CBV 装饰器的正确方法?

如何使用请求编写 Flask 装饰器?

Python装饰器

如何在vue.js 2应用程序中全局使用自定义装饰器?