Подтвердить что ты не робот

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

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

4b9b3361

Ответ 1

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

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

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

Mixin

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

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

Это не значит, что миксины плохие. Множественное наследование неплохое. Способ, которым С++ разрешает множественное наследование, - это то, о чем все заботятся.

На усталом, старом примере формы

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

Вы заметите, что нет разницы между этим и абстрактным примером класса.

Один дополнительный лакомый кусочек - это то, что С# поддерживает миксины с версии 3.0. Вы можете сделать это с помощью методов расширения на интерфейсах. Здесь пример формы с реальным (!) С# кодом mixin style

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

Ответ 2

В общем:

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

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

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

interface RequestHandler {
  void handleRequest(Request request);
}

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

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.

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

Ответ 3

Ссылка на Java и приведенный пример абстрактного класса для предоставления mixin вводит в заблуждение. Прежде всего, Java не поддерживает "mixins" по умолчанию. В языке Java абстрактный класс и Mixins запутываются.

Mixin - это тип, который класс может реализовать в дополнение к его "первичному типу", чтобы указать, что он обеспечивает некоторое необязательное поведение. Чтобы говорить на Java, одним из примеров может быть ваш объект бизнес-ценности, реализующий Serializable.

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

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

Ответ 4

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

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

Ответ 5

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

Вот пример в С#:

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

}

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

Вот пример в С#:

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

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

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

Ответ 7

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

Интерфейс:

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

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

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

  • Доля общего состояния среди связанных классов (состояние может быть изменено в конкретных классах)

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

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

Cat is a Животное

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

Собака can реализует интерфейс Bark. Тогда Dog has a способность лаять.

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

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

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

Mixin:

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

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

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

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

Связанный вопрос SE:

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