这可能是一个通用的OOP问题.我想根据它们的用法在接口和抽象类之间进行一般性比较.
什么时候想要使用接口?何时想要使用抽象类?
抽象类可以具有共享状态或功能.接口只是提供状态或功能的承诺.一个好的抽象类将减少必须重写的代码量,因为它的功能或状态可以共享.接口没有要共享的已定义信息
我写了一篇关于此的文章:
抽象类和接口
总结:
当我们谈论抽象类时,我们定义了对象类型的特征; 指定对象是什么.
当我们谈论接口并定义我们承诺提供的功能时,我们正在谈论建立一个关于对象可以做什么的合同.
就个人而言,我几乎从不需要编写抽象类.
大多数时候我看到抽象类被错误地使用,这是因为抽象类的作者使用了"模板方法"模式.
"模板方法"的问题在于它几乎总是有些可重入 - "派生"类不仅知道它正在实现的基类的"抽象"方法,而且还知道基类的公共方法,即使大多数时候它不需要打电话给他们.
(过度简化)示例:
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"函数......在函数式编程语言中,这种编程风格变得更加自然,用更少的代码.
好吧,我自己只是"弄清楚" - 这里是外行人的说法(如果我错了,请随时纠正我) - 我知道这个话题太棒了,但有一天其他人可能偶然发现它......
抽象类允许您创建蓝图,并允许您另外构造(实现)您希望其所有后代拥有的属性和方法.
另一方面,接口只允许您声明您希望具有给定名称的属性和/或方法存在于实现它的所有类中 - 但是没有指定您应该如何实现它.此外,一个类可以实现MANY接口,但只能扩展一个Abstract类.界面更像是一种高级建筑工具(如果你开始掌握设计模式,它会变得更加清晰) - 一个抽象在两个阵营都有一个脚,也可以执行一些肮脏的工作.
为什么用一个而不是另一个?前者允许对后代进行更具体的定义 - 后者允许更大的多态性.最后一点对最终用户/编码人员很重要,他们可以利用这些信息以各种组合/形状实现AP I(接口)以满足他们的需要.
我认为这对我来说是一个"灯泡"时刻 - 考虑一下与作者相关的界面,以及更多来自链接后期的任何编码人员,他们正在为项目添加实现或扩展 API.
如果您将java视为OOP语言,
" 接口不提供方法实现 "在Java 8启动时不再有效.现在java在默认方法的接口中提供了实现.
简单来说,我想用
interface:通过多个不相关的对象实现合同.它提供" HAS A "功能.
抽象类:在多个相关对象之间实现相同或不同的行为.它建立了" IS A "关系.
Oracle 网站提供了类interface
和abstract
类之间的主要区别.
考虑使用抽象类,如果:
您希望在几个密切相关的类之间共享代码.
您希望扩展抽象类的类具有许多常用方法或字段,或者需要除公共之外的访问修饰符(例如protected和private).
您想声明非静态或非最终字段.
考虑使用接口:
您希望不相关的类可以实现您的接口.例如,许多不相关的对象可以实现Serializable
接口.
您希望指定特定数据类型的行为,但不关心谁实现其行为.
您希望利用类型的多重继承.
例:
抽象类(IS A关系)
Reader是一个抽象类.
BufferedReader是一个Reader
FileReader是一个Reader
FileReader
并BufferedReader
用于共同目的:读取数据,并通过Reader
课程相关.
接口(HAS A功能)
Serializable是一个接口.
假设您的应用程序中有两个类,它们正在实现Serializable
接口
Employee implements Serializable
Game implements Serializable
在这里,你不能通过和Serializable
之间的接口建立任何关系,这是为了不同的目的.两者都能够序列化国家,并且比较在那里结束.Employee
Game
看看这些帖子:
我该如何解释Interface和Abstract类之间的区别?
我的两分钱:
接口基本上定义了一个契约,任何实现类都必须遵守(实现接口成员).它不包含任何代码.
另一方面,抽象类可以包含代码,并且可能有一些标记为抽象的方法,继承类必须实现这些方法.
我使用抽象类的罕见情况是,当我有一些默认功能时,继承类在重写时可能没有意义,比如一个专门的类继承的抽象基类.
示例(一个非常基本的一个!):考虑一个基类叫做客户具有抽象方法一样CalculatePayment()
,CalculateRewardPoints()
和一些非抽象的方法,比如GetName()
,SavePaymentDetails()
.
专业类,如RegularCustomer
以及GoldCustomer
将从继承Customer
基类和实现自己的CalculatePayment()
和CalculateRewardPoints()
方法逻辑,但重复使用GetName()
和SavePaymentDetails()
方法.
您可以向抽象类添加更多功能(非抽象方法),而不会影响使用旧版本的子类.而向接口添加方法会影响实现它的所有类,因为它们现在需要实现新添加的接口成员.
包含所有抽象成员的抽象类与接口类似.
如果你在脑海中清楚地了解这个概念,那么什么时候做一件非常简单的事情.
抽象类可以派生,而接口可以实现.两者之间存在一些差异.派生Abstract类时,派生类和基类之间的关系是'是'关系.例如,Dog是Animal,Sheep是Animal,这意味着Derived类从基类继承了一些属性.
然而,对于接口的实现,关系是"可以".例如,狗可以是间谍犬.狗可以是马戏团的狗.狗可以是赛狗.这意味着您实现某些方法来获取某些东西.
我希望我很清楚.
我写了一篇关于何时使用抽象类以及何时使用接口的文章.除了"一个IS-A ......和一个CAN-DO ......"之外,它们之间还有很多不同之处.对我来说,这些都是罐头答案.我提到了使用其中任何一个的几个原因.希望能帮助到你.
http://codeofdoom.com/wordpress/2009/02/12/learn-this-when-to-use-an-abstract-class-and-an-interface/
1.如果要创建为不相关的类提供通用功能的东西,请使用接口.
2.如果要为层次结构中密切相关的对象创建内容,请使用抽象类.
我认为最简洁的表达方式如下:
共享属性=>抽象类。
共享功能=>接口。
更简单地说...
抽象类示例:
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) { } }
Since animals have a shared property - number of legs in this case - it makes sense to make an abstract class containing this shared property. This also allows us to write common code that operates on that property. For example:
public static int CountAllLegs(Listanimals) { int legCount = 0; foreach (BaseAnimal animal in animals) { legCount += animal.NumberOfLegs; } return legCount; }
Interface Example:
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!"); }
Note here that Vuvuzelas and Cars are completely different things, but they have shared functionality: making a sound. Thus, an interface makes sense here. Further, it will allow programmers to group things that make sounds together under a common interface -- IMakeSound
in this case. With this design, you could write the following code:
ListsoundMakers = new List (); 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(); }
Can you tell what that would output?
Lastly, you can combine the two.
Combined Example:
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!"); }
Here, we're requiring all BaseAnimal
s make a sound, but we don't know its implementation yet. In such a case, we can abstract the interface implementation and delegate its implementation to its subclasses.
One last point, remember how in the abstract class example we were able to operate on the shared properties of different objects and in the interface example we were able to invoke the shared functionality of different objects? In this last example, we could do both.
类只能从一个基类继承,因此,如果要使用抽象类为一组类提供多态性,则它们都必须都从该类继承。抽象类也可以提供已经实现的成员。因此,您可以通过抽象类确保一定数量的相同功能,但是不能通过接口来保证。
以下是一些建议,可以帮助您决定是使用接口还是抽象类为组件提供多态性。
如果您打算创建组件的多个版本,请创建一个抽象类。抽象类提供了一种简便的方式来对组件进行版本控制。通过更新基类,所有继承的类都会随着更改自动更新。另一方面,以这种方式创建的接口不能更改。如果需要新版本的接口,则必须创建一个全新的接口。
如果您要创建的功能将在各种不同的对象中有用,请使用界面。抽象类应主要用于紧密相关的对象,而接口最适合为不相关的类提供通用功能。
如果您正在设计小的简洁功能,请使用接口。如果要设计大型功能单元,请使用抽象类。
如果要在组件的所有实现之间提供通用的实现功能,请使用抽象类。抽象类允许您部分实现您的类,而接口不包含任何成员的实现。
复制自:http :
//msdn.microsoft.com/zh-cn/library/scsyfw1d%28v=vs.71%29.aspx
什么时候比接口更喜欢抽象类?
如果有人计划在程序/项目的整个生命周期中更新基类,则最好让基类成为抽象类。
如果要为层次结构中密切相关的对象构建主干,则使用抽象类非常有益
什么时候比抽象类更喜欢接口?
如果不处理大规模分层框架,接口将是一个不错的选择
因为抽象类不支持多重继承(钻石问题),所以接口可以节省时间