接口应该尽可能保持通用吗?
开发接口时,应该尽可能保持通用,还是应该尝试在接口中放入尽可能多的方法、属性以保持接口数量较少:例如,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();
}
上述方法的优点/缺点是什么?是否有一种首选方法(一种更具可扩展性和可维护性的方法)。
When developing interfaces, should they be kept as generic as possible or should you try to put as many methods, properties in an interface to keep the number of interfaces low: As an example, which is better 1 or 2:
1) Customer and Rental split into 2 interfaces (Data only relevant to a rental is in Rental interface and data only relevant to a customer is in the Customer interface)
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) Put all data into one interface.
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; }
}
Also regarding the first approach, is there a benefit to extending the ICustomer interface or should there just be an ICustomer property in IRental like the following:
interface IRental
{
ICustomer customer {get;set;}
string Title { get; set; }
decimal Cost{ get; set; }
void Rent();
}
What are the advantages/disadvantages of the approaches above? and is there a preferred way (one that is more scalable and maintainable).
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(8)
查看 SOLID 的接口隔离原则。胖接口可能会带来问题,实现者和消费者被迫关心比他们需要的更多的事情。保持你的界面简洁且高度集中。经常使用的一个例子是调制解调器的概念,
调制解调器的实现者必须提供拨号和挂断的实现,这是连接状态问题。然后提供发送和接收的实现,也就是数据传输的问题。这些可能应该是两个独特的接口,它们是两个不同的职责组,这也符合单一职责原则。并非所有调制解调器都需要这两套职责。
Look into the Interface Segregation Principle of SOLID. Fat interfaces can be problematic, implementers and consumers are forced to care about more things than they need. Keep your interfaces thin and highly focused. An example used is often the concept of a modem
Implementers of
Modem
have to provide implementations for dialing and hanging up, which are connection state issues. And then provide implementations for sending and receiving, which are data transfer issues. These should possibly be two unique interfaces, they are two different groups of responsibilities, which also goes into the Single Responsibility Principle. And not all modems might need both sets of responsibilities.您应该始终考虑单一责任原则。类、方法和接口应该是领域特定的项目。所以恕我直言,最好将 ICustomer 和 IRental 分开。
You should always take under consideration Single responsibility principle. Classes, methods and interfaces should be domain specific items. So IMHO it's better to separate ICustomer and IRental.
没有黄金法则。
就我个人而言,我只会在需要或有意义时才分解内容。这通常意味着理解接口试图公开什么并打破逻辑上不同的契约。
接口应该始终适合于公开特定目的的特定合约 - 这可能意味着它们的基础薄弱。它可以防止对您的界面感兴趣的人不得不公开他们不需要订阅的内容。
就您而言,IRental 和 ICustomer 是两个逻辑上独立的实体。租赁可能包含客户详细信息,但应该在客户类别中进行,而不是通过公寓物业进行。所以你的最后一个代码对我来说看起来最合理:
There is no golden rule.
Personally, I only break stuff down when I need to or when it makes sense. This usually means understanding what the interface is trying to expose and breaking up logically different contracts.
Interfaces should always be geared to exposing a specific contract for a specific purpose - this will likely mean they are thin on the ground. It stops people interested in your interface from having to expose things they don't need to subscribe to.
In your case, IRental and ICustomer are two logically separate entities. A rental might contain customer details, but it should do so in a customer class, not via flat properties. So your last code looks most sensible to me:
我会选择选项 1,它允许您独立编辑界面。
正如亚当指出的“总是使用”,我会害怕这个词,当设计规范发生变化或几年后升级时,这可能是一个艰难的困境。
I would go option 1, it allows you to edit interfaces independently.
As Adam points out "always uses", I would be scared of that term, it can be a hard corner to find yourself in when a design spec changes or years later upgrading.
这取决于您的数据模型,但如果客户可以拥有多个租赁,或者如果有可能出现这种情况,则租赁在情况 2 中拥有客户信息是没有意义的。
租赁不是客户,所以选项 1 不太适合。问问自己“租赁是客户的一种类型还是客户的特殊化”?
您暗示的第三个选项是,假设您无法在没有客户的情况下进行租赁,那么 IRental 引用 ICustomer 可能是有意义的。
It depends on your data model, but if a Customer can have more than one rental, or if there's ever potential for this, it doesn't make sense for a Rental to have the customer information in case 2.
A rental is not a Customer, so option 1 doesn't really fit. Ask yourself "Is Rental a type of Customer or a specialization of Customer"?
A third option that you are hinting at, for an IRental to have a reference to an ICustomer probably makes sense, assuming you can't have a Rental without a Customer.
我想说这个例子把事情混的太多了。租赁和客户是两种不同类型的实体。
乍一看,我会说您有客户、标题和租赁类型,其中租赁引用客户和标题。
我不确定接口是否会参与其中,直到你有一个可以做的关联——可能是围绕租赁本身。
I'd say that the example mixes things too much. Rental and Customer are two different types of entities.
At first glance, I'd say that you have Customer, Title and Rental types, where a Rental references a Customer and a Title.
I'm not sure interfaces enter into it until you have a can-do association -- possibly around the rental itself.
我想到的一件事是接口隔离原则 来自 SOLID 原则。 如果您认为某些客户端可能不需要实现该接口的所有方法/属性,那么最好拆分接口,而不是提供一个胖接口。。它还可能使您要求客户实现接口的意图更加清晰,明确地告诉他们您希望为某些特定功能实现某些方法。
对于您问题的第二部分,我再次认为使用“组合”的原则过度继承”可能适用,但这取决于您要解决的具体问题。如果Customer是一个可以在运行时切换的行为,那么您应该将其用作IRental接口的成员,这基本上是策略设计模式的前提。
One thing that comes to my mind is the Interface Segregation Principle from the SOLID Principles. Instead of providing one fat Interface, it is good to split up the interfaces if you think some clients might not need to implement all the methods/properties of that interface. It also probably makes your intentions for asking the client to implement the interface more clear by explicitly telling them that you want certain methods implemented for certain specific functionality
For the Second part of your question, I think again the tenet of using "Composition over Inheritance" may apply, though this depends on the exact problem you are trying to solve. If Customer is a behavior that can be switched at runtime, then you should use it as a member of the IRental interface which is basically the premise of the Strategy Design Pattern.
对于 IRental 来说,拥有 ICustomer 类型的 PrimaryCustomer 字段以及可能是 IList字段可能是件好事。称为“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 必须在其属性定义中添加一个“新”限定符以避免愚蠢的行为) “歧义”消息)。在 C# 中,此类更改不需要任何额外的代码来实现 ICustomer,但可以更好地控制谁可以更改客户记录。
It may be good for a IRental to have a field PrimaryCustomer, of type ICustomer, and possibly something a IList<Customer> called something like RelatedCustomers (in case, e.g. a customer has indicated that he plans to use an item with some other people who are also customers; such information may be useful if there's an issue with the rental but the customer cannot be reached). IRental should probably only inherit ICustomer, however, if every customer is going to rent exactly one thing.
Otherwise, suppose Rental1 and Rental2 both have a Name of "John Smith". After 'Rental1.Name = "Fred Jones"', what would Rental2.Name be? It would be unclear whether Rental1 and Rental2 refer to the same customer. If there were a Customer property, such ambiguity would go away. If Object.ReferenceEquals(Rental1.Customer, Rental2.Customer), then changes to Rental1.Customer.Name would affect Rental2.Customer.Name (it's the same property, of the same object). If Rental1.Customer is a different object from Rental2.Customer, then changes to one should not affect the other.
Incidentally, I would suggest that it might be good to define an interface IReadableCustomer, in which the properties just have "getters", and have ICustomer inherit from IReadableCustomer (ICustomer would have to add a "new" qualifier to its property definitions to avoid silly "ambiguity" messages). In C#, such a change would not require any extra code for implementations of ICustomer, but would allow better control over who was allowed to make changes to a customer record.