Миксины

Последнее обновление: 03.08.2023

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

class Worker{
    String company = "";
    void work()=>print("works in $company");
}
class Student{
    String university = "";
    void study() =>print("studies in $university");
}
class WorkingStudent implements Worker, Student{
    
    String name ="";
    @override
    String company = "";
    @override
    String university;
    WorkingStudent(this.name, this.university, this.company);
    @override
    void study() =>print("studies in $university");
    @override
    void work()=>print("works in $company");
}

Здесь класс WorkingStudent, который представляет работающего студента, реализует два интерфейса - Work (рабочего) и Student (студента). Соответственно WorkingStudent должен реализовать все поля и методы, определенные в Worker и Student. Но класс WorkingStudent по сути ничего нового не определяет, просто повторяет код полей и методов. И миксины как раз позволяют решить эту проблему.

В плане определения миксин похож на обычный класс, только определяется с помощью ключевого слова mixin. Кроме того, миксин не может содержать конструктор. Также миксин не может применять другие миксины и не может наследовать другие классы.

При определении миксинов есть два варианта. Если мы хотим, чтобы миксин мог также использоваться как обычный класс, то он определяется с помощью слов mixin class. Но если мы хотим создать чистый миксин, то для определения используется просто mixin.

Для применения миксинов применяется оператор with, после которого указываются применяемые классы:

mixin class A{}     // класс-миксин
mixin B{}           // чистый миксин
class С with A, B {}    // применение миксинов

Так, перепишим предыдущий пример с использованием миксинов:

mixin class Worker{
    String company = "Microsoft";
    void work()=>print("works in $company");
}
mixin Student{
    String university = "Stanford";
    void study() =>print("studies in $university");
}
class WorkingStudent with Worker, Student{
     
    String name;
    WorkingStudent(this.name, String school, String job){
        university = school;
        company = job;
    }
}
void main (){
 
    WorkingStudent tom = WorkingStudent("Tom", "MIT", "Google");
    tom.work();      // works in Google
    tom.study();     // studies in MIT
}

В данном случае Worker и Student выступают в роли миксинов. Причем Worker - класс-миксин, а Student - чистый миксин. Для применения этих миксинов при определении класса WorkingStudent применяется оператор with:

class WorkingStudent with Worker, Student{

После этого класс WorkingStudent может использовать поля и методы из класса Worker и Student.

Причем, так как Worker - класс-миксин, мы можем использовать его независимо - создавать его объекты, изменять его поля, вызывать его методы и т.д., например:

mixin class Worker{
    String company = "Microsoft";
    void work()=>print("works in $company");
}
void main (){

    Worker bob = Worker();
    bob.work();     // works in Microsoft
    bob.company = "Apple";
    bob.work();     // works in Apple
}

Обратите внимание, что для Worker мы можем вызвать конструктор без параметров для создания объекта данного типа.

В свою очередь, миксин Student выступает как чистый миксин, поэтому мы можем его использовать только как миксин. Если мы попробуем создать его объект, как в следующем случае, то мы получим ошибку:

mixin Student{
    String university = "Stanford";
    void study() =>print("studies in $university");
}

void main (){
 
    Student sam = Student();    // ! Ошибка: Couldn't find constructor 'Student'.
    sam.study();
}

Переопределение миксинов

Если нам не устраивает полученная функциональность классов-миксинов, то мы можем ее переопределить:

mixin class Worker{
    String company = "Microsoft";
    void work()=>print("works in $company");
}
mixin Student{
    String university = "Stanford";
    void study() =>print("studies in $university");
}
class WorkingStudent with Worker, Student{
     
    String name;
    WorkingStudent(this.name, String univer, String job){
        university = univer;
        company = job;
    }
    // переопределяем методы миксинов
    @override
    void work()=>print("$name works in $company");
    @override
    void study() =>print("$name studies in $university");
}
void main (){
 
    WorkingStudent tom = WorkingStudent("Tom", "MIT", "Google");
    tom.work();      // Tom works in Google
    tom.study();     // Tom studies in MIT
}

Сочетание миксинов с наследованием

Также мы можем комбинировать применение миксинов с наследованием:

mixin class Worker{
    String company = "Microsoft";
    void work()=>print("works in $company");
}
class Student{
    String university;
    Student(this.university);
    void study() =>print("studies in $university");
}
class WorkingStudent extends Student with Worker{
     
    String name;
    WorkingStudent(this.name, super.university, String job){
        company = job;
    }
    @override
    void work()=>print("$name works in $company");
    @override
    void study() =>print("$name studies in $university");
}
void main (){
 
    WorkingStudent tom = WorkingStudent("Tom", "MIT", "Google");
    tom.work();      // Tom works in Google
    tom.study();     // Tom studies in MIT
}

В данном случае для примера сущность Student определена как обычный класс, которую можно унаследовать.

Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850