ViewHelper 可更新/可注入的困境

发布于 2024-08-18 09:18:20 字数 1030 浏览 8 评论 0原文

我正在尝试设计一个应用程序 米斯科·赫弗里斯的见解。这是一个有趣的实验,也是一个挑战。目前我正在为 ViewHelper 的实现而苦苦挣扎。

ViewHelper 将模型与视图解耦。在我的实现中,它包装了模型并提供了 API 供视图使用。我正在使用 PHP,但我希望每个人都可以阅读该实现:

class PostViewHelper {
    private $postModel;

    public function __construct(PostModel $postModel) {
         $this->postModel = $postModel;
    }

    public function title() {
         return $this->postModel->getTitle();
    }
}

在我的模板(视图)文件中,可以这样调用:

<h1><?php echo $this->post->title(); ?></h1>

到目前为止一切顺利。我遇到的问题是当我想将过滤器附加到 ViewHelpers 时。我想要有过滤 title() 调用的输出的插件。该方法将变成这样:

public function title() {
    return $this->filter($this->postModel->getTitle());
}

我需要在那里获取观察者,或者 EventHandler,或者任何服务(在我看来是可更新的,所以它需要通过堆栈传入)。我怎样才能遵循 Misko Hevery 的原则做到这一点?我知道没有它我怎么能做到这一点。我对如何接受它很感兴趣,但目前我没有看到解决方案。 ViewHelper 也可以是可注入的,但是将模型放入其中就会出现问题。

I'm trying to design an application following Misko Heverys insights. It's an interesting experiment and a challenge. Currently I'm struggling with my ViewHelper implementation.

The ViewHelper decouples the model from the view. In my implementation it wraps the model and provides the API for the view to use. I'm using PHP, but I hope the implementation is readable for everyone:

class PostViewHelper {
    private $postModel;

    public function __construct(PostModel $postModel) {
         $this->postModel = $postModel;
    }

    public function title() {
         return $this->postModel->getTitle();
    }
}

In my template (view) file this could be called like this:

<h1><?php echo $this->post->title(); ?></h1>

So far so good. The problem I have is when I want to attach a filter to the ViewHelpers. I want to have plugins that filter the output of the title() call. The method would become like this:

public function title() {
    return $this->filter($this->postModel->getTitle());
}

I need to get observers in there, or an EventHandler, or whatever service (in what I see as a newable, so it needs to be passed in through the stack). How can I do this following the principles of Misko Hevery? I know how I can do this without it. I'm interested in how for I can take it and currently I don't see a solution. ViewHelper could be an injectable too, but then getting the model in there is the problem.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

梦在深巷 2024-08-25 09:18:20

我没有发现您引用的博客文章非常有趣或富有洞察力。

您所描述的内容看起来更像是 装饰器,而不是与依赖注入有关的任何内容。依赖注入是如何构建对象图,而不是构建后的状态

也就是说,我建议采用装饰器模式并运行它。

interface PostInterface
{
    public function title();
}

class PostModel implements PostInterface
{
    public function title()
    {
        return $this->title;
    }
}

class PostViewHelper implements PostInterface
{
    public function __construct(PostInterface $post)
    {
        $this->post = $post;
    }

    public function title()
    {
        return $this->post->title();
    }
}

class PostFilter implements PostInterface
{
    public function __construct(PostInterface $post)
    {
        $this->post = $post;
    }

    public function title()
    {
        return $this->filter($this->post->title());
    }

    protected function filter($str)
    {
        return "FILTERED:$str";
    }
}

您只需使用您所拥有的任何 DI 框架来构建此对象图,如下所示:

$post = new PostFilter(new PostViewHelper($model)));

在构建复杂的嵌套对象时,我经常使用这种方法。

您可能遇到的一个问题是在 PostInterface 中定义“太多”函数。必须在每个装饰器类中实现这些可能会很痛苦。我利用 PHP 魔法函数来解决这个问题。

interface PostInterface
{
    /**
     * Minimal interface. This is the accessor
     * for the unique ID of this Post.
     */
    public function getId();
}


class SomeDecoratedPost implements PostInterface
{
    public function __construct(PostInterface $post)
    {
        $this->_post = $post;
    }

    public function getId()
    {
        return $this->_post->getId();
    }

    /**
     * The following magic functions proxy all 
     * calls back to the decorated Post
     */
    public function __call($name, $arguments)
    {
        return call_user_func_array(array($this->_post, $name), $arguments);
    }

    public function __get($name)
    {
        return $this->_post->get($name);
    }

    public function __set($name, $value)
    {
        $this->_post->__set($name, $value);
    }

    public function __isset($name)
    {
        return $this->_post->__isset($name);
    }

    public function __unset($name)
    {
        $this->_post->__unset($name);
    }
}

使用这种类型的装饰器,我可以有选择地重写提供装饰功能所需的任何方法。我没有覆盖的任何内容都会传递回底层对象。在维护底层对象的接口的同时,可以发生多个装饰。

I didn't find the blog post you referenced very interesting or insightful.

What you are describing seems more like a Decorator than anything to do with dependency injection. Dependency injection is how you construct your object graphs, not their state once constructed.

That said, I'd suggest taking your Decorator pattern and running with it.

interface PostInterface
{
    public function title();
}

class PostModel implements PostInterface
{
    public function title()
    {
        return $this->title;
    }
}

class PostViewHelper implements PostInterface
{
    public function __construct(PostInterface $post)
    {
        $this->post = $post;
    }

    public function title()
    {
        return $this->post->title();
    }
}

class PostFilter implements PostInterface
{
    public function __construct(PostInterface $post)
    {
        $this->post = $post;
    }

    public function title()
    {
        return $this->filter($this->post->title());
    }

    protected function filter($str)
    {
        return "FILTERED:$str";
    }
}

You'd simply use whatever DI framework you have to build this object graph like so:

$post = new PostFilter(new PostViewHelper($model)));

I often use this approach when building complex nested objects.

One problem you might run into is defining "too many" functions in your PostInterface. It can be a pain to have to implement these in every decorator class. I take advantage of the PHP magic functions to get around this.

interface PostInterface
{
    /**
     * Minimal interface. This is the accessor
     * for the unique ID of this Post.
     */
    public function getId();
}


class SomeDecoratedPost implements PostInterface
{
    public function __construct(PostInterface $post)
    {
        $this->_post = $post;
    }

    public function getId()
    {
        return $this->_post->getId();
    }

    /**
     * The following magic functions proxy all 
     * calls back to the decorated Post
     */
    public function __call($name, $arguments)
    {
        return call_user_func_array(array($this->_post, $name), $arguments);
    }

    public function __get($name)
    {
        return $this->_post->get($name);
    }

    public function __set($name, $value)
    {
        $this->_post->__set($name, $value);
    }

    public function __isset($name)
    {
        return $this->_post->__isset($name);
    }

    public function __unset($name)
    {
        $this->_post->__unset($name);
    }
}

With this type of decorator in use, I can selectively override whatever method I need to provide the decorated functionality. Anything I don't override is passed back to the underlying object. Multiple decorations can occur all while maintaining the interface of the underlying object.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文