2

我看到的一个非常常见的模式(我选择 Zend Framework,只是因为我在这个问题的那一刻正在处理它),是这样的:

class My_Form extends Zend_Form {
     public function init() {
          $this->addElement();
     }
}

Zend_Form 不是一个抽象类,但完全可以单独使用。这似乎被“推荐”为将您的表单“封装”成一个不错的类的地方。

这是否违反了 Liskov 替换原则?Zend_Form 的每个子类都将具有与基类截然不同的行为。为此使用组合会更好,还是我完全误解了这个原则?

4

2 回答 2

0

Zend_Form 是为继承而设计的。在使用 Zend_Form 时,每个人都应该牢记这一点——实际上它不是必需的 Zend_Form,但可能是它的子类。因此,如果任何程序依赖 Zend_Form 来完全按照它的方式运行,而不是按照它的子类可以运行的方式 - 该程序是错误的。正如 Liskov 原则所说,它不是“使用基类”,而是在滥用它。

Zend_Form 主要由 Zend 框架使用,我确信它正确使用它。

我认为对于此类为继承而设计并用作基于某些框架的应用程序构建块的类,“行为”的定义应该更加抽象——将一些细节留给子类,即使类本身不是抽象的。我会说 Zend_Form 的行为是“呈现一些html 并使用一些验证规则”。在这个意义上, Zend_Form 的所有子类都以相同的方式表现。Zend_Form 非抽象只是定义了默认行为,使其更易于使用。

另外为了让它更学术一点,我可以从中做两节课。一个应该是抽象的——所有形式的基类。另一个空表单,它的行为与 Zend_Form 现在的行为完全相同,并且可以单独使用。所以它会像

// sorry, I don't like PHP so here goes java 
public abstract class ZendForm{/*implementation here and NO abstract methods*/} 
public final class DefaultZendForm extends ZendForm{/*nothing here*/}

这将消除对 Liskov 原则的任何混淆,但可能不会为程序增加任何实际价值。

子类应该与超类有所不同,否则创建子类没有意义。而且您总是可以滥用这种差异并编写一个适用于超类而对子类失败的程序。但这不合理。完全呈现默认(空)表单不是 Zend_Form 合同的一部分。只有属于类契约的行为才是 LSP 的主题。

于 2009-06-09T22:03:02.477 回答
0

Liskov 的替换原则指出,如果程序模块正在使用 Base 类,那么对 Base 类的引用可以替换为 Derived 类,而不会影响程序模块的功能。

那么 MyForm 与 ZendForm 有什么不同呢?它会改变程序的功能吗?

另外,检查这 2 个项目符号(来自Wikipedia 文章):

  • 不能在子类中加强先决条件。
  • 后置条件不能在子类中被削弱。

也许您会发现 MyForm 与 ZendForm 并没有太大的不同,您可以安全地使用继承。

于 2009-05-21T20:38:05.960 回答