我听说利斯科夫替换原则(LSP)是面向对象设计的基本原则。它是什么?它的一些使用例子是什么?


当前回答

我建议您阅读这篇文章:违反利斯科夫替换原则(LSP)。

你可以在那里找到一个解释,什么是利斯科夫替换原则,一般线索帮助你猜测你是否已经违反了它,一个方法的例子,将帮助你使你的类层次结构更安全。

其他回答

以下是这篇文章的摘录,很好地澄清了事情:

(. .为了理解一些原则,重要的是要意识到它什么时候被违反了。这就是我现在要做的。

违反这一原则意味着什么?它意味着对象不履行用接口表示的抽象所施加的契约。换句话说,这意味着您错误地识别了抽象。

考虑下面的例子:

interface Account
{
    /**
     * Withdraw $money amount from this account.
     *
     * @param Money $money
     * @return mixed
     */
    public function withdraw(Money $money);
}
class DefaultAccount implements Account
{
    private $balance;
    public function withdraw(Money $money)
    {
        if (!$this->enoughMoney($money)) {
            return;
        }
        $this->balance->subtract($money);
    }
}

是否违反LSP?是的。这是因为帐户合同告诉我们帐户将被提取,但情况并非总是如此。那么,我该怎么做才能解决这个问题呢?我只是修改了合同:

interface Account
{
    /**
     * Withdraw $money amount from this account if its balance is enough.
     * Otherwise do nothing.
     *
     * @param Money $money
     * @return mixed
     */
    public function withdraw(Money $money);
}

Voilà,现在合同已得到满足。

这种微妙的违反通常会使客户有能力区分所使用的具体对象之间的差异。例如,给定第一个Account的契约,它看起来像下面这样:

class Client
{
    public function go(Account $account, Money $money)
    {
        if ($account instanceof DefaultAccount && !$account->hasEnoughMoney($money)) {
            return;
        }
        $account->withdraw($money);
    }
}

而且,这自动违反了开闭原则(即取款要求)。因为你永远不知道如果违反合同的对象没有足够的钱会发生什么。它可能什么都不返回,可能会抛出异常。所以你必须检查它是否hasEnoughMoney()——这不是接口的一部分。因此这种强制的依赖于具体类的检查违反了OCP。

这一点也解决了我经常遇到的关于LSP违反的误解。它说:“如果父母的行为在孩子身上改变了,那么它就违反了LSP。”然而,事实并非如此——只要孩子不违反父母的契约。

以Board数组的形式实现ThreeDBoard会有用吗?

也许你想把不同平面上的ThreeDBoard切片作为一个板。在这种情况下,您可能希望为Board抽象出一个接口(或抽象类),以允许多种实现。

就外部接口而言,您可能希望为TwoDBoard和ThreeDBoard提取一个Board接口(尽管上述方法都不适合)。

利科夫替换原则指出,如果程序模块使用基类,则基类的引用可以被派生类替换,而不会影响程序模块的功能。

派生类型必须能够完全替代它们的基类型。

示例- java中的协变返回类型。

利斯科夫替换原理

被重写的方法不应该保持为空 被重写的方法不应该抛出错误 基类或接口行为不应该因为派生类行为而进行修改(重做)。

到目前为止,我发现LSP最清晰的解释是“利斯科夫替换原则说,派生类的对象应该能够替换基类的对象,而不会给系统带来任何错误,也不会修改基类的行为”。文中给出了违反LSP的代码示例并进行了修复。