在PHP应用程序中使用Decorator模式

时间:2012-06-27 18:38:38

标签: php design-patterns

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

<?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>

然而,我已经开始遇到一个问题,我有很多特定于某些模型的View Helper,不需要占用整个文件。如果我可以将这个表示逻辑组合在一起并将其保持在模型之外,那将是很好的。我认为这是decorator pattern有意义的时候。

  

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

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

3 个答案:

答案 0 :(得分:2)

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

$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;
    }
}

我把很多这些小装饰器类放在一个巨大的文件中。

答案 1 :(得分:0)

Zend Framework使用装饰器模式作为其表单元素。想象一下,输入元素的类型为“text”。您可以使用标签,div,字段集等“装饰”此元素。

答案 2 :(得分:0)

我理解你的问题。是的,Decorator模式可以帮助您在不同的业务逻辑中重用您的类。

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

$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() ) ) ;

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