mixin이 뭔가요?

flunge·2021년 9월 25일
7

Dart

목록 보기
1/2
post-thumbnail

with?

Flutter 개발을 하다보면 심심치 않게 with라는 키워드를 만나게 된다. 사용 위치가 extends나 implements의 옆이기 때문에 비슷한 쓰임이구나 예상은 되지만 무엇이고 왜 쓰는 것일까?라는 궁금증을 해결하기 위해 공부하고 정리해봤다.

with는 Mixin으로 사용한다는 일종의 키워드이다.

클래스를 상속받기 위해서 사용하는 extends, 인터페이스를 구현하기 위해 사용하는 implements처럼 Mixin을 사용한다는 키워드이다.

Mixin을 "상속받는다" 혹은 "구현한다" 라는 표현을 쓰지 않은 이유는 그 방식이 조금 다르다고 느껴서이다. 정확한 명칭은 모르겠고 제 주관적인 뇌피셜입니다.

그럼 Mixin은 뭔데?

"Mixins are a way of reusing a class’s code in multiple class hierarchies."
dart.dev에서의 mixin에 대한 한줄 소개로 여러 클래스 계층 구조에서 클래스의 코드를 재사용하는 방법이라고 한다. 클래스와 인터페이스 중간에 위치한 무엇이다 라는 것이 개인적인 결론이다.

간단히 키워드별로 비교를 하자면

extends

  • 상위 클래스의 필드, 메소드들을 상속받아 바로 사용할 수 있다.
  • 대부분 언어에서 다중 상속이 불가능하다.

implements

  • 메소드의 이름, 반환값, 매개변수만 정해져 있어 바로 사용할 수 없고 이것을 오버라이딩해서 사용해야한다.
  • 여러개의 인터페이스를 상속받고 구현해서 사용할 수 있다.

with

  • 메소드가 구현되어 있어 바로 사용할 수 있다.
  • 다중 상속이 가능하다.

나름 두줄 요약을 통해 설명을 해놨는데 이렇게만 보면 이걸 왜씀? 생각이 들정도로 포지션이 굉장히 애매하다.

예시

눈으로 보고 손가락으로 느끼는게 가장 확실하다.

class Bird{
	void fly(){
    	print('I can fly');
    }
}

class Fish{
	void swim(){
    	print('I can swim');
    }
}

Bird클래스의 객체는 fly()라는 메소드를, Fish라는 클래스는 swim()이라는 메소드를 가지고 있다.

class Pigeon extends Bird{
	void gugu(){
    	print('gugu');
    }
}

Bird를 상속받은 Pigeon은 새로만든 gugu()말고도 Bird의 fly()를 호출 할 수 있을것이다.

class Goose extends Bird{
	void swim(){
    	print('I can fly');
    }
}

거위나 오리는 새지만 헤엄을 칠 수 있다. 하지만 이미 Bird를 상속받은데다가 Fish는 상속을 받을 수도 없다. 그렇기 때문에 swim()이라는 메소드를 새로 만들었는데 이럴때 Mixin이 유용하게 사용될 수 있다.

이런식으로 with를 이용해 Bird와 Fish를 사용한다고 명시하면 두 mixin의 메소드를 사용할 수 있다.


파란 밑줄은 프로덕션 단계의 앱에서 print문의 사용을 피하라는 lint입니다.



이런식으로 코드의 재사용성이 그 전보다 월등히 좋아졌다.

코드의 재사용성을 위한 기능이기때문에 그럼 혹시? 하는것들은 전부 되는것 같다.

class Goose extends Bird with Fish{}

이건 당연히 될 것이고

class Bird{
	...
}

class Goose with Bird{}

이렇게 클래스를 mixin으로도 사용이 가능하다. 이미 만들어논 클래스를 mixin으로 수정하지 않아도 된다!

Mixin과 순서

mixin Bird{
  void fly(){
    print('I can fly');
  }
  void say(){
    print('I am a bird');
  }

}

mixin Fish{
  void swim(){
    print('I can swim');
  }
  void say(){
    print('I am a fish');
  }
}

class Creature{
  void breath(){
    print('I can breath');
  }
  void say(){
    print('I am a creature');
  }
}

class Goose extends Creature with Bird, Fish{

}

Creature를 상속받고 Bird와 Fish로 확장하는 Goose클래스를 이용해 객체를 생성하고 모두 공통으로 가지고 있는 say()를 호출하면 누구의 say()가 호출될까?에대한 궁금증이 생긴다.

정답은 I am a fish가 콘솔에 찍히게 된다. mixin이 선언되는 순서가 상위에서 하위까지의 체인을 나타내게 된다.

이유는 mixin이 일반적인 상속을 얻는 방법이 아니기 때문이다. mixin은 클래스를 확장해 재사용하는것과 비슷하지만 선형이기 때문에 단일상속과 결과적으로 같게된다. 그렇기 때문에 순서가 중요한 것이고 가장 나중에 선언된 Fish의 say() 호출된 것이다.

결론

mixin은 기존의 상속의 개념과는 다른 무엇이며 코드의 재사용에 특화된 것으로 보인다. 특히 완전히 다른 계층의 클래스들에서 코드를 재상용할 수 있다는건 엄청난 메리트로 느껴진다.

❗포스트에 잘못된 내용이 있다면 알려주세요!

포스트의 내용이 잘못돼있을 수 있습니다. 너그러운 마음으로 이해해 주시고 알려주세요!

1개의 댓글

comment-user-thumbnail
2022년 6월 26일

잘 읽었습니다.~

답글 달기