[Dart] mixin 2편 - 활용

티라노·2025년 1월 15일
0

Today I Learned

목록 보기
36/38

들어가기

지난 시간에 mixin이 무엇인지, 클래스와 어떻게 다른지 공부했었다.

내용 참고 :
https://velog.io/@utiranoj/Dart-mixin이란

오늘은 어떤 상황에서 mixin을 활용할 수 있을지 알아보자.


객체지향의 핵심 - 다형성

상속 구조를 잘 활용하면 비슷한 메서드를 여러 번 작성하는 경우를 줄일 수 있다. 그러나 무조건 같은 코드를 여러 곳에서 돌려쓰는 것만이 코드 재사용의 전부를 의미하지는 않는다.

만약 추상 클래스 대신 인터페이스를 활용한다면 메서드는 오버라이딩을 통해 다양한 형태를 가질 수 있다. 이런 성질을 다형성이라고 부른다.

반대로 함수 내용은 정의해두고, 변수를 제네릭으로 받아서 한 가지 기능이 여러 타입의 파라미터에 대해 작동하도록 만드는 경우는 오버로딩이라고 부른다.
오버로딩도 오버라이딩과 함께 다형성을 이루는 요소이다.


다중 상속

코드를 깔끔하게 짜려면 자식 클래스가 여러 가지 부모 클래스를 참조해야 하는 경우가 종종 발생한다.

예시를 들어보자. 휴대전화 는 TV와 라디오의 기능을 모두 가지고 있다.
그러면 TV를 상속받아서 거기에 라디오를 추가로 구현하거나, 반대로 라디오를 상속받아서 TV를 추가하는 것보다는 그냥 한 번에 TV와 라디오를 모두 상속받는 게 편하다.

하지만 기본적으로 추상 클래스는 다중 상속이 불가능하다. 그렇다고 TV와 라디오를 인터페이스로 만들 수는 없다. 그 둘도 스스로 작동하는 메서드를 가지고 있어야 하기 때문이다.

이런 경우 mixinwith를 활용하면 두 기능 모두 상속받은 휴대전화를 빠르게 만들 수 있다.

하지만 문제 질문이 'mixin의 장점을 말하시오'가 아니고 '반드시 mixin을 써야만 하는 상황을 알고 있는가'길래...뭔가 좀 더 무조건 써야 하는 포인트가 있나 싶어서 더 알아보았다.

mixin의 쓸모를 알아가는 과정에서 한 실험들...

1) 인터페이스 함수를 구현하지 않아도 되나?

인터페이스 클래스를 상속받으면 부모의 메서드를 전부 오버라이드해야 한다.

만약 mixin에서 부모의 메서드를 모두 오버라이드하지 않아도 된다면, 부모 인터페이스에 함수를 왕창 선언해놓고 자식 클래스에서 필요한 것들만 구현해서 쓸모있지 않을까? 라는 구상을 하고 실험해봤다.

결과...실패
인터페이스 함수와 똑같이 모든 메서드를 오버라이딩해야 한다.

2) 두 가지 클래스의 멤버 메서드를 전부 활용

이번에 앱을 만들면서 Firebase와 Algolia 두 가지 데이터베이스를 사용했다.

Algolia는 Firebase에 비해 뛰어난 검색 알고리즘을 제공하지만 용량에 한계가 있다. 그래서 검색 기능이 필요한 부분만 Algolia DB에 연결했고, 단순히 데이터를 받아오기만 할 때는 Firebase에 연결했다.

그 다음 DataSource 를 만드려고 하는데 작명할 때 고민이 생겼다.
SubjectDataSource 라는 인터페이스를 만들어놓고 상속을 받으려고 했는데, algoliafirebase를 쓰니까 AlgoliaFirebaseSubjectDataS.....
이렇게 지으니까 이름이 너무 길어져서 결국 Subject...~...Impl 로 타협했지만 별로 명확하지 않은 이름이라고 생각했다..

그런데 mixin을 공부하고 나니까 이런 아이디어가 떠올랐다.

  1. (mixin) Firebase쪽 인터페이스 만들기
  2. (mixin) Algolia쪽 인터페이스 만들기
  3. SubjectDataSouce 에서 위 두 개를 with로 상속받는다.
  4. Provider를 각자 만들면 두 기능을 구별해서 쓸 수 있겠다

이렇게 구성하면 Subject에는 Algolia에서 상속받은 거 하나, Firebase에서 상속받은 거 하나가 구현되어있으니까, 데이터소스의 다음 단계인 레포지토리에서는 두 기능을 구별해서 쓸 수 있지 않을까...라는 고민을 했다.

까지 하고 나서........
그럼 그냥...A는 A대로 B는 B대로 자식 클래스에서 구현해서 쓰면 되지 굳이 하나로 합쳐서 구현할 필요가 없다는 사실을 문득 깨달았다.

그러니까 A의 기능과 B의 기능이 모두 필요한 자식 클래스 C는 with 키워드를 쓸 수 있다.
하지만 부모 인터페이스를 굳이 반으로 가르기 위해서 with 를 쓸 필요는 없다는 것이다.

3) 스택오버플로우

뭔가 또 알만한 게 있을까 싶어서 스택오버플로우에 mixin의 쓸모를 검색해보았다.

참고 링크 1 :
https://stackoverflow.com/questions/45901297/when-to-use-mixins-and-when-to-use-interfaces-in-dart

믹스인을 언제 활용하느냐에 대한 질문이었는데 답변 내용이 인상적이었다.

믹스인은 클래스에 비해서 제한이 덜하다. 클래스에서는 불가능하고 믹스인에서만 가능한 기능이 있다. (추상클래스 다중상속)
그렇기 때문에 어떤 클래스가 추후에 믹스인으로 쓰일 가능성이 있다면 그 클래스는 반드시 믹스인으로 남겨두어야 한다.

참고 링크 2 :
https://stackoverflow.com/questions/69406570/whats-the-difference-between-using-a-mixin-and-extend-a-component-in-vue

(자바스크립트 질문이지만 비슷한 개념인 것 같아서 가져왔습니다)
수정: 자바스크립트의 mixin은 조금 다른 개념이라고 합니다

질문 : 언제 extend를 쓰고 언제 mixin을 쓰는지
답변 : extend는 클래스 전체를 상속받아 새로 만들고 확장하는 기능이지만 mixin은 작은 기능들을 만들 때 사용한다

이게 무슨 말인지 잘 이해가 안 가서 찾아보다가 도움 되는 글을 발견했다.

참고 문서 3 :
https://medium.com/@chetan.akarte/what-is-mixins-and-why-do-we-need-mixins-in-flutter-6546591f9bb2

출처:https://medium.com/@chetan.akarte/what-is-mixins-and-why-do-we-need-mixins-in-flutter-6546591f9bb2

위의 출처에서 제공한 코드이다.
이 코드를 살펴보면, 먼저 클릭 시 print문을 띄우는 Clickable이라는 mixin이 있다.

그리고 버튼 클래스가 있는데, gesture detector의 onclick 옵션에 믹스인으로 상속받은 함수를 넣어두었다. 이렇게 하면 StatelessWidget을 이미 상속받은 위젯 클래스에서 또 상속받을 수 있다...!

물론 onClick이라는 함수를 클래스 안에 정의해도 된다. 하지만 필요한 함수가 여러 가지라고 생각해보자. 클래스 안에 하나하나 정의하는 것보다 믹스인에 여러 가지를 정의해두고 이 위젯에서도 쓰고 저 위젯에서도 쓰는 쪽이 훨씬 이득이다.

결론 : mixin을 쓰면 Stateless/Stateful/Consumer...등등 위젯이 무언가를 또 상속받을 수 있다.

0개의 댓글

관련 채용 정보