我应该何时使用接口,何时使用基类?
如果我不想实际定义方法的基本实现,它应该始终是一个接口吗?
如果我有狗和猫的课。为什么我要实现IPet而不是PetBase?我可以理解为ISheds或IBarks(IMakesNoise?)提供接口,因为它们可以逐个宠物放置,但我不知道该为普通宠物使用哪个接口。
我应该何时使用接口,何时使用基类?
如果我不想实际定义方法的基本实现,它应该始终是一个接口吗?
如果我有狗和猫的课。为什么我要实现IPet而不是PetBase?我可以理解为ISheds或IBarks(IMakesNoise?)提供接口,因为它们可以逐个宠物放置,但我不知道该为普通宠物使用哪个接口。
当前回答
用你自己的判断,要聪明。不要总是照别人(像我)说的做。你会听到“更喜欢接口而不是抽象类”,但这取决于实际情况。这取决于班级。
在上面提到的情况下,我们有一个对象层次结构,接口是一个好主意。接口有助于处理这些对象的集合,在实现服务时也有助于使用层次结构中的任何对象。您只需定义一个用于处理层次结构中的对象的契约。
另一方面,当您实现一组共享公共功能的服务时,您可以将公共功能分离为一个完整的单独类,也可以将其移动到一个公共基类中,并使其抽象化,以便没有人可以实例化基类。
还要考虑如何随时间推移支持抽象。接口是固定的:您将一个接口作为任何类型都可以实现的一组功能的契约发布。基类可以随着时间的推移而扩展。这些扩展成为每个派生类的一部分。
其他回答
一个重要的区别是,只能继承一个基类,但可以实现多个接口。因此,如果您绝对确定不需要同时继承不同的基类,那么只需要使用基类。此外,如果你发现你的接口越来越大,那么你应该开始把它分成几个逻辑块来定义独立的功能,因为没有规则规定你的类不能实现所有的功能(或者你可以定义一个不同的接口来继承所有的功能)。
另一个要记住的选项是使用“has-a”关系,也就是“以”或“组合的方式实现”。有时这是一种比使用“is-a”继承更干净、更灵活的结构方式。
从逻辑上说,狗和猫都“拥有”一只宠物可能不太合理,但它避免了常见的多重继承陷阱:
public class Pet
{
void Bathe();
void Train(Trick t);
}
public class Dog
{
private Pet pet;
public void Bathe() { pet.Bathe(); }
public void Train(Trick t) { pet.Train(t); }
}
public class Cat
{
private Pet pet;
public void Bathe() { pet.Bathe(); }
public void Train(Trick t) { pet.Train(t); }
}
是的,这个例子表明,在以这种方式做事时存在大量的代码重复和缺乏优雅。但我们也应该意识到,这有助于保持狗和猫与宠物类的分离(因为狗和猫不能访问宠物的私人成员),并为狗和猫从其他东西(可能是哺乳动物类)继承留下了空间。
当不需要私人访问并且您不需要使用通用的宠物参考/指针来参考狗和猫时,合成是最好的。接口为您提供了通用的引用功能,可以帮助减少代码的冗长,但当组织不好时,它们也会使事情变得模糊。当您需要私人成员访问时,继承是有用的,在使用它时,您将致力于将您的狗和猫类与您的宠物类高度耦合,这是一个巨大的成本。
在继承、组合和接口之间,没有一种方法总是正确的,考虑如何协调地使用这三种选项是有帮助的。在这三个选项中,继承通常是最不常用的选项。
现代风格是定义IPet和PetBase。
该接口的优点是其他代码可以使用它,而与其他可执行代码没有任何联系。完全“干净”。界面也可以混合。
但是基类对于简单的实现和通用实用程序很有用。因此,还提供一个抽象基类,以节省时间和代码。
胡安,
我喜欢将接口视为一种描述类的方式。一个特定的犬类,比如约克郡梗,可能是母犬类的后代,但它也执行IFurry、IStubby和IYippieDog。所以类定义了类是什么,但接口告诉了我们关于它的事情。
这样做的好处是,例如,它允许我收集所有的IYippieDog,并将它们放入我的海洋收藏。因此,现在我可以跨越一组特定的对象,找到符合我所关注的标准的对象,而不必过于仔细地检查类。
我发现接口确实应该定义类的公共行为的子集。如果它为实现的所有类定义了所有公共行为,那么它通常不需要存在。他们没有告诉我任何有用的东西。
但这种想法与每个类都应该有一个接口的想法背道而驰,您应该编写接口代码。这很好,但最终会有很多一对一的类接口,这会让事情变得混乱。我知道这样做并不需要花费任何费用,现在你可以轻松地交换东西。然而,我发现我很少这样做。大多数时候,我只是在原地修改现有的类,如果该类的公共接口需要更改,我会遇到完全相同的问题,但我现在必须在两个地方更改它。
所以,如果你像我一样思考,你肯定会说《猫和狗》是IPettable的。这是一个符合两者的特征。
但另一个问题是,它们是否应该有相同的基类?问题是,它们是否需要被广泛地视为同一事物。当然,它们都是动物,但这是否符合我们将如何一起使用它们。
假设我想收集所有动物类并将它们放在我的方舟容器中。
或者它们需要是哺乳动物吗?也许我们需要某种跨动物挤奶工厂?
他们甚至需要联系在一起吗?仅仅知道它们都是IPettable就足够了吗?
当我真的只需要一个类时,我常常会感到想要派生出一个完整的类层次结构。我做这件事是为了期待有一天我可能会需要它,但通常我从来不会这样做。即使我做了,我通常也会发现我必须做很多事情来修复它。这是因为我创建的第一个类不是狗,我没有那么幸运,而是鸭嘴兽。现在,我的整个类层次结构都基于这个奇怪的案例,我有很多浪费的代码。
你可能还会发现,并不是所有的猫都是IPettable的(比如那个无毛的猫)。现在您可以将该接口移动到所有适合的派生类。你会发现,突然之间,猫不再是PettableBase的产物,这是一个小得多的变化。
列出你的对象必须要做的事情,你的对象可以做的事情。必须指明你的基类型,可以指明你的接口。
例如,您的PetBase必须呼吸,而您的IPet可能会DoTricks。
对问题域的分析将帮助您定义精确的层次结构。