这可能是一个通用的OOP问题。我想在接口和抽象类的使用基础上做一个通用的比较。
什么时候需要使用接口,什么时候需要使用抽象类?
这可能是一个通用的OOP问题。我想在接口和抽象类的使用基础上做一个通用的比较。
什么时候需要使用接口,什么时候需要使用抽象类?
当前回答
1.如果您正在创建为不相关的类提供公共功能的东西,请使用接口。
2.如果您正在为层次结构中密切相关的对象创建一些东西,请使用抽象类。
其他回答
就我个人而言,我几乎从不需要编写抽象类。
大多数时候,我看到抽象类被(错误地)使用,这是因为抽象类的作者使用了“模板方法”模式。
“Template方法”的问题在于它几乎总是某种程度上是可重入的——“派生”类不仅知道它正在实现的基类的“抽象”方法,还知道基类的公共方法,即使大多数时候它不需要调用它们。
(过于简化的)例子:
abstract class QuickSorter
{
public void Sort(object[] items)
{
// implementation code that somewhere along the way calls:
bool less = compare(x,y);
// ... more implementation code
}
abstract bool compare(object lhs, object rhs);
}
因此,在这里,该类的作者编写了一个泛型算法,并打算通过提供自己的“钩子”(在本例中是一个“比较”方法)来“专门化”它,以供人们使用。
所以预期的用法是这样的:
class NameSorter : QuickSorter
{
public bool compare(object lhs, object rhs)
{
// etc.
}
}
这样做的问题在于,你将两个概念过度地耦合在了一起:
比较两个项目的一种方法(哪个项目应该放在前面) 排序项目的方法(即快速排序vs归并排序等)
在上面的代码中,从理论上讲,“compare”方法的作者可以重新调用超类“Sort”方法…即使在实践中,他们永远不会想要或需要这样做。
为这种不必要的耦合付出的代价是,很难更改超类,而且在大多数OO语言中,不可能在运行时更改它。
另一种方法是使用“策略”设计模式:
interface IComparator
{
bool compare(object lhs, object rhs);
}
class QuickSorter
{
private readonly IComparator comparator;
public QuickSorter(IComparator comparator)
{
this.comparator = comparator;
}
public void Sort(object[] items)
{
// usual code but call comparator.Compare();
}
}
class NameComparator : IComparator
{
bool compare(object lhs, object rhs)
{
// same code as before;
}
}
现在请注意:我们所拥有的只是接口,以及这些接口的具体实现。在实践中,您实际上不需要任何其他东西来进行高级OO设计。
为了“隐藏”我们已经通过使用“QuickSort”类和“NameComparator”实现了“名称排序”的事实,我们仍然可以在某个地方写一个工厂方法:
ISorter CreateNameSorter()
{
return new QuickSorter(new NameComparator());
}
任何时候你有一个抽象类,你都可以这样做…即使基类和派生类之间存在自然的可重入关系,将它们显式化通常也是值得的。
最后一个想法:我们上面所做的一切都是通过使用“QuickSort”函数和“NameComparison”函数来“组合”一个“NameSorting”函数……在函数式编程语言中,这种编程风格变得更加自然,代码更少。
我的观点是:
接口基本上定义了一个契约,任何实现类都必须遵守(实现接口成员)。它不包含任何代码。
另一方面,抽象类可以包含代码,并且可能有一些标记为抽象的方法,继承类必须实现这些方法。
我很少使用抽象类的情况是,当我有一些默认功能时,继承类可能对重写不感兴趣,比如一个抽象基类,一些专门的类继承自它。
示例(非常基本的一个!):考虑一个名为Customer的基类,它具有抽象方法,如CalculatePayment(), CalculateRewardPoints()和一些非抽象方法,如GetName(), SavePaymentDetails()。
像RegularCustomer和GoldCustomer这样的专门的类将继承自Customer基类,并实现它们自己的CalculatePayment()和CalculateRewardPoints()方法逻辑,但是重用GetName()和SavePaymentDetails()方法。
您可以向抽象类(即非抽象方法)添加更多功能,而不会影响使用旧版本的子类。然而,向接口添加方法会影响实现它的所有类,因为它们现在需要实现新添加的接口成员。
具有所有抽象成员的抽象类类似于接口。
什么时候选择抽象类而不是接口?
如果计划在程序/项目的整个生命周期中更新基类,最好允许基类是一个抽象类 如果有人试图为层次结构中密切相关的对象构建主干,那么使用抽象类是非常有益的
什么时候选择接口而不是抽象类?
如果不需要处理大量的层次结构类型的框架,那么接口将是一个很好的选择 因为抽象类不支持多重继承(菱形问题),所以接口可以挽救局面
在Java中,您可以从一个(抽象)类继承来“提供”功能,并且可以实现许多接口来“确保”功能
这是一个很难打的电话。
我可以给出一个提示:一个对象可以实现许多接口,而一个对象只能继承一个基类(在像c#这样的现代OO语言中,我知道c++有多个继承-但这不是不受欢迎吗?)