django 类视图装饰器_灵活视图处理的模式,第2部分–使用装饰器

django 类视图装饰器

For years, we’ve been so busy discussing the virtues and drawbacks of the model/controller tandem that we’ve overlooked the role of views, denigrating them to a level of clunky HTML templates. Though the origins of MVC isn’t rooted in the OOP paradigm per se, most of the implementations currently available in the wild rely on the use of classes. In short, this means that if our own arrogant nature leads us believe the “view = HTML template” equation is valid, we’ll be failing to see the benefits of thinking of views as rich objects carrying state and behavior which can be manipulated through the logic of several design patterns.

多年以来,我们一直忙于讨论模型/控制器串联的优缺点,以至于我们忽略了视图的作用,将它们贬低为笨拙HTML模板。 尽管MVC的起源本身并不是植根于OOP范式中 ,但是当前在野外可用的大多数实现都依赖于类的使用。 简而言之,这意味着,如果我们自己的傲慢天性使我们相信“视图= HTML模板”等式是正确的,那么我们将无法看到将视图视为具有状态和行为的丰富对象的好处,这些对象可以通过以下方式进行操纵几种设计模式的逻辑。

In a humble attempt to avoid being misjudged for just babbling, in the first installment I implemented a customizable view module from scratch where views were conceptually designed up front as POPOs and the module was given the ability to recursively parse multiple view objects via the same API, hence honoring a typical implementation of the Composite pattern.

为了避免被误判为谦虚,在第一部分中,我从头开始实现了一个可自定义的视图模块,其中视图在概念上预先设计为POPO,并且该模块具有通过同一API递归解析多个视图对象的功能。因此,荣幸地使用Composite模式的典型实现。

Indeed, processing individual views and composite ones through the reins of a unified API provides a lot of flexibility as it allows us to render tree structures (in most cases chunks of HTML, but it could just as easily be anything else a browser can digest) without having to unnecessarily pollute client code with smelly conditionals, an ominous sign of a breaking the Open/Closed principle.

确实,通过统一API来处理单个视图和组合视图提供了很大的灵活性,因为它允许我们呈现树结构(在大多数情况下为HTML块,但它很容易被浏览器消化)而不用不必要的方式污染带有臭味的条件的客户代码,这是打破开放/封闭原则的不祥之兆。

It’s also feasible to manipulate views in fairly flexible fashion by appealing to the niceties of a few other patterns as well, including the rather underrated Decorators. If you’re wondering in what parallel universe Decorators get along with views, in this part I’ll be showing how to put them to work side by side in nice orchestration to bring to life yet another view module.

通过吸引其他一些模式的优点,包括相当低估的Decorators ,以相当灵活的方式操纵视图也是可行的。 如果您想知道Decorator在哪种并行Universe中与视图融为一体,那么在这一部分中,我将向您展示如何以一种很好的编排使它们并排工作,使另一个视图模块栩栩如生。

运行时的流水线功能–将装饰器带到桌面 (Pipelining Functionality at Runtime – Bringing Decorators to the Table)

Here’s a quick test you can try on your work mates: catch them off guard by asking them where Decorators can be used in PHP. They’ll probably react by saying something like “well, Zend Framework implements form decorators, and PHPUnit also uses them… and Symfony used to drop them somewhere, but I’m not quite sure where actually.” In fact, this is probably what many of us will mumble in such situations since Decorators aren’t precisely creatures with a prolific existence in the world of PHP.

这是一个可以测试您的同事的快速测试:询问他们在PHP中可以在Decorator中使用的位置,以使他们措手不及。 他们可能会说“好吧,Zend Framework实现了窗体装饰器,PHPUnit也使用了它们……而Symfony曾经将它们放到某个地方,但我不确定确切位置在哪里”。 实际上,这可能是我们许多人在这种情况下会抱怨的事情,因为Decorator并不是在PHP世界中存在丰富的生物。

Regardless, the benefits they provide of the box are pretty remarkable. They allow us to attach functionality to a given component at runtime by injecting it into a few other wrappers that share the same interface.

无论如何,他们提供的包装盒的好处是非常显着的。 它们允许我们通过将功能注入到共享同一接口的其他几个包装中,从而在运行时将功能附加到给定的组件。

It sounds like an obscure concept? Let’s try an example where an HTML element is modeled as a renderable structure implementing the following contract:

听起来像是一个晦涩的概念? 让我们尝试一个示例,其中将HTML元素建模为实现以下协定的可渲染结构:

<?php
namespace LibraryHtml;

interface HtmlElementInterface
{
    public function getText();
    public function render();
}
<?php
namespace LibraryHtml;

class Span implements HtmlElementInterface
{
    protected $text;
    
    public function __construct($text) {
        if (!is_string($text) || empty($text)) {
            throw new InvalidArgumentException(
                "The text of the element must be a non-empty string.");
        }
        $this->text = $text;
    }
    
    public function getText() {
        return $this->text;
    }
    
    public function render() {
        return "<span>" . $this->text . "</span>";
    }
}

Building a class that dumps a single <span> element won’t add a new notch to my developer belt, but it’s useful for showing how to exploit the benefits of decorators.

构建一个仅转储单个<span>元素的类不会在我的开发人员腰带上添加新的标记,但是对于显示如何利用装饰器的好处很有用。

Let’s say that we need to append some extra functionality to the object that renders the element or, in a broader range, to any other implementer of the HtmlElementInterface interface. To do so, we should first drop the common implementation inside the boundaries of an abstract decorator, like this one:

假设我们需要向呈现元素的对象或在更大范围内的HtmlElementInterface接口的任何其他实现者的对象中添加一些额外的功能。 为此,我们应该首先将通用实现放在抽象装饰器的边界内,如下所示:

<?php
namespace LibraryHtml;

abstract class AbstractHtmlDecorator implements HtmlElementInterface
{
    protected $element;

    public function __construct(HtmlElementInterface $element) {
        $this->element = $element;
    }

    public function getText() {
        return $this->element->getText();
    }

    public function render() {
        return $this->element->render();
    }
}

With the AbstractHtmlDecorator class making use of delegation, it’s easy to subclass a few concrete decorators that perform some additional tasks on the injected HTML element behind the scenes. Moreover, the ones below rely on this concept for wrapping the element’s markup inside some <p>, <div>, and <section> tags:

通过使用委托的AbstractHtmlDecorator类,可以很容易地对一些具体的装饰器进行子类化,这些装饰器在幕后对注入HTML元素执行一些其他任务。 此外,下面的代码依靠此概念将元素的标记包装在某些<p>,<div>和<section>标签内:

<?php
namespace LibraryHtml;

class DivDecorator extends AbstractHtmlDecorator
{
    public function render() {
        return "<div>" . $this->element->render() . "</div>";
    }
}
<?php
namespace LibraryHtml;

class ParagraphDecorator extends AbstractHtmlDecorator
{
    public function render() {
        return "<p>" . $this->element->render() . "</p>";
    }
}
<?php
namespace LibraryHtml;

class SectionDecorator extends AbstractHtmlDecorator
{
    public function render() {
        return "<section>" . $this->element->render() . "</section>";
    }
}

Admittedly, the role that each decorator plays is rather banal. Still, let’s see how they work:

诚然,每个装饰者扮演的角色都是平庸的。 不过,让我们看看它们是如何工作的:

<?php
use LibraryLoaderAutoloader,
    LibraryHtmlSpan,
    LibraryHtmlParagraphDecorator,
    LibraryHtmlDivDecorator,
    LibraryHtmlSectionDecorator;
    
require_once __DIR__ . "/Library/Loader/Autoloader.php";
$autoloader = new Autoloader();
$autoloader->register();

$div = new DivDecorator(
    new ParagraphDecorator(new Span("Welcome to SitePoint!"))
);
echo $div->render();

$section = new SectionDecorator(
    new DivDecorator(
        new ParagraphDecorator(new Span("Welcome to SitePoint!"))
    )
);
echo $section->render();

Constructing multiple pipelines of decorators at runtime in order to render several markup structures is a straightforward process. The sequence in which each decorator is assembled can be altered without much fuss, and it’s possible to appeal to this trick and generate disparate portions of HTML on the fly, each one exposing a different semantic meaning.

在运行时构造装饰器的多个管道以渲染几个标记结构是一个简单的过程。 每个装饰器的组装顺序可以更改,而不必大惊小怪,并且有可能吸引这种技巧并动态生成HTML的不同部分,每个部分都有不同的语义。

Although the earlier example is illustrative, it falls short when it comes to treading the road of production, since generating large chunks of markup programmatically with this method is pointless. You’re probably wondering how we can use decorators in a more useful fashion other than rendering just a few naive HTML elements.

尽管前面的示例是说明性的,但在踏上生产之路时却显得不足,因为使用此方法以编程方式生成大量标记是没有意义的。 您可能想知道我们如何能以更有用的方式使用装饰器,而不是仅渲染一些简单HTML元素。

以更实用的方式使用装饰器–设置可扩展视图模块 (Using Decorators in a More Pragmatic Fashion – Setting up an Extensible View Module)

We could model a base view class and, in turn, attach to it a bunch of decorators implementing the same interface which, among other neat things, would let us dynamically generate fancy web page headers, footers, and even outer layouts. Implementing a customizable view module that exploits decorators is a lot easier than one might think, as the whole process can be boiled down to creating the aforementioned base view class and then extending its functionality through one or more decorators according to more refined needs.

我们可以对基本视图类进行建模,然后在其上附加一堆装饰器,以实现相同的接口,除其他外,这些接口还可以让我们动态地生成精美的网页页眉,页脚,甚至外部布局。 实现一个利用装饰器的可自定义视图模块比人们想象的要容易得多,因为整个过程可以归结为创建上述基础视图类,然后根据更精细的需求通过一个或多个装饰器扩展其功能。

To get the module working, I’m going to use the same View class I wrote in the first part which implemented a segregated ViewInterface interface. With this component already coded, we can focus on a few decorators which will be responsible for rendering custom headers, footers, and layouts. Here’s the hierarchy of decorators to perform these tasks:

为了使模块正常工作,我将使用在第一部分中编写的相同的View类,该类实现了ViewInterface接口。 有了这个组件的编码,我们可以专注于一些装饰器,这些装饰器将负责呈现自定义的页眉,页脚和布局。 这是执行这些任务的装饰器的层次结构:

<?php
namespace LibraryView;

abstract class AbstractViewDecorator implements ViewInterface
{
    const DEFAULT_TEMPLATE = "default.php";
    protected $template = self::DEFAULT_TEMPLATE;
    protected $view;

    public function __construct(ViewInterface $view) {
        $this->view = $view;
    }
    
    public function render() {
        return $this->view->render();
    }
    
    protected function renderTemplate(array $data = array()) {
        extract($data);
        ob_start();
        include $this->template;
        return ob_get_clean();
    }
}
<?php
namespace LibraryView;

class OuterViewDecorator extends AbstractViewDecorator
{
    const DEFAULT_TEMPLATE = "layout.php";
    
    public function render() {
        $data["innerview"] = $this->view->render();
        return $this->renderTemplate($data);
    }
}
<?php
namespace LibraryView;

class HeaderViewDecorator extends AbstractViewDecorator
{
    const DEFAULT_TEMPLATE = "header.php";
    
    public function render() {
        return $this->renderTemplate() . $this->view->render();
    }
}
<?php
namespace LibraryView;

class FooterViewDecorator extends AbstractViewDecorator
{
    const DEFAULT_TEMPLATE = "footer.php";
    
    public function render() {
        return $this->view->render() . $this->renderTemplate();
    }
}

Each decorator is a plain wrapper for injected view objects which in turn override the base render() method in order to add some specific web page sections to the objects’ templates. The rendering process should be even easier to understand if I pitched some testable examples. So, suppose we’ve defined a couple of skeletal templates, called partial.php and layout.php, which look as follows:

每个装饰器都是注入的视图对象的简单包装,该视图对象依次重写基本render()方法,以便将某些特定的网页部分添加到对象的模板中。 如果我介绍一些可测试的示例,则渲染过程应该更容易理解。 因此,假设我们已经定义了两个骨架模板,称为partial.phplayout.php ,如下所示:

<h2><?php echo $this->heading;?></h2>
<p><?php echo $this->content;?></p>
<!doctype html>
<html>
 <head>
  <meta charset="utf-8">
  <title>My fancy web page</title>
 </head>
 <body>
  <header>
   <h1>A sample header</h1>
  </header>
  <section>
   <?php echo $innerview;?>
  </section>
  <footer>
   <p>A sample footer</p>
  </footer>
 </body>
</html>

Now, if we ever need to embed the partial straight into the outer layout, the process would be reduced to coding the following snippet:

现在,如果我们需要将部分片段直接嵌入到外部布局中,则该过程将简化为对以下代码段进行编码:

<?php
$view = new View("partial");
$view->heading = "This is the sample heading line";
$view->content = "This is the sample content";

$page = new OuterViewDecorator($view);
echo $page->render();

That was a breeze indeed. But we’re not done yet; there’s a few more nifty things to play around with. How about rendering a similar HTML5 document, this time by attaching the header and the footer at runtime? Well, let’s then define the templates bound to the corresponding decorators, namely header.php and footer.php:

那真是轻而易举。 但是我们还没有完成; 还有一些更有趣的事情。 这次如何通过在运行时附加页眉和页脚来呈现类似HTML5文档呢? 好吧,然后让我们定义绑定到相应装饰器的模板,即header.phpfooter.php

<!doctype html>
<html>
 <head>
  <meta charset="utf-8">
  <title>My fancy web page</title>
 </head>
 <body>
  <header>
   <h1>A sample header</h1>
  </header>
<footer>
   <p>A sample footer</p>
  </footer>
 </body>
</html>

With the templates in place, we can now spawn a view object containing the partial, and then drop the decorators in the desired sequence, as following:

放置好模板之后,我们现在可以生成一个包含该部分的视图对象,然后按照所需的顺序放置装饰器,如下所示:

<?php
$view = new View("partial");
$view->heading = "This is the sample heading line";
$view->content = "This is the sample content";

$page = new FooterViewDecorator(new HeaderViewDecorator($view));
echo $page->render();

While I have to admit that the process of rendering a well-formed HTML document is in this case a little bit more cumbersome and tedious than just handling a single template with some PHP code scattered through it, in a nutshell it shows how to exploit the functionality provided by a few simplistic decorators in the implementation of a customizable view module. Furthermore, considering the module’s inherent flexibility, it’s fairly easy to make it a little bit richer by pushing into its internals some extra decorators, others than the ones just implemented.

尽管我不得不承认,在这种情况下,呈现格式正确HTML文档的过程比仅处理散布有一些PHP代码的单个模板要麻烦和乏味,但简而言之,它显示了如何利用在可自定义视图模块的实现中,由一些简单修饰器提供的功能。 此外,考虑到模块的固有灵活性,通过在内部添加一些额外的装饰器(而不是刚刚实现的装饰器)来使其变得更加丰富是很容易的。

If you’re feeling bold and want to tackle that challenge, you’ll get a break on sharpening your coding skills for free.

如果您感到胆怯,并且想应对这一挑战,那么免费提高磨练技巧的机会就会有所突破。

最后的想法 (Final Thoughts)

Very few will disagree that the Composite and Decorator patterns are among the most blatant examples of the “Favor Composition over Inheritance” mantra in action. Despite of this, they’re still rather overlooked paradigms in PHP limited to the dark corners of just a few popular frameworks.

几乎没有人会反对“复合”和“装饰器”模式是“在继承上由偏爱构成”这一口头禅最明显的例子。 尽管这样,但在PHP中,它们仍然被忽略了,仅局限于一些流行框架的黑暗角落。

Even so, leveraging their abilities and employ them for manipulating view objects in a flexible manner is everything but complex. In this case, the modules showcased in this roundup are just elementary examples, a far cry away for being production ready; hopefully they should be illustrative enough for making a valid point and encouraging the use of the patterns in a more multifaceted way.

即便如此,充分利用它们的能力并以灵活的方式使用它们来操纵视图对象仍然很复杂。 在这种情况下,本综述中展示的模块只是基本示例,与准备就绪相去甚远。 希望它们应该有足够的说明性,以提出一个合理的观点,并鼓励以更加多方面的方式使用这些模式。

Image via Fotolia

图片来自Fotolia

翻译自: https://www.sitepoint.com/flexible-view-manipulation-2/

django 类视图装饰器

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值