1

我有一个这样的构造函数:

public function __construct($data)
{
    $this->_data = new SimpleXMLElement($data);
}

这很好。但是我想给机会传递 simplexmlelement 或字符串。

就像是:

public function __construct($data)
{
    if (is_string($data)) {
        $data = new SimpleXMLElement($data);
    }
    $this->_data = $data;
}

这是不好的做法吗?

我的想法是客户端代码可以决定它想要预先做多少工作。但这会引起任何问题吗?

4

1 回答 1

6

好吧,解决这个问题的一种方法是将问题移出那里,实际上很简单:

public function __construct(SimpleXMLElement $data)
{
    $this->_data = $data;
}

然后,您只需在其中传递一个 SimpleXMLElement 并可以决定何时构造对象是来自字符串还是另一个 SimpleXMLElement 或者不是。

如您所见,这使代码更加灵活。遵循两个简单的规则:

  • 防止new在构造函数中使用关键字。
  • 减少if.

如果您现在想知道可以将创建对象的代码放在哪里(例如,如果这是您将在多个地方使用的逻辑并且您害怕复制它),您可以考虑添加一些静态辅助方法:

public static function createFromXmlString($string) {
    return static::createFromSimpleXmlElement(
        simplexml_load_string($string)
    );
}

public static function createFromSimpleXmlElement(SimpleXMLElement $element) {
    return new static($element);
}

public function __construct(SimpleXMLElement $data)
{
    ...

用法:

$foo = foo::createFromXmlString('<root/>');

或者创建一个完成这项工作的工厂对象(将对象创建代码从对象中移开)。

静态全局类方法通常更容易引入,因此您通常可以即时编写它们,但如果有两个以上,通常会开始考虑如何再次删除这些函数,例如将代码移动到工厂中。


这是[处理多种输入类型的构造函数]不好的做法吗?

有点像,因为您new在构造函数中使用了关键字。它破坏了测试并且你使用的类名new是一个隐藏的依赖项(你不想要那些;参见为什么不“在对象构造函数中实例化一个新对象”? [实际上不是一个很好的问答,只是寻找代码气味关于这个以及控制反转/依赖注入的讨论——更新:更多这种材料在我的脑海中:缺陷:构造函数做真正的工作(约 2008 年 11 月;由 Miško Hevery)(还有这个家伙在 Youtube 上的视频,他有很好的口音和很好的解释事物的风格)])

不良做法的另一部分是您倾向于在同一个构造函数中添加越来越多的案例来处理。所以它会随着时间的推移而变化,而实际上对象并没有真正改变。

我想说这是一个常见的错误(也是捷径),因为生活中没有那么多对与错,只是试着自己找出什么是你的决定点。已经在这里问是一个好兆头,我会说,你开始敏锐你的感官。

我的想法是客户端代码可以决定它想要预先做多少工作。但这会引起任何问题吗?

首先,区分“客户端”代码和目标代码是一个很好的理解。你清楚地展示了你认为的好处。

另一方面,一个对象一次应该做一件事,并且创建一个新对象也是一个关键阶段。如果您开始将大量代码和逻辑放入构造函数中,事情往往会变得复杂。它只是一个太重要的地方,不能引入复杂的代码(例如,任何超过三行的东西都是复杂的)。构造函数应该只初始化新对象,就是这样。

无论我在答案的第一部分中给出了哪些建议,更多的 OOP 风格可能如下:

abstract class Foo {...}

class FooString extends Foo {
    public function __construct($string) {
        ...
    }
}

class FooSimpleXML extends Foo {
    public function __construct(SimpleXMLElement $element) {
        ...
    }
}

您有一种类型/对象,即Foo. 随着您的代码库的增长,您的客户端代码对获取 a 有不同的要求Foo,但它始终需要一个Foo. 通过创建基本类型的子Foo类型,不得更改旧代码,并且可以为更改的需求添加新代码。

这可能是最干净的方法,但是,您需要知道这Foo是不同的类型,而不是某种辅助对象。

于 2013-08-28T19:44:00.520 回答