【发布时间】:2010-10-29 10:29:06
【问题描述】:
有人可以向我解释一下抽象类、接口和mixins之间的区别吗?我以前在我的代码中使用过每个,但我不知道技术差异。
【问题讨论】:
-
重新标记为“oop”,因为它似乎比标记“概念”更相关。
标签: oop interface terminology abstract-class mixins
有人可以向我解释一下抽象类、接口和mixins之间的区别吗?我以前在我的代码中使用过每个,但我不知道技术差异。
【问题讨论】:
标签: oop interface terminology abstract-class mixins
抽象类是不被设计为实例化的类。抽象类可以没有实现、某些实现或全部实现。抽象类旨在允许其子类共享一个通用(默认)实现。抽象类的(伪编码)示例将是这样的
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 关键字。这些或多或少是一个没有实现的抽象类。 (关于常量、嵌套类、显式实现和访问修饰符有一些技巧,我不打算讨论。)虽然关于“无实现”的部分不再适合 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# 不允许对具有实现的类进行多重继承,但它们确实允许实现多个接口。 Java 和 C# 使用接口作为在允许多重继承的语言中发现的 Deadly Diamond of Death Problem 的解决方法(如果处理得当,这并不是那么致命)。
mixin(有时称为特征)允许抽象类的多重继承。 Mixins 没有多重继承所具有的可怕关联(由于 C++ 的疯狂),因此人们更愿意使用它们。它们具有完全相同的致命钻石死亡问题,但支持它们的语言有比 C++ 更优雅的缓解方法,因此它们被认为更好。
Mixins 被誉为与behavioral reuse、more flexible 接口和more powerful 接口的接口。您会注意到所有这些都包含术语interface,指的是Java 和C# 关键字。 混合不是接口。它们是多重继承。有一个更漂亮的名字。
这并不是说 mixins 不好。多重继承也不错。 C++ 解决多重继承的方式是每个人都在讨论的问题。
关于疲惫的、陈旧的 Shape 示例
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#代码混合样式的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());
}
}
}
【讨论】:
由于很多人已经解释了定义和用法,我想只强调重点
界面:
has a”功能链接不相关的类。抽象类:
在几个密切相关的类之间共享代码。它建立了“is a”关系。
在相关类之间共享公共状态(状态可以在具体类中修改)
我用一个小例子来结束差异。
Animal 可以是抽象类。 Cat 和Dog,扩展这个抽象类建立“is a”关系。
猫 is a动物
狗 is a动物。
狗 can 实现Bark 接口。然后 Dog has a 的吠叫能力。
猫 can 实现Hunt 接口。然后猫has a 狩猎能力。
Man,即not Animal,可以实现Hunt接口。然后 Man 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 问题:
What is the difference between an interface and abstract class?
【讨论】:
Joshua Bloch 在其有效的 Java 书籍中很好地定义了“混合”的含义。同一本书的摘录:
"mixin 是一种类型 一个类可以实现除了它的“主要类型”来声明它提供 一些可选的行为。例如,Comparable 是一个 mixin 接口,它 允许一个类声明它的实例是相对于其他相互排序的 可比较的对象。这样的接口称为 mixin,因为它允许 可选功能“混合”到类型的主要功能中。"
【讨论】:
对 Java 的引用和提供 mixin 的抽象类的示例具有误导性。 首先,Java 默认不支持“mixins”。在 Java 术语中,抽象类和 Mixins 变得令人困惑。
mixin 是一个类除了它的“主要类型”之外还可以实现的类型,以表明它提供了一些可选的行为。用 Java 术语来说,一个例子就是实现 Serializable 的业务价值对象。
Josh Bloch 说 - “抽象类不能用于定义 mixins - 因为一个类不能有多个父类”(记住 Java 只允许一个“扩展”候选者)
寻找像 Scala 和 Ruby 这样的语言来适当地实现“mixin”的概念
【讨论】:
一般:
接口是一个指定操作的合约,但没有任何实现。一些语言(Java、C#)已经内置了对接口的支持,而在另一些语言中,“接口”描述了一种约定,就像 C++ 中的纯虚拟类一样。
抽象类是一个类,它指定了至少一个没有实现的操作。抽象类还可以提供其实现的某些部分。同样,一些语言已经内置支持将类标记为抽象,而在其他语言中它是隐含的。例如,在 C++ 中,定义纯虚方法的类是抽象类。
mixin 是一个类,它旨在使子类中某些功能的实现更容易,但它并非旨在供其自身使用。例如,假设我们有一个用于处理请求的对象的接口
interface RequestHandler {
void handleRequest(Request request);
}
也许通过累积请求来缓冲请求直到我们有一些预定的数量然后刷新缓冲区是有用的。我们可以使用 mixin 实现缓冲功能,而无需指定刷新行为:
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 服务等,而无需每次都重写缓冲功能。这些请求处理程序可以简单地扩展BufferedRequestHandlerMixin 并实现flushBuffer。
mixin 的另一个很好的例子是 Spring 中的众多支持类之一,即。 HibernateDaoSupport.
【讨论】:
抽象类是不是所有成员都实现的类,它们留给继承者实现。它强制其继承者实现其抽象成员。 抽象类不能被实例化,因此它们的构造函数不应该是公共的。]
这是一个 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
}
}
【讨论】:
基本上,抽象类是具有一些具体实现的接口。接口只是一个没有实现细节的契约。
如果您想在所有实现抽象类的对象中创建通用功能,您将使用抽象类。遵守 OOP 的 DRY(Don't Repeat Yourself)原则。
【讨论】: