스칼라의 믹스 인 vs 컴포지션


78

자바 세계에서 (더 정확하게는 다중 상속 / 믹 신이없는 경우) 경험 법칙은 매우 간단합니다. "클래스 상속보다 객체 구성을 선호"합니다.

믹스 인, 특히 스칼라도 고려한다면 어떻게 변경되는지 알고 싶습니다.
믹스 인은 다중 상속 또는 더 많은 클래스 구성의 방법으로 간주됩니까?
"클래스 구성보다 객체 구성 선호"(또는 그 반대) 지침도 있습니까?

나는 사람들이 믹스 인을 사용 (혹은 남용) 할 때 많은 예를 보았는데, 오브젝트 컴포지션도 그 일을 할 수 있고 어느 것이 더 좋은지 항상 확신하지 못합니다. 그들과 매우 비슷한 것을 얻을 수있는 것 같지만 몇 가지 차이점이 있습니다.

  • 가시성-mixin을 사용하면 모든 것이 공용 API의 일부가되지만 구성에서는 그렇지 않습니다.
  • 장황함-대부분의 경우 믹스 인은 덜 장황하고 사용하기가 조금 더 쉽지만 항상 그런 것은 아닙니다 (예 : 복잡한 계층 구조에서 자체 유형을 사용하는 경우).

짧은 대답은 "상황에 따라 다름"이라는 것을 알고 있지만, 이것이 더 나은 경우에는 일반적인 상황이있을 수 있습니다.

지금까지 생각해 낼 수있는 지침의 몇 가지 예 (내가 두 가지 특성 A와 B가 있고 A가 B의 몇 가지 방법을 사용하고 싶다고 가정) :

  • B의 메소드로 A의 API를 확장하려면 mixins, 그렇지 않으면 구성. 그러나 내가 만들고있는 클래스 / 인스턴스가 공용 API의 일부가 아닌 경우에는 도움이되지 않습니다.
  • 믹스 인이 필요한 패턴 (예 : Stackable Trait Pattern ) 을 사용하려는 경우 쉬운 결정입니다.
  • 순환 종속성이있는 경우 자체 유형이있는 믹스 인이 도움이 될 수 있습니다. (나는 이런 상황을 피하려고 노력하지만 항상 쉬운 것은 아닙니다)
  • 구성을 수행하는 방법을 동적 런타임 결정을 원한다면 개체 구성을 수행하십시오.

많은 경우 믹스 인이 더 쉬운 것 같지만 (그리고 / 또는 덜 장황한) "신 클래스"와 같은 몇 가지 함정과 두 개의 아티 마 기사에 설명 된 다른 것들이 있다고 확신합니다 : 파트 1 , 파트 2 (BTW it 대부분의 다른 문제는 스칼라와 관련이 없거나 심각하지 않은 것 같습니다.)

이와 같은 힌트가 더 있습니까?

답변:


41

사람들이 믹스 인과 관련된 많은 문제를 스칼라에서 피할 수 있습니다. 추상 특성을 클래스 정의에 혼합 한 다음 객체 인스턴스화 시간에 해당하는 구체적인 특성을 혼합하면 스칼라에서 피할 수 있습니다. 예를 들어

trait Locking{
   // abstract locking trait, many possible definitions
   protected def lock(body: =>A):A
}

class MyService{
   this:Locking =>
}

//For this time, we'll use a java.util.concurrent lock
val myService:MyService = new MyService with JDK15Locking 

이 구성에는 몇 가지 권장 사항이 있습니다. 첫째, 특성 기능의 다양한 조합이 필요하기 때문에 클래스가 폭발하는 것을 방지합니다. 둘째, 모의 객체와 유사한 "아무것도하지 않는"구체적인 특성을 만들고 혼합 할 수 있기 때문에 쉽게 테스트 할 수 있습니다. 마지막으로, 우리는 우리 서비스 소비자로부터 사용 된 잠금 특성을 완전히 숨겼습니다.

믹스 인의 단점을 대부분 극복 했으므로 믹스 인과 컴포지션 사이의 절충안이 남아 있습니다. 나는 일반적으로 가상의 델리게이트 객체가 포함 된 객체에 의해 완전히 캡슐화되는지 또는 잠재적으로 공유 될 수 있고 자체 수명주기를 가질 수 있는지 여부에 따라 결정을 내립니다. 잠금은 완전히 캡슐화 된 대리자의 좋은 예를 제공합니다. 클래스가 내부 상태에 대한 동시 액세스를 관리하기 위해 잠금 개체를 사용하는 경우 해당 잠금은 포함하는 개체에 의해 전적으로 제어되며 해당 잠금과 해당 작업은 클래스의 공용 인터페이스의 일부로 광고되지 않습니다. 이와 같이 완전히 캡슐화 된 기능을 위해 믹스 인을 사용합니다. 데이터 소스와 같은 공유 항목의 경우 구성을 사용하십시오.


구조의 의미는 무엇입니까 this => Logging? 컴파일되지 않습니다.
HRJ

3
나는 항상 구문을 잊어 버리는 자체 유형 주석입니다. 편집. 이 경우에는이면 MyService를 확장하는 객체가 잠금 확장해야한다는 것을 의미합니다
데이브 그리피스

11

언급하지 않은 다른 차이점 :

  • 특성 클래스는 독립적 인 존재가 없습니다.

( 스칼라 프로그래밍 )

특정 트레이 트가 다른 클래스의 부모로 가장 자주 사용되어 자식 클래스가 부모 트레이 트로 동작하는 것을 발견 한 경우, 대신 클래스로 트레이 트를 정의하여이 논리적 관계를 더 명확하게 만드십시오.
( 예를 들어 [Martin2003] 참조) 전자는 Liskov Substitution Principle을 기반으로 한 상속에 대한보다 정확한 정의이기 때문에 는 a가 아니라으로 동작 한다고 말했습니다 .

[Martin2003] : Robert C. Martin, 애자일 소프트웨어 개발 : Prentice-Hall, 2003 년 원리, 패턴 및 관행

  • mixins ( trait)에는 생성자 매개 변수가 없습니다.

따라서 Programming Scala조언은 다음과 같습니다.

적절한 기본값으로 초기화 할 수없는 트레이 트의 구체적인 필드는 피하십시오.
대신 추상 필드를 사용 하거나 생성자가있는 클래스로 트레이 트를 변환하십시오 .
물론 상태 비 저장 특성은 초기화에 문제가 없습니다.

생성 프로세스가 완료되는 순간부터 인스턴스가 항상 알려진 유효한 상태에 있어야한다는 것이 좋은 객체 지향 설계 의 일반적인 원칙입니다 .

객체 의 초기 상태 에 관한 마지막 부분 은 종종 주어진 개념에 대한 클래스 (및 클래스 구성)와 특성 (및 믹스 인) 사이를 결정하는 데 도움이되었습니다.


답변 해주셔서 감사합니다.하지만 특성 대 클래스에 관한 것입니다. 나는 그 주제에 대해 조금 더 편하게 느낀다. :-) "수업 구성"이라는 용어를 사용했을 때 나는 충분히 정확하지 않았을 것이다. 내가 의미하는 바는 Stackable Trait Pattern 기사에 설명되어 있습니다. "이 패턴은 데코레이터 패턴과 구조가 비슷하지만 객체 구성 대신 클래스 구성을위한 장식이 포함된다는 점만 제외하면"클래스와 특성이 혼합되어 있습니다.
Sandor Murakozi 2010 년

@Sandor : 이해합니다. 나는 아직 그 구별에 완전히 익숙하지 않기 때문에 질문의 기본 부분을 다루고 있었지만 곧 더 정확한 답을 얻을 것입니다.
VonC

" mixins ( trait)에는 생성자 매개 변수가 없습니다. "흥미롭게도 Dotty (Scala 3가되기 위해)는 특성 매개 변수를 지원합니다 . 결과적으로 당신이 언급 한 Programming Scala 의 경험 법칙 이 발전 할 수 있습니다.
jub0bs

1
@Jubobs 감사합니다. 8 년이 지난 후에도이 규칙이 발전 할 수 있다는 사실은 놀랍지 않습니다.
VonC
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.