믹스인
믹스인 (Mixins)
여러 클래스 계층에서 재사용할 코드를 정의하고 싶을 때가 있어요. 믹스인은 바로 그런, 여러 클래스 계층에서 재사용할 수 있는 코드를 정의하는 방법이에요. 멤버 구현을 한꺼번에(즉, 집단적으로) 제공하려는 목적을 갖고 있죠.
출처: Dart 공식 문서
본문
믹스인을 사용하려면 with 키워드 뒤에 믹스인 이름을 하나 이상 적어요. 다음 예시는 믹스인을 사용하는(또는 믹스인의 하위 클래스인) 두 클래스를 보여줘요.
class Musician extends Performer with Musical {
// ···
}
class Maestro extends Person with Musical, Aggressive, Demented {
Maestro(String maestroName) {
name = maestroName;
canConduct = true;
}
}
믹스인을 정의할 때는 mixin 선언을 써요. 드물게 믹스인 과 클래스를 모두 정의해야 하는 경우에는 mixin class 선언을 쓸 수 있어요.
믹스인과 믹스인 클래스는 extends 절을 가질 수 없고, 생성 생성자(generative constructor)를 선언해서도 안 돼요.
예를 들면:
mixin Musical {
bool canPlayPiano = false;
bool canCompose = false;
bool canConduct = false;
void entertainMe() {
if (canPlayPiano) {
print('Playing piano');
} else if (canConduct) {
print('Waving hands');
} else {
print('Humming to self');
}
}
}
믹스인이 스스로 호출할 멤버 지정하기 (Specify members a mixin can call on itself)
때로 믹스인은 메서드를 호출하거나 필드에 접근해야 하는데, 자기 자신은 그 멤버를 정의할 수 없을 때가 있어요. 믹스인은 생성자 매개변수로 자기 필드를 초기화할 수 없으니까요. 다음 절들은 믹스인의 하위 클래스가 그 믹스인의 동작이 의존하는 멤버를 정의하도록 보장하는 여러 전략을 다뤄요.
믹스인에 추상 멤버 정의하기 (Define abstract members in the mixin)
믹스인에 추상 메서드를 선언하면, 그 믹스인을 사용하는 어떤 타입이든 그 동작이 의존하는 추상 메서드를 정의해야 해요.
mixin Musician {
void playInstrument(String instrumentName); // Abstract method.
void playPiano() {
playInstrument('Piano');
}
void playFlute() {
playInstrument('Flute');
}
}
class Virtuoso with Musician {
@override
void playInstrument(String instrumentName) { // Subclass must define.
print('Plays the $instrumentName beautifully');
}
}
믹스인의 하위 클래스에서 상태에 접근하기 (Access state in the mixin's subclass)
추상 멤버를 선언하면 믹스인에서 추상으로 정의된 getter를 호출해서 믹스인의 하위 클래스 상태에 접근할 수도 있어요.
/// Can be applied to any type with a [name] property and provides an
/// implementation of [hashCode] and operator `==` in terms of it.
mixin NameIdentity {
String get name;
@override
int get hashCode => name.hashCode;
@override
bool operator ==(other) => other is NameIdentity && name == other.name;
}
class Person with NameIdentity {
final String name;
Person(this.name);
}
NameIdentity 믹스인은 name getter를 갖춘 어떤 타입에도 적용될 수 있어요. Person이 name을 제공하기 때문에 hashCode와 ==를 믹스인으로부터 받아 쓸 수 있습니다.
인터페이스 구현하기 (Implement an interface)
믹스인을 추상으로 선언하는 것과 비슷하게, 믹스인에 implements 절을 넣되 실제로 그 인터페이스를 구현하지 않으면, 멤버 의존성이 믹스인에 대해 정의되도록 보장할 수도 있어요.
abstract interface class Tuner {
void tuneInstrument();
}
mixin Guitarist implements Tuner {
void playSong() {
tuneInstrument();
print('Strums guitar majestically.');
}
}
class PunkRocker with Guitarist {
@override
void tuneInstrument() {
print("Don't bother, being out of tune is punk rock.");
}
}
Guitarist는 playSong 안에서 tuneInstrument()를 호출하지만 자신은 구현하지 않아요. implements Tuner라고 선언했으니, Guitarist를 사용하는 타입은 tuneInstrument를 구현해야 하죠. PunkRocker가 그걸 정의해서 사용합니다.
on 절로 수퍼클래스 선언하기 (Use the on clause to declare a superclass)
on 절은 super 호출이 해석될 타입을 정의하기 위해 존재해요. 그래서 믹스인 안에서 super 호출이 필요할 때만 사용해야 해요.
on 절은 믹스인을 사용하는 어떤 클래스든 그 on 절의 타입의 하위 클래스여야 한다는 걸 강제해요. 믹스인이 수퍼클래스의 멤버에 의존한다면, 이 is는 그 멤버들이 믹스인이 사용되는 곳에 존재하도록 보장해요.
class Musician {
musicianMethod() {
print('Playing music!');
}
}
mixin MusicalPerformer on Musician {
performerMethod() {
print('Performing music!');
super.musicianMethod();
}
}
class SingerDancer extends Musician with MusicalPerformer { }
main() {
SingerDancer().performerMethod();
}
이 예시에서 Musician 클래스를 확장하거나 구현하는 클래스만 MusicalPerformer 믹스인을 쓸 수 있어요. SingerDancer는 Musician을 확장하므로 MusicalPerformer를 믹스인으로 섞을 수 있죠.
class, mixin, 아니면 mixin class? (class, mixin, or mixin class?)
mixin class선언은 언어 버전이 최소 3.0이어야 해요.
mixin 선언은 믹스인을 정의해요. class 선언은 클래스를 정의하죠. mixin class 선언은 같은 이름과 같은 타입으로, 일반 클래스이면서 동시에 믹스인으로도 사용할 수 있는 클래스를 정의해요.
mixin class Musician {
// ...
}
class Novice with Musician { // Use Musician as a mixin
// ...
}
class Novice extends Musician { // Use Musician as a class
// ...
}
클래스나 믹스인에 적용되는 제약은 믹스인 클래스에도 적용돼요.
- 믹스인은
extends나with절을 가질 수 없으므로,mixin class도 가질 수 없어요. - 클래스는
on절을 가질 수 없으므로,mixin class도 가질 수 없어요.