这可能是一个通用的OOP问题。我想根据接口和抽象类的用法进行通用比较。
什么时候要使用接口,什么时候要使用抽象类?
这可能是一个通用的OOP问题。我想根据接口和抽象类的用法进行通用比较。
什么时候要使用接口,什么时候要使用抽象类?
Answers:
Interfaces do not express something like "a Doberman is a type of dog and every dog can walk" but more like "this thing can walk"
。谢谢
Use abstract classes and inheritance if you can make the statement “A is a B”. Use interfaces if you can make the statement “A is capable of [doing] as”
抽象类可以具有共享状态或功能。接口仅是提供状态或功能的承诺。一个好的抽象类将减少必须重写的代码量,因为它的功能或状态可以共享。该接口没有要共享的已定义信息
就个人而言,我几乎不需要编写抽象类。
大多数时候,我看到抽象类被(错误)使用,这是因为抽象类的作者正在使用“模板方法”模式。
“ Template method”的问题在于它几乎总是重入-“ derived”类不仅知道其正在实现的基类的“ abstract”方法,而且还知道该基类的public方法,即使大多数情况下也不需要调用它们。
(过于简化)示例:
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.
}
}
问题在于您将两个概念过度结合在一起:
在上面的代码中,理论上,“比较”方法的作者可以 重新进入超类“排序”方法中……尽管实际上他们从不愿意或不需要这样做。
您为这种不必要的耦合付出的代价是很难更改超类,并且在大多数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”函数……在函数式编程语言中,这种编程风格变得更加自然,用更少的代码。
如果您将Java视为OOP语言,
对于Java 8启动,“ 接口不提供方法实现 ”不再有效。现在,java在接口中为默认方法提供了实现。
简单来说,我想使用
接口:由多个不相关的对象实施合同。它提供“ HAS A ”功能。
抽象类:在多个相关对象之间实现相同或不同的行为。它建立“ IS A ”关系。
Oracle 网站提供了interface
和abstract
类之间的主要区别。
如果满足以下条件,请考虑使用抽象类:
如果满足以下条件,请考虑使用接口:
Serializable
接口。例:
抽象类(IS关系)
读者是一个抽象类。
BufferedReader是一个Reader
FileReader是一个Reader
FileReader
并且BufferedReader
用于一般目的:读取数据,并且它们通过Reader
类是相关的。
接口(具有HAS A功能)
可序列化是一个接口。
假设您的应用程序中有两个类,它们正在实现Serializable
接口
Employee implements Serializable
Game implements Serializable
在这里,您无法通过和Serializable
之间的接口建立任何关系,这是出于不同的目的。两者都能够序列化状态,并且比较到此结束。Employee
Game
看看这些帖子:
好的,我本人只是“抱怨”了这-用外行的话来说(如果我错了,请随时纠正我)-我知道这个话题太过老了,但是有一天别人可能会偶然发现...
抽象类使您可以创建一个蓝图,并允许您另外构造所有后代要拥有的CONSTRUCT(实现)属性和方法。
另一方面,接口仅允许您声明要使具有给定名称的属性和/或方法存在于实现该接口的所有类中,但没有指定应如何实现。同样,一个类可以实现MANY接口,但只能扩展一个Abstract类。接口更像是一种高级架构工具(如果您开始掌握设计模式,它将变得更加清晰)-摘要在两个阵营中都有一席之地,并且也可以执行一些肮脏的工作。
为什么要使用一个?前者允许更具体的后代的定义-后者允许更大的多态性。最后一点对最终用户/编码器很重要,他们可以利用此信息以各种组合/形状来实现AP I(接口)以适应他们的需求。
我认为这对我来说是“灯泡”时刻-少考虑作者的角度,而不要考虑链中后来为项目添加实现或扩展 API 的任何编码人员的接口。
我的两分钱:
接口基本上定义了一个契约,任何实现类都必须遵守(实现接口成员)。它不包含任何代码。
另一方面,抽象类可以包含代码,并且可能有一些标记为抽象的方法必须由继承类实现。
我使用抽象类的罕见情况是,当我具有某些默认功能时,继承类可能对覆盖某些专门类继承的抽象基类(例如抽象基类)不感兴趣。
示例(一个非常基本的一个!):考虑一个基类叫做客户具有抽象方法一样CalculatePayment()
,CalculateRewardPoints()
和一些非抽象的方法,比如GetName()
,SavePaymentDetails()
。
像RegularCustomer
和这样的专用类GoldCustomer
将从Customer
基类继承并实现自己的CalculatePayment()
和CalculateRewardPoints()
方法逻辑,但重新使用GetName()
和SavePaymentDetails()
方法。
您可以向抽象类(不是抽象方法)添加更多功能,而不会影响使用旧版本的子类。尽管向接口添加方法会影响所有实现该接口的类,因为它们现在需要实现新添加的接口成员。
具有所有抽象成员的抽象类将类似于接口。
我写了一篇关于何时使用抽象类以及何时使用接口的文章。除了“一个IS-A ...和一个CAN-DO ...”以外,它们之间还有很多区别。对我来说,这些是固定答案。我提到了使用其中任何一个的一些原因。希望能帮助到你。
我认为最简洁的表达方式如下:
共享属性=>抽象类。
共享功能=>接口。
更简单地说...
抽象类示例:
public abstract class BaseAnimal
{
public int NumberOfLegs { get; set; }
protected BaseAnimal(int numberOfLegs)
{
NumberOfLegs = numberOfLegs;
}
}
public class Dog : BaseAnimal
{
public Dog() : base(4) { }
}
public class Human : BaseAnimal
{
public Human() : base(2) { }
}
由于动物具有共享的属性(在这种情况下为支腿数),因此使包含该共享属性的抽象类变得有意义。这也使我们能够编写在该属性上运行的通用代码。例如:
public static int CountAllLegs(List<BaseAnimal> animals)
{
int legCount = 0;
foreach (BaseAnimal animal in animals)
{
legCount += animal.NumberOfLegs;
}
return legCount;
}
接口示例:
public interface IMakeSound
{
void MakeSound();
}
public class Car : IMakeSound
{
public void MakeSound() => Console.WriteLine("Vroom!");
}
public class Vuvuzela : IMakeSound
{
public void MakeSound() => Console.WriteLine("VZZZZZZZZZZZZZ!");
}
请注意,Vuvuzelas和Cars是完全不同的东西,但是它们具有共享的功能:发出声音。因此,这里的接口是有意义的。此外,它将允许程序员在一个公共界面下将声音组合在一起IMakeSound
。通过这种设计,您可以编写以下代码:
List<IMakeSound> soundMakers = new List<ImakeSound>();
soundMakers.Add(new Car());
soundMakers.Add(new Vuvuzela());
soundMakers.Add(new Car());
soundMakers.Add(new Vuvuzela());
soundMakers.Add(new Vuvuzela());
foreach (IMakeSound soundMaker in soundMakers)
{
soundMaker.MakeSound();
}
你能说出什么吗?
最后,您可以将两者结合起来。
组合示例:
public interface IMakeSound
{
void MakeSound();
}
public abstract class BaseAnimal : IMakeSound
{
public int NumberOfLegs { get; set; }
protected BaseAnimal(int numberOfLegs)
{
NumberOfLegs = numberOfLegs;
}
public abstract void MakeSound();
}
public class Cat : BaseAnimal
{
public Cat() : base(4) { }
public override void MakeSound() => Console.WriteLine("Meow!");
}
public class Human : BaseAnimal
{
public Human() : base(2) { }
public override void MakeSound() => Console.WriteLine("Hello, world!");
}
在这里,我们要求所有人都BaseAnimal
发出声音,但我们尚不知道它的实现。在这种情况下,我们可以抽象化接口实现并将其实现委托给其子类。
最后一点,还记得在抽象类示例中我们如何能够对不同对象的共享属性进行操作,而在接口示例中我们如何能够调用不同对象的共享功能?在最后一个示例中,我们可以同时进行。
什么时候比接口更喜欢抽象类?
什么时候比抽象类更喜欢接口?
类只能从一个基类继承,因此,如果要使用抽象类为一组类提供多态性,则它们都必须都从该类继承。抽象类也可以提供已经实现的成员。因此,您可以通过抽象类确保一定数量的相同功能,但是不能通过接口来保证。
以下是一些建议,可以帮助您决定是使用接口还是抽象类为组件提供多态性。
复制自:http :
//msdn.microsoft.com/zh-cn/library/scsyfw1d%28v=vs.71%29.aspx
如果以下任何一种情况适用于您的情况,请考虑使用抽象类:
如果以下任何一种情况适用于您的情况,请考虑使用接口:
对我来说,在很多情况下我都会使用接口。但是在某些情况下,我更喜欢抽象类。
OO中的类通常是指实现。当我想将一些实现细节强加给我使用接口的子类时,我使用抽象类。
当然,抽象类不仅在强制实施中有用,而且在许多相关类之间共享某些特定细节方面也很有用。
如果要提供一些基本的实现,请使用抽象类。
在Java中,您可以从一个(抽象)类继承来“提供”功能,并且可以实现许多接口来“确保”功能
接口比抽象类更好的一个有趣的地方是,当您需要向一组(相关或不相关)对象添加额外的功能时。如果您不能给他们一个基础抽象类(例如,他们已经sealed
或已经有一个父类),则可以给他们一个虚拟(空)接口,然后为该接口编写扩展方法。
拨打电话可能非常困难...
我可以给出一个指针:一个对象可以实现多个接口,而一个对象只能继承一个基类(在像c#这样的现代OO语言中,我知道C ++具有多重继承-但这不是皱眉吗?)
抽象类可以具有实现。
接口没有实现,只是定义了一种契约。
也可能存在一些与语言有关的差异:例如C#没有多重继承,但是可以在一个类中实现多个接口。
基本的经验法则是:对于“名词”,请使用Abstract类;对于“动词”,请使用接口
例如:car
是一个抽象类drive
,我们可以使其成为一个接口。
drive
在汽车中添加功能-这是一个抽象类。