设计模式 - 反模式调用超类 - 避免或保留。

5

我最近注意到,在某些情况下我喜欢做的事情(调用 super)实际上是一种反模式。(http://en.wikipedia.org/wiki/Call_super

所以我的问题是:

如何做以下的事情(我们大多数人需要的基本操作,即向对象添加一些细节)。我只是向保存在对象变量中的数组添加一个新元素:

   <?php
   class A {
       // bla bla bla
       public function __construct() {
            $this->_data['newDetail'] = "Ipiicaei";
       }
   }


   class B extends A {
       // bla bla bla
       // Override constructor, I need to add one more detail for this class
       public function __construct() {
            parent::__construct();
            $this->_data['newDetailSubcls'] = "Something";
       }
   }

现在...如果我不调用父类构造函数来添加第一个元素,我有两种可能性:
  1. 在每个子类中添加父类通常执行的代码部分,并将其从父类中删除。所以如果我使用999个类进行扩展,我将在父类中重复999*行代码。这对我来说听起来很糟糕。
  2. 从在子类中实现的父类中调用一个方法(模板方法模式,他们推荐这样做)。因此,如果只需要为一个类添加该方法,而其他998个类的行为与父类完全相同,在每个类中都添加一个空函数。这对我来说也听起来很糟糕。
请记住,我的示例很简单(这就是示例应该的方式),但父类和/或子类可能会执行复杂的操作。
我可以看到在某些情况下调用super可能不好。但在这种情况下,我认为它还可以。
那么...你会怎么做?忽略调用super是一种反模式,并像我一样做(或者如果证明我的方法不好,那么以前的做法)吗?还是...怎么做?
1个回答

4
使用构造函数是说明这种反模式如何出现的一个不好的例子,因为在 PHP 中,与大多数支持继承的语言一样,如果子类没有定义构造函数,则可以从父类中继承它,就像普通的类方法一样...(来自PHP手册)。
如果在其他 998 个类中的任何一个中故意不包含构造函数,则基础构造函数将被调用,使得在每个派生类中添加构造函数几乎成为必要。这就是为什么不建议在构造函数中添加可能会改变受保护字段/属性状态或调用可能在派生类中被覆盖的虚方法的逻辑。
然而,这种反模式实际上表达了以下内容:
请注意,调用父级的要求才是反模式(来自Wikipedia
因此,只要派生类不需要调用基类版本的方法,就可以调用该方法的基类版本。即使从未调用基本方法,派生类也必须正确地行为。这将避免派生类覆盖虚拟方法但不实现某些特殊要求(可能是由于缺乏有关基类的文档),此时派生类不符合系统的预期行为,可能导致系统崩溃或行为异常,这是Liskov替换原则的典型违反。
在您的特定示例中,此反模式不适用:您正在扩展构造函数,根据定义,必须调用它们,因为它们将类实例化为有用状态。在大多数情况下,这是您想要的。

是的...也许这不是最好的例子。我们假装那些不是构造函数。无论如何,你回答的第二部分有点回答了我的问题,所以...谢谢(我会在7小时内接受答案,也许在那段时间内我会得到第二个意见)。 - zozo
是的,我尝试不过多关注您的示例,因为我认为它可能会误导。通常情况下,模板方法模式非常适合这些类型的场景;如果有一种情况下派生类需要绕过功能(用空方法覆盖基类),那么你第一次就不应该从那个基类继承。无论如何,很高兴能够帮助。 - rae1

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接