抽象类与接口与mixins类
有人可以向我解释抽象类 , 接口和mixins之间的区别吗? 我以前在我的代码中使用过,但我不知道技术上的差异。
这些答案大部分都没有描述所有三个,所以我会加我自己的。
抽象类
抽象类是不是被devise为实例化的类。 抽象类可以没有实现,一些实现或全部实现。 抽象类旨在允许其子类共享一个通用(默认)实现。 抽象类的(伪代码)例子就是这样的
abstract class Shape { def abstract area(); // abstract (unimplemented method) def outline_width() = { return 1; } // default implementation }
一个子类可能看起来像
class Rectangle extends Shape { int height = width = 5; def override area() = { return height * width; } // implements abstract method // no need to override outline_width(), but may do so if needed }
可能的用法
def main() = { Shape[] shapes = { new Rectangle(), new Oval() }; foreach (s in shapes) { print("area: " + s.area() + ", outline width: " + s.outline_width()); } }
如果一个子类不覆盖未实现的方法,它也是一个抽象类。
接口
在一般的计算机科学术语中,接口是暴露给客户端的程序的一部分。 公共类和成员是接口的例子。
Java和C#有一个特殊的interface
关键字。 这些或多或less是一个没有实现的抽象类。 (有关常量,嵌套类,显式实现和访问修饰符的技巧,我不打算进入。)虽然关于“没有实现”的部分不再适合在Java中,他们添加了默认方法。 interface
关键字可以看作是接口概念的具体化。
回到形状的例子
interface Shape { def area(); // implicitly abstract so no need for abstract keyword def outline_width(); // cannot implement any methods } class Rectangle implements Shape { int height = width = 5; def override area() = { return height * width; } def override outline_width() = { return 1; } // every method in interface must be implemented } def main() = { Shape[] shapes = { new Rectangle(), new Oval() }; foreach (s in shapes) { print("area: " + s.area() + ", outline width: " + s.outline_width()); } }
Java和C#不允许实现多个类的inheritance,但它们允许多个接口的实现。 Java和C#使用接口作为在允许多inheritance的语言中发现的死亡致命钻石问题的解决方法(如果处理得当,这不是那么致命)。
混入
mixin(有时称为trait)允许抽象类的多重inheritance。 Mixins没有多重inheritance的可怕关联(由于C ++的疯狂),所以人们使用它们更加舒适。 他们有着与死亡问题完全一致的致命钻石,但支持他们的语言比C ++有更多优雅的缓解方式,所以他们被看作是上帝从高处传下来的“纯粹的真棒”(Unadulterated Awesomeness TM) 。
Mixin被誉为与行为重用 , 更灵活的接口和更强大的接口的接口。 你会注意到所有这些都有他们的术语interface
,指的是Java和C#关键字。 Mixin不是接口。 他们是多重inheritance。 用更漂亮的名字
(免责声明:这并不是说mixin是不好的,多重inheritance也不错,C ++解决多重inheritance的方式就是每个人都在做的事情,人们把它重新命名为与恐怖分离,这可能是一件好事那就是C ++。
免责声明:我爱C ++。 请不要以此作为任何语言的判断。 他们都像我珍贵的小雪花,我只想把每一个都拿在手里唱Kumbaya。)
在疲惫的,老的形状的例子
mixin Shape { def abstract area(); def outline_width() = { return 1; } } class Rectangle with Shape { int height = width = 5; def override area() = { return height * width; } } def main() = { Shape[] shapes = { new Rectangle(), new Oval() }; foreach (s in shapes) { print("area: " + s.area() + ", outline width: " + s.outline_width()); } }
你会注意到这和抽象类的例子没有区别。
另外一个好消息是C#从3.0版本开始支持mixins。 你可以在接口上使用扩展方法来完成。 这里是真实(!)C#代码mixin风格的Shape示例
interface Shape { int Area(); } static class ShapeExtensions { public static int OutlineWidth(this Shape s) { return 1; } } class Rectangle : Shape { int height = 5; int width = 5; public int Area() { return height * width; } } class Program { static void Main() { Shape[] shapes = new Shape[]{ new Rectangle(), new Oval() }; foreach (var s in shapes) { Console.Write("area: " + s.Area() + ", outline width: " + s.OutlineWidth()); } } }
一般来说:
接口是指定操作的合同,但没有任何实现。 一些语言(Java,C#)已经支持接口,而在其他接口中则描述了一个像C ++中的纯虚拟类一样的约定。
抽象类是一个类,它至less指定了一个操作而没有实现。 抽象类也可以提供一些实现的部分。 同样,一些语言已经支持将标记类标记为抽象类,并且在其他语言中是隐含的。 例如,在C ++中,定义纯虚拟方法的类是抽象的。
mixin是一个类,它被devise成使子类中的特定function的实现更容易,但是它并不是被devise用来自己使用的。 例如,假设我们有一个处理请求的对象的接口
interface RequestHandler { void handleRequest(Request request); }
也许通过累积它们来缓冲这些请求是有用的,直到我们有了一些预定的数字,然后刷新缓冲区。 我们可以用mixin实现缓冲function,而不需要指定刷新行为:
abstract class BufferedRequestHandlerMixin implements RequestHandler { List<Request> buffer = new List<Request>(); void handleRequest(Request request) { buffer.add(request); if (buffer.size == BUFFER_FLUSH_SIZE) { flushBuffer(buffer); buffer.clear(); } } abstract void flushBuffer(List<Request> buffer); }
这样,我们很容易编写一个请求处理程序,将请求写入磁盘,调用Web服务等,而不必每次重写缓冲function。 这些请求处理程序可以简单地扩展BufferedRequestHandlerMixin
并实现flushBuffer
。
另一个很好的例子是Spring中的许多支持类之一, HibernateDaoSupport 。
参考Java并给出提供mixin的抽象类的例子是有误导性的。 首先,Java默认不支持“mixins”。 在Java中,抽象类和Mixins变得混乱。
mixin是类可以实现的types,除了它的“主types”,以表明它提供了一些可选的行为。 用Java术语来讲,一个例子就是你的业务价值对象实现Serializable。
Josh Bloch说:“抽象类不能用来定义混合类,因为一个类不能有多个父类”(记住,Java只允许一个“扩展”候选)
寻找像Scala和Ruby这样的语言来适当实现“mixin”的概念
基本上抽象类是一些具体实现的接口。 一个接口只是一个没有实现细节的合约。
如果要在所有实现抽象类的对象中创build通用function,则可以使用抽象类。 保持OOP的干(不要重复自己)的原则。
抽象类是不是所有成员都被实现的类,它们被留给inheritance者来实现。它迫使它的inheritance者实现它的抽象成员。 抽象类不能实例化,因此它们的构造函数不应该公开。]
这是C#中的一个例子:
public abstract class Employee { protected Employee(){} public abstract double CalculateSalary(WorkingInfo workingInfo);//no implementation each type of employee should define its salary calculation method. } public class PartTimeEmployee:Employee { private double _workingRate; public Employee(double workingRate) { _workingRate=workingRate; } public override double CalculateSalary(WorkingInfo workingInfo) { return workingInfo.Hours*_workingRate; }
}
接口是一个由类实现的契约,它只是声明一个实现类的成员的签名,它本身没有实现。我们通常使用接口来实现多态,并且将依赖类解耦。
这是C#中的一个例子:
public interface IShape { int X{get;} int Y{get;} void Draw(); } public class Circle:IShape { public int X{get;set;} public int Y{get;set;} public void Draw() { //Draw a circle } } public class Rectangle:IShape { public int X{get;set;} public int Y{get;set;} public void Draw() { //Draw a rectangle } }
“Mixin”的含义在Joshua Bloch的有效Java书中得到了很好的定义。 同一本书的摘录:
“ mixin是一个类,除了”主types“之外,类还可以声明它提供了一些可选的行为,例如,Comparable是一个mixin接口,允许类声明它的实例相对于其他相互比较的对象,这样的接口被称为mixin,因为它允许可选的function被“混合”到types的主要function中。
由于许多人已经解释了这些定义和用法,我只想强调一下重要的一点
接口:
- 要定义合同(最好是无状态 – 我的意思是没有变数)
- 连接无关的类与“
has a
”的能力。 - 声明公共常量variables(不可变状态)
抽象类:
-
在几个密切相关的类中共享代码。 它确立了“
is a
”的关系。 -
在相关类之间共享状态(可以在具体类中修改状态)
我用一个小例子来弥补差异。
Animal
可以是一个抽象的类。 Dog
,延伸这个抽象类,build立“ is a
”关系。
猫 is a
动物
狗 is a
动物。
狗 can
实现Bark
界面。 那么狗has a
吠叫has a
能力。
Cat can
实现Hunt
接口。 然后猫has a
狩猎has a
能力。
not Animal
可以实现Hunt
接口。 那么曼has a
狩猎has a
能力。
人与动物(猫/狗)是无关的。 但是Hunt接口可以为不相关的实体提供相同的能力。
混入:
- 如果你想要
abstract class
和interface
。 当你想在许多不相关的类上强制一个新的合同时,特别有用,其中一些不必要的重新定义新的行为,其中一些应该坚持共同的实现。 在Mixin中添加通用实现,并允许其他类根据需要重新定义合同方法
如果我想宣布一个抽象类,我将遵循这两种方法之一。
-
将所有的抽象方法移动到
interface
,我的抽象类实现该接口。interface IHunt{ public void doHunting(); } abstract class Animal implements IHunt{ } class Cat extends Animal{ public void doHunting(){} }
相关的SE问题:
界面和抽象类有什么区别?