【问题标题】:Using the Decorator Pattern in a PHP application在 PHP 应用程序中使用装饰器模式
【发布时间】:2012-06-27 18:38:37
【问题描述】:

在我的应用程序中,我的模型中似乎有很多表示逻辑:

<?php foreach ($this->users as $user): ?>
    <span class="phone">
        <?php echo $user->getPhoneNumberFormattedAsText(); ?>
    </span>
<?php endforeach; ?>

起初,我开始将其视为对 View Helpers 的需求:

<span class="phone"><?php echo $this->userPhone($user->getPhone()); ?></span>

但是,我开始遇到一个问题,即我有很多特定于某些模型的小视图助手,它们不需要占用整个文件。如果我可以将这个表示逻辑组合在一起并将其排除在模型之外,那就太好了。我认为这是 decorator pattern 有意义的时候。

“装饰器模式是一种允许将行为动态添加到现有对象的设计模式。”

我在网上看到了一些示例,但没有真正实用的代码示例。我想知道您是否在您的 PHP 应用程序中成功使用了这种模式,以及这种模式的 PHP 示例应该是什么样子。

【问题讨论】:

    标签: php design-patterns


    【解决方案1】:

    我已经为我的 php 应用程序实现了一个装饰器模式。基本上它是一个 xml 配置文件的包装类,我在其中定义了我的基本需求。为简化起见,我以披萨为例。然后我为每种成分设置一个类,并将其包裹在另一个类中。最后我调用了每个类的 Prize 方法,它给了我所有的总和。

    $order = new pizza($xml_file);
    $order = new add_salami($order);
    $order = new add_cheese($order);
    $order = new add_tomato($order);   
    $order = $order->prize();
    

    您需要在每个成分类中维护一个指向当前对象的指针。当您调用 php new 函数时,您可以使用它来备份当前对象。它有点像(对象的)链表。然后你可以调用最后一个对象的 Prize() 方法并循环遍历所有其他类。但是要装饰你需要添加新的类。您还可以将 $xml_file 替换为起始值。我将所有装饰器类都放在一个文件中。装饰器类可以如下所示:

    class add_salami {
    
        protected $order;
        protected $prize;
    
        public function __construct ($order) {
            $this->order = $order;
            $this->prize = $order->getPrize();
        }
    
        public function getPrize() {
            return $this->prize + 10;
        }
    }
    

    我将许多这些微小的装饰器类保存在一个大文件中。

    【讨论】:

    • 我添加了一个示例。我认为您还需要在每个装饰器类的构造函数中调用 getPrize 函数。做某事是我对意大利腊肠装饰者需要的一些方法。 tx_decorator 类本身只是一个空的接口类。我不确定你是否需要它。我的示例在没有它的情况下也可以工作。
    • 您可以将 tx_salami 替换为 add_salami。我希望你知道构造函数是做什么的?无论如何,这就是我想说的。我出去了。
    • 我冒昧地修复了您的代码示例。我希望你不介意,我传达了你的初衷。如果我弄错了,请告诉我。
    • 嗯,它是 php 4。而不是 php 5。你不需要改变它吗?
    【解决方案2】:

    Zend Framework 对其表单元素使用装饰器模式。想象一下有一个 type="text" 的输​​入元素。你可以用标签、div、字段集等“装饰”这个元素。

    【讨论】:

    • 我已经在使用 Zend 框架,所以我不确定你想要暗示什么。此外,表单装饰器的 ZF 实现有点像一个黑盒子,不清楚它是如何工作的。此外,我不确定它与“装饰”模型以在视图脚本中显示相比如何,以及在我的视图脚本中使用此装饰器的效果如何。
    • 我不是在暗示什么。您要求提供装饰器模式的实际示例。 ZF是开源的,黑盒如何?
    【解决方案3】:

    我了解您的问题。是的,装饰器模式可以帮助您在甚至不同的业务逻辑中重用您的类。

    我在所有可能的地方都使用装饰器模式,因为它真的可以帮助你。例如,假设您想从数据库中获取记录列表,为此您正在使用简单的存储库。这将返回您全部或一条记录。像这儿。有关存储库的更多信息,请访问此处。

    $repo=new ClientRepository();
    $repo->all();
    

    但是,假设您有一个业务规则,即在某些地方您应该记录请求的数据,而在其他地方您应该缓存请求。所以,你会怎么做。当然,大多数人会为 CacheClientRepository 和 LogClientRepository 创建单独的类。可能没问题,但是如果业务规则会同时询问您 Log 和 Cache 那么您将创建什么单独的类,例如 LogAndCacheClientReposiotry();如果你这样做,你最终会得到大量重复代码的类。

    对于这种情况,最好的解决方案是使用 DecoratorPattern。您将需要创建一个接口并在每个装饰器中实现它。您可以随心所欲地装饰您的存储库。

    这里是代码示例:

     <?php
    
    interface RepositoryInterface
    {
        public function all();
    }
    
    class ClientRepository implements RepositoryInterface
    {
    
        public function all()
        {
          // Your code for database goes here
        }
    }
    
    class CacheRepository implements RepositoryInterface
    {
        /**
         * @var RepositoryInterface
         */
        private $repo;
    
        /**
         * CacheRepository constructor.
         * @param RepositoryInterface $repo
         */
        public function __construct(RepositoryInterface $repo)
        {
            $this->repo = $repo;
        }
    
        public function all()
        {
            //Code for caching goes here
    
            return $this->cache('cache-all',function(){
                return $this->repo->all();
            });
        }
        //......
    }
    class LogRepository implements RepositoryInterface
    {
        /**
         * @var RepositoryInterface
         */
        private $repo;
    
        /**
         * LogRepository constructor.
         * @param RepositoryInterface $repo
         */
        public function __construct(RepositoryInterface $repo)
        {
            $this->repo = $repo;
        }
    
        public function all()
        {
            //Code for logging goes here
    
            return $this->log('log-all',function(){
                return $this->repo->all();
            });
        }
        //......
    }
    
    //.. Simple Repository
    $repo=new ClientRepository();
    //.. Cached Repository
    $repo=new CacheRepository( new ClientRepository() );
    //.. Logging Repository
    $repo=new LogRepository( new ClientRepository() ) ;
    
    //.. Logging and Caching at the same time
    $repo=new LogRepository( new CacheRepository( new ClientRepository() ) ) ;
    

    希望对你有所帮助,因为装饰器模式是我所知道的最好的模式之一。

    【讨论】:

      猜你喜欢
      • 2013-05-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-04-28
      • 2016-06-04
      • 2020-03-31
      • 1970-01-01
      相关资源
      最近更新 更多