接口应该尽可能保持通用

本文关键字:尽可能 接口 | 更新日期: 2023-09-27 18:12:06

在开发接口时,它们应该尽可能保持通用,还是应该尝试在接口中放置尽可能多的方法,属性以保持接口的数量较少:作为一个例子,哪个更好: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; }
}

同样关于第一种方法,扩展iccustomer接口是否有好处,或者应该在IRental中像下面这样有一个iccustomer属性:

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

以上方法的优点/缺点是什么?是否有更好的方法(更易于扩展和维护的方法)。

接口应该尽可能保持通用

查看SOLID的接口隔离原则。胖接口可能会有问题,实现者和消费者被迫关心比他们需要的更多的东西。保持你的界面简洁和高度集中。经常使用的一个例子是调制解调器

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

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

您应该始终考虑单一责任原则。类、方法和接口应该是特定于领域的项目。所以我认为最好将iccustomer和IRental分开。

没有什么金科玉律。

就我个人而言,我只在需要或有意义的时候才分解东西。这通常意味着理解接口试图公开什么,并分解逻辑上不同的契约。

接口应该总是为特定的目的而暴露特定的契约——这可能意味着它们在地面上很薄。它可以防止对你的界面感兴趣的人不得不公开他们不需要订阅的东西。

在您的示例中,IRental和iccustomer是两个逻辑上独立的实体。租赁可能包含客户详细信息,但应该在客户类中这样做,而不是通过平面属性。所以你最后的代码对我来说是最明智的:

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

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

正如Adam指出的"总是使用",我会害怕这个术语,当设计规范发生变化或多年后升级时,它可能会让你陷入困境。

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

租赁者不是客户,所以选项1并不适合。问问你自己"租赁是一种客户还是一种专业化的客户?"

您暗示的第三个选项,对于IRental有一个对iccustomer的引用可能是有意义的,假设没有Customer就不能有Rental。

我想说这个例子把事情混合得太多了。租赁和客户是两种不同类型的实体。

乍一看,我会说您有Customer, Title和Rental类型,其中Rental引用了Customer和Title。

    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);
}

我不确定接口进入它,直到你有一个可以做的联系——可能是围绕租赁本身。

我想到的一件事是来自SOLID原则的接口隔离原则。如果您认为某些客户端可能不需要实现该接口的所有方法/属性,那么拆分接口而不是提供一个胖接口是很好的。它还可能通过显式地告诉客户您希望为某些特定功能实现某些方法,从而使您要求客户端实现接口的意图更加明确

对于你问题的第二部分,我再次认为使用"组合优于继承"的原则可能适用,尽管这取决于你想要解决的确切问题。如果Customer是一个可以在运行时切换的行为,那么你应该将它用作IRental接口的成员,这基本上是策略设计模式的前提。

对于IRental来说,拥有一个类型为iccustomer的PrimaryCustomer字段可能是好的,可能还有一个类型为IList调用类似RelatedCustomers的东西(在这样的情况下,例如,一个客户已经表示他计划和其他一些也是客户的人一起使用一个项目;如果租赁有问题,但无法联系到客户,这些信息可能会很有用)。但是,如果每个客户只打算租用一件东西,那么IRental可能应该只继承iccustomer。

否则,假设Rental1和Rental2的Name都是"John Smith"。"Rental1之后。名字="弗雷德·琼斯",什么是租金?的名字是什么?目前尚不清楚Rental1和Rental2是否指的是同一客户。如果有一个Customer属性,这种模糊性就会消失。如果Object.ReferenceEquals (Rental1。Customer, Rental2.Customer),然后更改为Rental1.Customer。名称会影响Rental2.Customer。名称(它是相同对象的相同属性)。如果Rental1。Customer是与Rental2不同的对象。客户,那么更改其中一个不应该影响另一个。

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