首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >C# LSP构造函数参数和保护子句

C# LSP构造函数参数和保护子句
EN

Stack Overflow用户
提问于 2016-12-22 04:51:44
回答 2查看 634关注 0票数 0

我一直在阅读关于Liskov替换原则(LSP)的文章,我对如何正确地遵守它感到有点困惑。尤其是在使用接口和子类时。

例如,如果我有一个基类:

代码语言:javascript
运行
复制
public abstract class AccountBase
{
    private string primaryAccountHolder;

    public string PrimaryAccountHolder 
    { 
        get { return this.primaryAccountHolder; } 
        set
        {
            if (value == null) throw ArgumentNullException("value");
            this.primaryAccountHolder = value;
        }
    }

    public string SecondaryAccountHolder { get; set; }

    protected AccountBase(string primary)
    {
        if (primary == null) throw new ArgumentNullException("primary");
        this.primaryAccountHolder = primary;
    }
}

现在假设我有两个从基类继承的帐户。其中一个需要SecondaryAccountHolder。向子类添加null防护是违反LSP的,对吗?那么,我如何设计我的类,使它们不违反LSP,但我的一个子类需要一个辅助帐户持有者,而另一个不需要?

将这个问题与一个事实相结合,即可能有大量不同类型的帐户,这些帐户需要通过工厂或返回构建器或其他东西的工厂生成。

对于接口,我也有同样的问题。如果我有一个接口:

代码语言:javascript
运行
复制
public interface IPrintsSomething
{
    void PrintSomething(string text);
}

在任何实现IPrintsSomething的类上为文本添加空保护子句不是违反了LSP吗?你如何保护你的不变量?这是正确的词,对吗?

EN

回答 2

Stack Overflow用户

发布于 2016-12-24 11:02:12

你应该研究tell-don ask,以及命令/查询分离,你可以从这里开始:https://pragprog.com/articles/tell-dont-ask

你应该努力告诉对象你想让他们做什么;不要问他们关于他们的状态的问题,做出决定,然后告诉他们该做什么。

总是有一些你想要对属性做的事情,不要让对象去做,告诉它去做一些事情。

而不是像这样问它并做出决定:

代码语言:javascript
运行
复制
string holders = account.PrimaryAccountHolder;
if (accountHolder.SecondaryAccountHolder != null)
{
    holders += " " + accountHolder.SecondaryAccountHolder;
}

告诉它:

代码语言:javascript
运行
复制
string holders = account.ListAllHoldersAsAString();

理想情况下,您应该实际告诉它您实际想要对该字符串做什么:

代码语言:javascript
运行
复制
account.MailMergeAllAccountHoldersNames(letterDocument);

现在,处理两个帐户持有者的逻辑在子类中。可能是一个、两个或n个帐户持有人,调用代码并不关心也不需要知道。

至于LSP,如果有一个正式的(或非正式的)文档合同,说客户必须从一开始就检查第二个持有者上的null,那就没问题了。这不是很好,但任何空指针异常都将是客户端的错误,因为没有正确使用该类。(请注意,添加布尔属性并不能改善这一点,它只是可能更具可读性,即有人在写入IList.IsReadOnly之前检查它吗?!)

但是,如果您从双重持有者帐户开始,然后添加了这样的条件,即第二个帐户持有者稍后可以对单个帐户执行null,那么您更改了合同,并且单个帐户的实例可能会破坏现有代码。如果你完全控制了所有使用账户的地方,那么你就可以这么做了,如果你改变的是一个公共api,那就是另一回事了。

但在这种情况下,tell-don-ask避免了整个问题。

票数 1
EN

Stack Overflow用户

发布于 2016-12-22 05:07:52

那么,我如何设计我的类,使它们不违反

,但是我的一个子类需要一个辅助帐户持有者,而另一个不需要?

解决这个问题的方法是通过将这种可变性呈现给基类的契约。它可能看起来像这样(省略了不必要的实现细节):

代码语言:javascript
运行
复制
public abstract class AccountBase
{
    public string PrimaryAccountHolder 
    { 
        get { … } 
        set { … }
    }

    public string SecondaryAccountHolder
    { 
        get { … } 
        set 
        { 
            …  
            if (RequiresSecondaryAccountHolder && value == null) throw …;
            …  
        } 
    }

    public abstract bool RequiresSecondaryAccountHolder { get; }
}

那么您就没有违反LSP,因为AccountBase的用户可以决定他们是否必须提供SecondaryAcccountHolder的值。

和我对接口也有同样的问题。…在任何实现IPrintsSomething的类上为文本添加空保护子句不是违反了LSP吗?

使验证成为接口契约中显而易见的一部分。多么?文档中,实现者必须检查nulltext的值。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/41271696

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档