我应该何时使用接口,何时使用基类?
如果我不想实际定义方法的基本实现,它应该始终是一个接口吗?
如果我有狗和猫的课。为什么我要实现IPet而不是PetBase?我可以理解为ISheds或IBarks(IMakesNoise?)提供接口,因为它们可以逐个宠物放置,但我不知道该为普通宠物使用哪个接口。
我应该何时使用接口,何时使用基类?
如果我不想实际定义方法的基本实现,它应该始终是一个接口吗?
如果我有狗和猫的课。为什么我要实现IPet而不是PetBase?我可以理解为ISheds或IBarks(IMakesNoise?)提供接口,因为它们可以逐个宠物放置,但我不知道该为普通宠物使用哪个接口。
当前回答
除了那些提到IPet/PetBase实现的注释之外,在某些情况下,提供访问器帮助器类可能非常有价值。
IPet/PetBase风格假设您有多个实现,从而增加了PetBase的价值,因为它简化了实现。然而,如果您有多个客户端,则提供一个类帮助来帮助使用接口,可以通过使接口更容易使用来降低成本。
其他回答
让我们以Dog和Cat类为例,使用C#进行演示:
狗和猫都是动物,特别是四足哺乳动物(动物太普通了)。让我们假设您有一个抽象类Mammal,用于这两个类:
public abstract class Mammal
此基类可能具有默认方法,例如:
喂养朋友
所有这些都是在两个物种之间或多或少具有相同实现的行为。要定义此项,您需要:
public class Dog : Mammal
public class Cat : Mammal
现在让我们假设还有其他哺乳动物,我们通常会在动物园里看到:
public class Giraffe : Mammal
public class Rhinoceros : Mammal
public class Hippopotamus : Mammal
这仍然有效,因为Feed()和Mate()功能的核心仍然相同。
然而,长颈鹿、犀牛和河马并不是你可以用来做宠物的动物。这就是界面将有用的地方:
public interface IPettable
{
IList<Trick> Tricks{get; set;}
void Bathe();
void Train(Trick t);
}
上述合同的执行在猫和狗之间是不同的;将它们的实现放在抽象类中继承将是一个坏主意。
狗和猫的定义现在应该如下:
public class Dog : Mammal, IPettable
public class Cat : Mammal, IPettable
理论上,您可以从更高的基类重写它们,但本质上,接口允许您只将所需的内容添加到类中,而不需要继承。
因此,由于您通常只能从一个抽象类继承(在大多数静态类型的OO语言中,即……例外包括C++),但能够实现多个接口,因此它允许您严格按照需要构造对象。
通常,您应该喜欢接口而不是抽象类。使用抽象类的一个原因是,如果在具体类之间有共同的实现。当然,您仍然应该声明一个接口(IPet),并让一个抽象类(PetBase)实现该接口。使用小的、不同的接口,您可以使用多个来进一步提高灵活性。接口允许最大限度的灵活性和跨边界类型的可移植性。当跨越边界传递引用时,始终传递接口而不是具体类型。这允许接收端确定具体实施,并提供最大的灵活性。当以TDD/BDD方式编程时,这是绝对正确的。
“四人帮”在他们的书中说:“因为继承将子类暴露于其父类实现的细节,所以人们常说‘继承破坏了封装’。”。我相信这是真的。
我应该何时使用接口,何时使用基类?
如果
你有纯抽象方法,没有非抽象方法您没有非抽象方法的默认实现(除了Java 8语言,其中接口方法提供默认实现)若您使用的是Java8,现在接口将为一些非抽象方法提供默认实现。与抽象类相比,这将使接口更可用。
请查看此SE问题以了解更多详细信息。
如果我不想实际定义方法的基本实现,它应该始终是一个接口吗?
对它更好更干净。即使您有一个带有一些抽象方法的基类,让我们的基类通过接口扩展抽象方法。您可以在将来更改接口而不更改基类。
java示例:
abstract class PetBase implements IPet {
// Add all abstract methods in IPet interface and keep base class clean.
Base class will contain only non abstract methods and static methods.
}
如果我有狗和猫的课。为什么我要实现IPet而不是PetBase?我可以理解为ISheds或IBarks(IMakesNoise?)提供接口,因为它们可以逐个宠物放置,但我不知道该为普通宠物使用哪个接口。
我更希望基类实现接口。
abstract class PetBase implements IPet {
// Add all abstract methods in IPet
}
/*If ISheds,IBarks is common for Pets, your PetBase can implement ISheds,IBarks.
Respective implementations of PetBase can change the behaviour in their concrete classes*/
abstract class PetBase implements IPet,ISheds,IBarks {
// Add all abstract methods in respective interfaces
}
优势:
如果我想在现有接口中再添加一个抽象方法,我只需简单地更改接口,而不需要接触抽象基类。如果我想更改契约,我将在不接触基类的情况下更改接口和实现类。您可以通过接口为基类提供不变性。看看这篇文章
有关详细信息,请参阅此相关SE问题:
我应该如何解释接口和抽象类之间的区别?
通过def,接口提供了一个与其他代码通信的层。默认情况下,类的所有公共财产和方法都实现隐式接口。我们也可以将接口定义为一个角色,当任何类都需要扮演这个角色时,它必须实现它,根据实现它的类,给它不同的实现形式。oops的两个概念!!!
我通常在需要之前都不会实现。与抽象类相比,我更喜欢接口,因为这提供了更多的灵活性。如果在某些继承类中有共同的行为,我会将其上移并创建一个抽象基类。我不认为两者都有必要,因为它们本质上服务于相同的目的,而且两者都有一种糟糕的代码气味(imho),解决方案被过度设计了。