【问题标题】:Design pattern question: encapsulation or inheritance设计模式问题:封装还是继承
【发布时间】:2010-05-07 03:37:23
【问题描述】:

我有一个问题困扰了我很久。我正在构建一个模板引擎,它有两个主要类Template.phpTag.php,还有一堆扩展类,如Img.phpString.php

程序是这样工作的:

一个模板对象创建一个标签对象。每个标签对象决定实现哪个扩展类(img、string等)。

Tag类的重点是为每个扩展类提供帮助函数如wrap('div'),addClass('slideshow')

每个 Img 或 String 类 用于呈现特定于所需内容的代码,因此$Img->render() 会给出类似<img src='blah.jpg' /> 的代码

我的问题是:

我是否应该像这样封装 Tag 对象中的所有扩展功能:

Tag.php

function __construct($namespace, $args) {
    // Sort out namespace to determine which extension to call
    $this->extension = new $namespace($this); // Pass in Tag object so it can be used within extension

    return $this; // Tag object
}

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

Img.php

    function __construct(Tag $T) {
        $args = $T->getArgs();
        $T->addClass('img');
    }

    function render() {
        return '<img src="blah.jpg" />';
    }

用法:

$T = new Tag("img", array(...);
$T->render();

....或者我应该创建更多的继承结构,因为“Img 是一个标签”

Tag.php

public static create($namespace, $args) {
    // Sort out namespace to determine which extension to call
    return new $namespace($args);

}

Img.php

class Img extends Tag {
    function __construct($args) {
        // Determine namespace then call create tag
        $T = parent::__construct($namespace, $args);
    }

    function render() {
        return '<img src="blah.jpg" />';
    }
}

用法:

$Img = Tag::create('img', array(...));
$Img->render();

我确实需要一个用于创建自定义标签的通用接口,即我可以实例化 Img(...) 然后实例化 String(...),我确实需要使用 Tag 实例化每个扩展。

编辑:也只是为了澄清:Tag 类具有所有扩展类共有的功能,Tag 类中不应该有任何方法需要在扩展类中实现。标签类仅提供辅助函数。

我知道这是一个有点模糊的问题,我希望你们中的一些人过去已经处理过这个问题,并且可以预见到选择每种设计模式时会遇到某些问题。如果您有任何其他建议,我很想听听。

谢谢! 马特·穆勒

【问题讨论】:

    标签: php design-patterns templates


    【解决方案1】:

    继承比将不同标签的所有功能封装在单个Tag 类中更有意义。关注点分离很重要,因此这里最好采用基于继承的方法。否则,您最终会在单个类中拥有一堆不同的特定于标签的逻辑,这很糟糕。这是维护的噩梦!

    如果你有Img 特定的逻辑,把它放在它自己的类中。您可以将所有常用方法放在Tag 类中。如果这是 Java,我会让 Tag 成为抽象类甚至接口,并让不同的实现(ImgDiv 等)扩展(在抽象类的情况下)或实现(在接口的情况)。

    更好的方法是拥有一个Tag 接口,然后是一个实现所有常见逻辑的AbstractTag 抽象类。然后您的特定标签可以实现Tag 接口并扩展AbstractTag。但是,我不知道这在 PHP 中是否完全可行,但您可以尝试做类似的事情。

    【讨论】:

    • 非常感谢您的建议。静态 Tag::create(...) 怎么样?自从我对严重的 OOP 感到沮丧和肮脏以来已经有一段时间了。也只是为了澄清:Tag 类具有所有扩展类共有的功能,Tag 类中不应该有任何方法需要在扩展类中实现。标记类仅提供辅助函数。
    • 我会做类似Img::create() 的事情。 AbstractTag 中的构造函数应该是抽象的,因此创建标签的唯一方法是调用特定于标签的构造函数。您可以这样做,也可以在 Tag 类中创建特定标签对象的工厂。
    【解决方案2】:

    new Tag('img')Tag::create('img') 在我看来都没有说服力。我认为,使用哪个标签的决定是在错误的地方 - 在 Tag 类中 - 虽然它显然属于模板。 Tag 及其后代不应控制它们的使用方式。

    我建议如下:创建标签的层​​次结构

     abstract class Tag { ...common methods.... }
     class ImgTag extends Tag { ...image specific methods... }
     class SpanTag extends Tag { ...image specific methods... }
    

    并且在模板中只需要在需要时使用新的 WhatTag

     class Template...
        function insertImage
          $tag = new ImgTag($atts);
          $tag->render();
    

    如果标签是在多个地方创建的,最好有一个工厂方法,但它仍然应该属于模板

     class Template...
        function createImage
          return new ImgTag($atts);
    
        function insertOneImage
          $tag = $this->createImage
          $tag->render();
    
        function insertAnotherImage
          $tag = $this->createImage
          $tag->render();
    

    作为一般建议,不惜一切代价避免使用静态方法。静态只是全局函数的花哨别名,没有任何面向对象的东西。

    【讨论】:

    • 好建议!对于我的情况,我看到的一个问题是 Template 对 createImage(或 insertImage)有特定的调用。我希望模板不知道可以使用哪些插件或扩展(img,span)类。这使得扩展非常容易。我同意标签的创建应该在模板中。我忘了添加,但是新的 Tag(..) 和 Tag.create(..) 在 Template.php 中。非常感谢您的撰写!
    猜你喜欢
    • 1970-01-01
    • 2010-12-06
    • 2011-04-22
    • 2012-08-02
    • 2012-09-04
    • 2011-01-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多