抽象类与接口与mixins

时间:2009-05-27 22:59:55

标签: oop interface terminology abstract-class mixins

有人可以向我解释抽象类接口 mixins 之间的区别吗?我以前在我的代码中使用了每个,但我不知道技术差异。

7 个答案:

答案 0 :(得分:69)

抽象类

抽象类是一个不是为实例化而设计的类。抽象类可以没有实现,某些实现或所有实现。抽象类旨在允许其子类共享一个公共(默认)实现。抽象类的(伪编码)示例将是这样的

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关键字可以看作是界面概念的具体化。

回到Shape示例

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(有时称为trait)允许抽象类的多重继承。 Mixins没有多重继承所带来的可怕关联(由于C ++疯狂),所以人们更习惯使用它们。它们具有相同的致命死亡钻石问题,但支持它们的语言比C ++具有更优雅的缓解方式,因此它们被认为更好。

Mixins被誉为具有behavioral reusemore flexible接口和more powerful接口的接口。您将注意到所有这些都包含术语interface,指的是Java和C#关键字。 Mixins不是接口。它们是多重继承。有一个更漂亮的名字。

这并不是说mixin很糟糕。多重继承也不错。 C ++解析多重继承的方式是每个人都能解决的问题。

对疲惫的旧形状示例

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());
        }
    }
}

答案 1 :(得分:21)

一般来说:

接口是指定操作的合同,但没有任何实现。有些语言(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

答案 2 :(得分:6)

引用Java并给出Abstract类的示例以提供mixin是误导性的。 首先,Java默认不支持“mixins”。在Java术语中,抽象类和Mixins变得令人困惑。

mixin是类可以实现的类型,除了它的“主类型”以指示它提供一些可选行为。用Java术语来说,一个例子是实现Serializable的业务价值对象。

Josh Bloch说 - “抽象类不能用于定义mixins - 因为一个类不能有多个父级”(记住Java只允许一个“扩展”候选者)

寻找像Scala和Ruby这样的语言来正确实现“mixin”的概念

答案 3 :(得分:3)

基本上,抽象类是具有一些具体实现的接口。接口只是一个没有实现细节的合同。

如果要在实现抽象类的所有对象中创建公共功能,则可以使用和抽象类。保持OOP的DRY(不要重复自己)原则。

答案 4 :(得分:3)

由于许多人已经解释了定义和用法,我想强调一些重点

界面:

  1. 定义合同(最好是无状态 - 我的意思是没有变量)
  2. 将不相关的类与&#34; has a&#34;相关联。能力。
  3. 声明公共常量变量(不可变状态)
  4. 抽象类:

    1. 在几个密切相关的类之间共享代码。它建立了&#34; is a&#34;关系。

    2. 在相关类之间共享公共状态(状态可以在具体类中修改)

    3. 我用一个小例子来弥补差异。

      Animal可以是一个抽象类。 CatDog,扩展此抽象类会建立&#34; is a&#34;关系。

      is a动物

      is a动物。

      Dog can实施Bark界面。然后是狗has a Barking的能力。

      Cat can实施Hunt界面。然后是Cat has a狩猎的能力。

      not Animal

      可以实现Hunt界面。然后是has a狩猎的能力。

      人与动物(猫/狗)无关。但是Hunt接口可以为不相关的实体提供相同的功能。

      <强> 密新:

      1. 如果您想混合使用abstract classinterface。当您想要在许多不相关的类上强制新合同时,尤其有用,其中一些不得不重新定义新行为,其中一些应该坚持常见的实现。在Mixin中添加常用实现,并允许其他类在需要时重新定义合同方法
      2. 如果我想宣布一个抽象类,我将遵循这两种方法中的一种。

        1. 将所有抽象方法移至interface,我的抽象类实现该接口。

          interface IHunt{
              public void doHunting();
          }
          abstract class Animal implements IHunt{
          
          }
          class Cat extends Animal{
              public void doHunting(){}
          }
          
        2. 相关的SE问题:

          What is the difference between an interface and abstract class?

答案 5 :(得分:1)

抽象类是一个不是所有成员都被实现的类,它们留给实现的继承者。它强制它的继承者实现它的抽象成员。 抽象类无法实例化,因此它们的构造函数不应该是公共的。]

这是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
}
}

答案 6 :(得分:1)

&#39; Mixin&#39;约书亚布洛赫在他有效的爪哇书中出色地定义了这本书。同一本书的摘录:

&#34; mixin是一种类型 一个类除了声明它提供的“主要类型”之外还可以实现 一些可选行为。例如,Comparable是一个mixin接口 允许类声明其实例是相对于其他实例进行的 可比对象。这样的界面被称为mixin,因为它允许 可选功能“混入”类型的主要功能。&#34;