본문 바로가기

ruby

상속 vs 믹스인(mixin)

루비만의 모듈 사용 방법이다. 이 방법을 이용하면 다중 상속 문제나 굳이 상속을 할 필요가 사라진다.

루비는 단일 상속을 지원하는 언어이고 믹스인을 통해 다중 상속을 간접적으로 지원한다.

 

1. 그렇다면 상속이 아닌 왜 믹스인을 써야 할까?

정답은 상황에 따라 다르다.

상속을 사용해야 하는 상황은 리스코프의 치환 원칙으로 설명할 수 있다.

이 원칙은 "타입 T 객체 x에 관해 참이 되는 속성을 q(x)라고 한다면, S가 T의 자식이라면 타입 S의 객체 y에 대해 q(y)도 참이 된다."로 설명할 수 있다.

즉, 부모 클래스의 객체는 모두 자식 클래스의 객체로 바꿔서 사용할 수 있고 자식 클래스는 한 종류의 부모 클래스라고 말할 수 있어야 한다는 의미다.(is-a)

ex) 자동차는 운송 수단이다.

즉, is-a관계에 있다면 상속을 사용하는 것을 권장한다.

그런데 여기서 문제가 상속을 사용하면 강한 결합을 만들어 낸다.

한 예로, 자식 클래스에서 부모 클래스에 정의된 메서드를 사용하고 있다면, 부모 클래스의 수정으로 자식 클래스에 문제가 생길 여지가 존재한다.

그래서 is-a관계에 있지 않다면 믹스인을 사용하는 것을 권장한다.

 

2. 사용 방법

class Array
  include Enumerable
  
  ...
   ..
end

module Enumerable
  def each_slice(n)
    block_given? ? (yield to_a; nil) : [to_a].to_enum
  end
end

실제 루비 Array 클래스가 Enumerable 모듈을 믹스인하고 있는 코드이다. 

여기에서 block_given? 메서드는 ruby kernel 모듈이 제공하는 메서드다.

yield는 블록 문을 실행시키는 예약어다.

to_a는 리스트이고 each_slice에서 n크기만큼 자른 sublist를 반환한다.

블록에 sublist를 모두 yield문으로 실행시킨 후 nil을 최종적으로 반환한다.

만약에 블록이 없다면 enumerator 타입으로 서브 리스트를 가진 리스트를 반환한다.

 

여기서 (yield to_a; nil)을 이해하려면 삼항 연산자 특징을 알아야 한다. 조건식이 true일 때, 맨 마지막 값을 리턴해준다.

즉, block_given? ? (yield to_a; nil; 2; 5) : .. 라고 한다면, yield문이 끝난 후 5를 마지막에 리턴해준다.

list = [1,2,3]
list.each_slice(2) { |sub| p sub }

 

 

'ruby' 카테고리의 다른 글

DI(Dependency Injection)  (0) 2021.12.30
instance variable encapsulation  (0) 2021.12.30
블록  (0) 2021.12.26
루비에서 람다식이란  (0) 2021.12.26
ruby 특징  (0) 2021.12.26