Абстрактные классы против интерфейсов против миксинов - PullRequest
63 голосов
/ 28 мая 2009

Может ли кто-нибудь объяснить мне различия между абстрактными классами , интерфейсами и mixins ? Я использовал каждый из них в своем коде, но я не знаю технических отличий.

Ответы [ 7 ]

71 голосов
/ 25 марта 2013

Абстрактный класс

Абстрактный класс - это класс, который не предназначен для реализации. Абстрактные классы могут не иметь никакой реализации, какой-либо реализации или всей реализации. Абстрактные классы предназначены для того, чтобы их подклассы имели общую (стандартную) реализацию. (Псевдокодированный) пример абстрактного класса будет выглядеть примерно так

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 # используют интерфейсы в качестве обходного пути к проблеме Смертельный бриллиант смерти , обнаруживаемой в языках, которые допускают множественное наследование (что на самом деле не так уж смертельно при правильной обработке).

Mixin

Миксин (иногда называемый признаком) позволяет множественное наследование абстрактных классов. Миксины не имеют страшной связи, которая имеет множественное наследование (из-за сумасшествия C ++), поэтому людям удобнее ими пользоваться. У них точно такая же проблема Смертельного Бриллианта Смерти, но языки, которые их поддерживают, имеют более изящные способы ее смягчения, чем С ++, поэтому они воспринимаются как лучшие.

Миксины приветствуются как интерфейсы с поведенческим повторным использованием , более гибкими интерфейсами и более мощными интерфейсами. Вы заметите, что все они содержат термин interface, относящийся к ключевым словам Java и C #. Миксины не являются интерфейсами. Они являются множественным наследованием. С красивым именем.

Это не значит, что миксины плохие. Многократное наследование не плохо. То, как 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. Вы можете сделать это с помощью методов расширения на интерфейсах. Вот пример Shape с настоящим (!) Миксин-стилем кода C #

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());
        }
    }
}
21 голосов
/ 28 мая 2009

В целом:

интерфейс - это контракт, определяющий операции, но без какой-либо реализации. Некоторые языки (Java, C #) имеют встроенную поддержку интерфейсов, а в других «интерфейс» описывает соглашение, подобное чисто виртуальному классу в C ++.

абстрактный класс - это класс, который определяет хотя бы одну операцию без реализации. Абстрактные классы также могут предоставлять некоторые части их реализации. Опять же, некоторые языки имеют встроенную поддержку для обозначения классов как абстрактных, и это неявно в других. Например, в C ++ класс, который определяет чисто виртуальный метод, является абстрактным.

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

Таким образом, нам легко написать обработчик запросов, который записывает запросы на диск, вызывает веб-сервис и т. Д., Не переписывая каждый раз функциональность буферизации. Эти обработчики запросов могут просто расширять BufferedRequestHandlerMixin и реализовывать flushBuffer.

Другим хорошим примером миксина является один из многих классов поддержки в Spring, а именно. HibernateDaoSupport .

6 голосов
/ 29 июля 2009

Ссылка на Java и приведенный пример класса Abstract для предоставления mixin вводит в заблуждение. Во-первых, Java по умолчанию не поддерживает «mixins». В терминах Java абстрактный класс и Mixins становятся запутанными.

Миксин - это тип, который класс может реализовать в дополнение к своему «первичному типу», чтобы указать, что он обеспечивает некоторое необязательное поведение. Говоря в терминах Java, одним примером будет объект вашей коммерческой ценности, реализующий Serializable.

Джош Блох говорит - «Абстрактные классы не могут использоваться для определения миксинов - поскольку класс не может иметь более одного родителя» (помните, что Java допускает только одного «расширяющего» кандидата)

Ищите языки, такие как Scala и Ruby, для соответствующей реализации понятия "mixin"

3 голосов
/ 08 января 2016

Так как многие из ребят объяснили об определениях и использовании, я хотел бы выделить только важные моменты

Интерфейс:

  1. Чтобы определить контракт (желательно без гражданства - я имею в виду без переменных)
  2. Чтобы связать несвязанные классы с возможностями "has a".
  3. Чтобы объявить публичные постоянные переменные (неизменяемое состояние)

Абстрактный класс:

  1. Поделиться кодом среди нескольких тесно связанных классов. Он устанавливает отношение "is a".

  2. Совместное использование общего состояния между связанными классами (состояние может быть изменено в конкретных классах)

Я закрываю разницу небольшим примером.

Animal может быть абстрактным классом. Cat и Dog, расширение этого абстрактного класса устанавливает отношение "is a".

Кошка is a Животное

Собака is a Животное.

Собака can Реализация Bark Интерфейс. Тогда Дог has a Способность Лая.

Cat can агрегат Hunt интерфейс. Тогда Cat has a Способность к Охоте.

Человек , который является not Animal, может реализовать интерфейс Hunt. Тогда Человек has a Способность Охоты.

Человек и животное (кошка / собака) не связаны. Но интерфейс Hunt может предоставлять те же возможности несвязанным объектам.

Mixin:

  1. Если вы хотите смесь как abstract class, так и interface. Особенно полезно, когда вы хотите навязать новый контракт многим несвязанным классам, где некоторые из них должны переопределить новое поведение, а некоторые должны придерживаться общей реализации. Добавьте общую реализацию в Mixin и разрешите другим классам переопределять методы контракта при необходимости

Если я хочу объявить абстрактный класс, я буду следовать одному из этих двух подходов.

  1. Переместите все абстрактные методы в interface, и мой абстрактный класс реализует этот интерфейс.

    interface IHunt{
        public void doHunting();
    }
    abstract class Animal implements IHunt{
    
    }
    class Cat extends Animal{
        public void doHunting(){}
    }
    

Вопрос по теме SE:

В чем разница между интерфейсом и абстрактным классом?

3 голосов
/ 28 мая 2009

В основном абстрактный класс - это интерфейс с некоторой конкретной реализацией. Интерфейс - это просто контракт, в котором нет деталей реализации.

Вы бы использовали и абстрактный класс, если хотите создать общую функциональность среди всех объектов, которые реализуют абстрактный класс. Соблюдение принципа СУХОЙ (не повторять себя) ООП.

1 голос
/ 08 мая 2014

Значение «Mixin» превосходно определено Джошуа Блохом в его эффективной книге по Java. Отрывок из той же книги:

" миксин является типом что класс может реализовать в дополнение к своему «первичному типу», чтобы объявить, что он обеспечивает какое-то необязательное поведение. Например, Comparable - это смешанный интерфейс, который позволяет классу объявить, что его экземпляры упорядочены относительно других взаимно сопоставимые объекты. Такой интерфейс называется mixin, потому что он позволяет дополнительные функции, которые нужно «смешать» с основными функциями типа."

1 голос
/ 28 мая 2009

Абстрактный класс - это класс, в котором реализованы не все его члены, они оставлены для реализации наследниками. Это заставляет его наследников реализовывать его абстрактные члены. Абстрактные классы не могут быть созданы, поэтому их конструкторы не должны быть открытыми.]

Вот пример на 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
}
}
...