Абстрактные классы против интерфейсов против миксинов

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


person Sasha Chedygov    schedule 27.05.2009    source источник
comment
Отмечено как «оп», потому что это казалось более актуальным, чем тег «концепции».   -  person Paul Morie    schedule 28.05.2009


Ответы (7)


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

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

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

Mixin

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

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

Это не значит, что миксины плохие. Множественное наследование - это неплохо. Все волнуются о том, как 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. Вы можете сделать это с помощью методов расширения на интерфейсах. Вот пример формы с реальным (!) Стилем микширования кода 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());
        }
    }
}
person Eva    schedule 24.03.2013
comment
Ух, я ненавижу менять принятые ответы, но этот, несомненно, лучший. - person Sasha Chedygov; 26.03.2013
comment
Для ясности: Mixin \ Traits - это способы расширения функциональности класса (например, добавления методов) без использования наследования. Ответы предполагают, что это одно и то же, но есть некоторые важные различия. 1. Миксины могут иметь состояние, черты не сохраняют состояние 2. Миксины используют неявное разрешение конфликтов, черты используют явное разрешение конфликтов (подробнее stackoverflow .com / questions / 925609 / mixins-vs-traits). - person Igor; 25.03.2017

В целом:

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

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

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

interface RequestHandler {
  void handleRequest(Request request);
}

Возможно, было бы полезно буферизовать запросы, накапливая их до тех пор, пока у нас не будет некоторого заранее определенного числа, а затем очистить буфер. Мы можем реализовать функцию буферизации с помощью микширования без указания поведения сброса:

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 .

person Paul Morie    schedule 27.05.2009
comment
Отлично, это именно то, что мне нужно, спасибо. Однако одно: не могли бы вы уточнить, как миксины упрощают реализацию некоторого поведения в подклассах? - person Sasha Chedygov; 28.05.2009
comment
Сообщите мне, поможет ли этот пример. - person Paul Morie; 28.05.2009
comment
Подождите, может быть, я что-то упускаю, но разве BufferedRequestHandlerMixin не абстрактный класс? Чем он отличается от миксина? - person mR_fr0g; 17.06.2009
comment
mR_fr0g, да, BufferedRequestHandlerMixin реализован как абстрактный класс. Обычно миксины реализуются с использованием абстрактных классов в Java, поскольку ключевое слово abstract отмечает, что класс предназначен для повторного использования и не предназначен для использования сам по себе (он также, очевидно, не позволяет вам использовать его отдельно). - person Paul Morie; 17.06.2009
comment
Абстрактный класс - это класс, который определяет по крайней мере одну операцию без реализации: не совсем верно для .NET. Например, следующее допустимое определение класса в C #: публичный абстрактный класс Foo {} - person Darin Dimitrov; 29.07.2009

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

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

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

Поищите такие языки, как Scala и Ruby, для соответствующей реализации понятия «миксин».

person Community    schedule 29.07.2009
comment
Также JavaScript, наиболее широко используемый язык с концепцией миксинов. - person Gaurav Ramanan; 05.02.2013
comment
В версии java 8 методы по умолчанию в интерфейсе предоставляют функцию смешивания. - person Ravindra babu; 08.01.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:

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

person Ravindra babu    schedule 08.01.2016

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

Вы должны использовать абстрактный класс, если хотите создать общую функциональность среди всех объектов, реализующих абстрактный класс. Соблюдение принципа DRY (Don't Repeat Yourself) ООП.

person Jon Erickson    schedule 27.05.2009
comment
Это еще не все. Например, абстрактный класс может определять абстрактный защищенный метод, а интерфейс - нет. И вы можете реализовать любое количество интерфейсов, но вы можете расширить только один абстрактный класс (если ваш язык не имеет множественного наследования). - person n3rd; 28.05.2009
comment
да, это определенно нечто большее, чем то, что я описал (и @musicfreak, вам следует изучить больше, потому что по этой теме много). просто пытался дать быстрое определение OP, поскольку он заявил, что хочет чего-то менее технического. - person Jon Erickson; 28.05.2009
comment
+1 за то, что он был простым и точным. Также, n3rd, спасибо за информацию. - person Sasha Chedygov; 28.05.2009

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

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

person Sumit Sengar    schedule 08.05.2014

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

Вот пример на 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
}
}
person Beatles1692    schedule 27.05.2009