这是违反《里斯科夫换人原则》的前提吗 [英] Is this precondition a violation of the Liskov Substitution Principle

查看:49
本文介绍了这是违反《里斯科夫换人原则》的前提吗的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有3类, Account CappedAccount UserAccount

CappedAccount UserAccount 都扩展了 Account .

帐户包含以下内容:

abstract class Account {
   ...
   /**
   * Attempts to add money to account.
   */
   public void add(double amount) {
      balance += amount;
   }
}

CappedAccount 会覆盖以下行为:

public class CappedAccount extends Account {
   ...
   @Override
   public void add(double amount) {
      if (balance + amount > cap) { // New Precondition
         return;
      }
      balance += amount;
   }
}

UserAccount 不会覆盖 Account 中的任何方法,因此无需说明.

UserAccount doesn't override any methods from Account, so it doesn't need to be stated.

我的问题是, CappedAccount#add 是否违反了LSP,如果确实违反了LSP,我该如何设计使其符合LSP.

My question is, does CappedAccount#add violate LSP, and if it does, how can I design it to comply with LSP.

例如, CappedAccount 中的 add()是否算作加强前提条件"?

For example, does add() in CappedAccount count as "strengthening preconditions"?

推荐答案

TLDR;

if (balance + amount > cap) {
    return;
}

不是先决条件,而是不变式,因此(本身)不违反《里斯科夫替代原理》.

is not a precondition but an invariant, hence not a violation (on his own) of the Liskov Substition Principle.

现在是实际答案.

真正的前提是(伪代码):

A real precondition would be (pseudo code):

[requires] balance + amount <= cap

您应该能够强制执行此先决条件,即检查条件并在不满足条件的情况下引发错误.如果您确实执行了前提条件,则会看到LSP被违反:

You should be able to enforce this precondition, that is check the condtion and raise an error if it is not met. If you do enforce the precondition, you'll see that the LSP is violated:

Account a = new Account(); // suppose it is not abstract
a.add(1000); // ok

Account a = new CappedAccount(100); // balance = 0, cap = 100
a.add(1000); // raise an error !

子类型的行为应类似于其父类型(见下文).

The subtype should behave like its supertype (see below).

加强"前提条件的唯一方法是加强不变式.因为在每个方法调用之前之前和之后,不变性均应为true.增强的不变式不会违反LSP(自己),因为在方法调用之前为该不变式免费提供了 :初始化时为true,因此在第一个方法调用之前为true.因为它是不变的,所以在第一个方法调用之后它是正确的.逐步执行,在下一个方法调用之前总是正确的(这是一个数学归纳法...).

The only way to "strengthen" the precondition is to strenghten the invariant. Because the invariant should be true before and after each method call. The LSP is not violated (on his own) by a strengthened invariant, because the invariant is given for free before the method call: it was true at the initialisation, hence true before the first method call. Because it's an invariant, it is true after the first method call. And step by step, is always true before the next method call (this is a mathematicual induction...).

class CappedAccount extends Account {
    [invariant] balance <= cap
}

在方法调用之前和之后,不变性应为true

The invariant should be true before and after the method call:

@Override
public void add(double amount) {
    assert balance <= cap;
    // code
    assert balance <= cap;
}

您将如何在 add 方法中实现该目标?您有一些选择.可以的:

How would you implement that in the add method? You have some options. This one is ok:

@Override
public void add(double amount) {
    assert balance <= cap;
    if (balance + amount <= cap) {
        balance += cap;
    }
    assert balance <= cap;
}

嘿,但这就是您所做的!(有一点点不同:这个有一个出口来检查不变量.)

Hey, but that's exactly what you did! (There is a slight difference: this one has one exit to check the invariant.)

这也是,但是语义不同:

This one too, but the semantic is different:

@Override
public void add(double amount) {
    assert balance <= cap;
    if (balance + amount > cap) {
        balance = cap;
    } else {
        balance += cap;
    }
    assert balance <= cap;
}

这也是,但语义是荒谬的(或关闭帐户?):

This one too but the semantic is absurd (or a closed account?):

@Override
public void add(double amount) {
    assert balance <= cap;
    // do nothing
    assert balance <= cap;
}

好的,您添加了一个不变式,而不是前提条件,这就是为什么不违反LSP的原因.答案结束.

Okay, you added an invariant, not a precondition, and that's why the LSP is not violated. End of the answer.

但是...这并不令人满意: add 试图向帐户添加资金".我想知道这是否成功!让我们在基类中尝试一下:

But... this is not satisfying: add "attempts to add money to account". I would like to know if it was a success!! Let's try this in the base class:

/**
* Attempts to add money to account.
* @param amount  the amount of money
* @return True if the money was added.
*/
public boolean add(double amount) {
    [requires] amount >= 0
    [ensures] balance = (result && balance == old balance + amount) || (!result && balance == old balance)
}

以及实现,带有不变式:

And the implementation, with the invariant:

/**
* Attempts to add money to account.
* @param amount  the amount of money
* @return True is the money was added.
*/
public boolean add(double amount) {
    assert balance <= cap;
    assert amount >= 0;
    double old_balance = balance; // snapshot of the initial state
    bool result;
    if (balance + amount <= cap) {
        balance += cap;
        result = true;
    } else {
        result = false;
    }
    assert (result && balance == old balance + amount) || (!result && balance == old balance)
    assert balance <= cap;
    return result;
}

当然,没有人会像那样编写代码,除非您使用Eiffel(这可能是个好主意),但是您会发现这个主意.这是一个没有所有条件的版本:

Of course, nobody writes code like that, unless you use Eiffel (that might be a good idea), but you see the idea. Here's a version without all the conditions:

public boolean add(double amount) {
    if (balance + amount <= cap) {
        balance += cap;
        return true;
    } else {
        return false;
}

请注意原始版本的LSP(如果对于每个类型为 S 的对象 o_1 ,都有一个类型为 o_2 的对象 T ,这样对于根据 T 定义的所有程序 P ,当 o_1 代替 o_2 ,则 S T 的子类型)被违反.您必须定义适用于每个程序的 o_2 .选择一个上限,例如 1000 .我将编写以下程序:

Please note the the LSP in its original version ("If for each object o_1 of type S there is an object o_2 of type T such that for all programs P defined in terms of T, the behavior of P is unchanged when o_1 is substituted for o_2, then S is a subtype of T") is violated. You have to define o_2 that works for each program. Choose a cap, let's say 1000. I'll write the following program:

Account a = ...
if (a.add(1001)) {
    // if a = o_2, you're here
} else {
    // else you might be here.
}

这不是问题,因为,当然,每个人都使用LSP的弱化版本:我们不希望行为者保持不变(例如,子类型的兴趣有限,例如,考虑数组列表与链接列表)),我们希望保留所有该程序的理想属性" (请参阅

That's not a problem because, of course, everyone uses a weaken version of the LSP: we don't want the beahvior to be unchanged (subtype would have a limited interest, performance for instance, think of array list vs linked list)), we want to keep all "the desirable properties of that program" (see this question).

这篇关于这是违反《里斯科夫换人原则》的前提吗的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆