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