防止/限制方法继承


Prevent/Restrict Method Inheritance

我有一个应用程序,其中许多对象都在扩展一个抽象类,该类定义了create() edit() retrieve()delete()等方法。由于每个子类对这些函数使用相同的逻辑,抽象类定义了默认行为,在少数需要扩展的情况下,子类可以覆盖或使用我内置的钩子。

现在我遇到的情况是,一些子类需要变为不可变的,这意味着它们不应该有edit()delete()方法。在我看来,这个需求听起来像是一个名为immutable的接口的工作,不可变类可以实现这个接口。问题是接口并不能阻止方法被调用,它们只是强制方法的存在。所以这显然是行不通的。

创建两个父类,一个用于可变对象,另一个用于不可变对象,这很难看,可能会在维护过程中遇到问题。我可以让不可变对象用一个什么都没做的空方法覆盖有问题的方法,但这看起来也很混乱,而且我当时没有做正确的OOP。

那么,对于允许一大组类都继承一组方法,但其中一些类不继承所有方法的最佳方式,你会建议什么呢?(有问题的应用程序是用php编写的,但任何语言的通用OOP技术仍然有帮助)。

创建一个不可变基类作为基类的子级。不可变基应该实现edit()和delete()的最终重写,这两个重写不起任何作用或引发错误。

Final,确保所有不可变的子级都不能编辑或删除

此策略的奖金

  • 通过测试instanceof不可变的基本,轻松检查对象是否是不可变的

  • 通过修改扩展的内容,可以轻松地将对象从不可变更改为不可变

实际上,创建具有空方法或抛出错误的类是不好的——这样的方法令人困惑,它们占用空间,什么也不做。

更好的方法是将不可变类作为基类,并通过添加修改方法使可变类扩展它。通过这种方式,每个类只有那些真正属于它们的方法。

我喜欢Java的方法。引发异常。创建一个UnsupportedOperationException,对于那些不应该使用特定方法的实现,抛出一个,让用户知道他们不能将此功能用于此实现。

我想抛出的另一个想法作为可能的解决方案。类可以实现如下所示的接口:

Interface Immutable {
    const immutable = true;
}

然后Base抽象类可以用编写delete()edit()方法

if (!$this->immutable) {
    //do_stuff
}

这也可以很好地扩展到类的其他分类,如NonDeletable和NonEditable,以允许更细粒度的行为。

这里有一个超短的解决方法,使您的方法成为最终方法,并从开始

    if(self::class!=static::class) return;#or throw an error

它不会阻止继承本身,但方法在子类中不起作用(是否有错误取决于您)。

从PHP 5.4开始,您可以使用Traits。

例如,您可以创建一个基类,该基类只包括所有子类所具有的方法:

class EntityManager {
    public function create() {/*...*/}
    public function retrieve() {/*...*/}
}

然后你可以定义几个特征:

trait EditTrait {
    public function edit() {/*...*/}
}
trait DeleteTrait {
    public function delete() {/*...*/}
}

然后创建一个不可变的子类,如下所示:

class LogManager extends EntityManager {
    ...
}

像这样一个可变的子类:

class ContactManager extends EntityManager {
    use EditTrait;
    use DeleteTrait;
    ...
}

与这里的一些其他解决方案相比,特性具有一些优势,例如:

  • 无代码重复
  • 单个基类
  • 不起作用或没有意义的方法不会出现在不支持它们的类上(对文档和api尤其重要)