接口应尽可能保持通用吗?

时间:2011-10-20 15:08:45

标签: c# oop interface

在开发接口时,它们应该尽可能保持通用,还是应该尝试在接口中放置尽可能多的方法,属性以保持接口数量低:例如,哪个更好1或2:

1)客户和租赁拆分为2个接口(仅与租赁相关的数据位于租赁界面,仅与客户相关的数据位于客户界面中)

interface ICustomer
{
    public string Name { get; set; }
    public string Address { get; set; }
    public string Phone { get; set; }
    public string Email { get; set; }
 }

interface IRental: ICustomer
{
    string Title { get; set; }
    decimal Cost{ get; set; }
    void Rent();      
}

2)将所有数据放入一个界面。

interface IRental
{
    string Title { get; set; }
    decimal Cost{ get; set; }
    void Rent();  
    public string Name { get; set; }
    public string Address { get; set; }
    public string Phone { get; set; }
    public string Email { get; set; }
}

关于第一种方法,扩展ICustomer接口是否有好处,或者IRental中是否只有ICustomer属性,如下所示:

interface IRental
{
    ICustomer customer {get;set;}
    string Title { get; set; }
    decimal Cost{ get; set; }
    void Rent();      
}

上述方法有哪些优点/缺点?并且有一种首选方式(一种更具可扩展性和可维护性的方式)。

8 个答案:

答案 0 :(得分:8)

查看SOLID的 Interface Segregation Principle 。胖接口可能存在问题,实施者和消费者被迫关心的事情超出了他们的需要。保持您的界面薄,高度集中。使用的示例通常是调制解调器的概念

interface Modem
{
     void Dial();
     void Hangup();
     void Send();
     void Receive();
}

Modem的实施者必须提供拨号和挂断的实现,这些是连接状态问题。然后提供发送和接收的实现,这些是数据传输问题。这些应该是两个独特的接口,它们是两个不同的职责组,也属于单一责任原则。并非所有调制解调器都可能需要这两组职责。

答案 1 :(得分:5)

您应该始终考虑Single responsibility principle。类,方法和接口应该是特定于域的项。所以恕我直言,最好将ICustomer和IRental分开。

答案 2 :(得分:4)

没有黄金法则。

就个人而言,我只是在需要时或在有意义的时候打破东西。这通常意味着理解界面试图揭示和破坏逻辑上不同的合同。

接口应始终适合于为特定目的公开特定合同 - 这可能意味着它们在实地很薄弱。它阻止了对你的界面感兴趣的人不得不暴露他们不需要订阅的东西。

在您的情况下,IRental和ICustomer是两个逻辑上独立的实体。租赁可能包含客户详细信息,但应在客户类别中进行,而不是通过平面属性。所以你的最后一段代码对我来说最明智:

interface IRental
{
    ICustomer customer {get;set;}
    string Title { get; set; }
    decimal Cost{ get; set; }
    void Rent();      
}

答案 3 :(得分:2)

我会选择选项1,它允许您独立编辑界面。

正如亚当指出“总是使用”一样,我会害怕这个术语,当设计规范发生变化或多年后升级时,发现自己可能是个难题。

答案 4 :(得分:2)

这取决于您的数据模型,但如果客户可以拥有多个租赁,或者如果有可能出现这种情况,那么租赁在案例2中获取客户信息是没有意义的。

租赁不是客户,因此选项1不适合。问问自己“租赁是一种客户还是客户的专业化”?

您提示的第三个选项是,如果您没有客户就无法租借,那么IRental可能会引用ICustomer可能是有道理的。

答案 5 :(得分:1)

我会说这个例子混淆了太多东西。租赁和客户是两种不同类型的实体。

乍一看,我会说您有客户,标题和租赁类型,其中租赁引用客户和标题。

    public class Customer
{
    string Name { get; set; }
    string Address { get; set; }
    string Phone { get; set; }
    string Email { get; set; }
}

public class Title
{
    string Name { get; set; }
    decimal Cost { get; set; }
}

public class Rental
{
    Customer Renter { get; }
    Title Media { get; }
    DateTime Due {get;}

    public void Rent(Customer, Title);
}

我不确定接口会进入它,直到你有一个可以做的关联 - 可能在租赁本身附近。

答案 6 :(得分:0)

我想到的一件事是SOLID原则中的Interface Segregation Principle如果您认为某些客户端可能不需要实现该接口的所有方法/属性,则可以分割接口,而不是提供一个胖接口。通过明确告诉他们您希望为某些特定功能实现某些方法,它也可能使您有意要求客户端更清晰地实现接口

对于问题的第二部分,我再次认为使用“组合而不是继承”的原则可能适用,但这取决于您尝试解决的确切问题。如果Customer是可以在运行时切换的行为,那么您应该将其用作IRental接口的成员,这基本上是策略设计模式的前提。 / p>

答案 7 :(得分:0)

对于IRental来说,拥有ICustomer类型的字段PrimaryCustomer,并且可能是IList< Customer>称为RelatedCustomers之类的东西(例如,如果客户表示他计划将物品与其他也是客户的人一起使用;如果租赁出现问题但无法联系到客户,则此类信息可能会有用)。但是,如果每个客户都要租一件东西,那么IRental应该只能继承ICustomer。

否则,假设Rental1和Rental2都具有名称“John Smith”。在'Rental1.Name =“Fred Jones”'之后,Rental2.Name会是什么?尚不清楚Rental1和Rental2是否指向同一客户。如果有客户财产,这种歧义就会消失。如果Object.ReferenceEquals(Rental1.Customer,Rental2.Customer),那么对Rental1.Customer.Name的更改将影响Rental2.Customer.Name(它是相同对象的相同属性)。如果Rental1.Customer是与Rental2.Customer不同的对象,那么对一个的更改不应该影响另一个。

顺便提一下,我建议定义一个接口IReadableCustomer可能会很好,其中属性只有“getters”,并且ICustomer继承自IReadableCustomer(ICustomer必须在其属性定义中添加“new”限定符)避免愚蠢的“歧义”消息)。在C#中,这样的更改不需要任何额外的代码来实现ICustomer,但可以更好地控制允许谁更改客户记录。