409
9
트레이트
두가지예외를제외하면구체적클래스를결코상속하지않는다. (
2
)자동화된단위테스트를지원하기위한
테스트용버전을만드는경우
적용 가능한 상황이 아니다.
상속이바람직한접근방법이라생각되는경우,상속대신동작을트레이트로분할해서각각의트레이트를
합할방법이없는지살펴보라.
방금 그렇게 했다!!
논리적상태를부모
-
자식타입경계에걸쳐서나눠담지말라.
버튼이나 다른
UI
위젯의 논리적이고 눈에 보이는 상태는 내부의 상태 변경 방식과 서로
겹치지 않는다. 따라서
UI
상태는 여전히
Button
들어 있으며, 관찰자 패턴관련 있는
상태는
State
트레이트 안에 들어 있다.
9.3
트레이트 쌓기
코드의 재사용성을 향상시키고, 트레이트를 번에 하나 이상 사용하기 (즉, 트레이트를 ‘쌓
기’ ) 위해 있는 추가 세분화가 있다.
먼저 ‘누를 있음
clickablility
GUI
에서 버튼에만 국한된 것이 아니다. 따라서 이런 논리도 분리
해야 할 것이다. 이를
Button
의 부모지만, 아직까지는 비어 있는
Widget
에 넣을 수도 있지만,
모든
GUI
위젯을 누를 있는 것은 아니다. 대신에
Clickable
이라는 트레이트를 도입하자.
//
src
/
main
/
scala
/
progscala2
/
traits
/
ui2
/
Clickable
.
scala
package progscala2
.
traits
.
ui2
//
trait Clickable
{
def click
():
Unit
=
updateUI
()
//
protected def updateUI
():
Unit
//
}
traits
.
ui
있는 타입을 재구현하고 있기 때문에 새로운 패키지를 선언한다.
410
2
기본기 다지기
공개 메서드인
click
구체적 메서드다. 여기서는
updateUI
모든 것을 위임한다.
updateUI
메서드는 보호 메서드며 추상 메서드다. 구현 클래스에서는 이 메서드에 적절한
논리를 제공해야 한다.
단순한 인터페이스는 실제로
Button
예제 수준에서는 필요하지 않다. 여기서
click
자신의
작업을 구체적인 보호된 메서드
updateUI
에 위임한다. 이는 세부적인 구현과 공개적인 추상화
분리하기 위해 자주 사용하는 형태다. 이는
4
인방패턴 책에 있는 템플릿 메서드 패턴
Template
Method
Pattern
간단한 예라고 있다.
다음은 트레이트를 사용해서 리팩토링한 버튼이다.
//
src
/
main
/
scala
/
progscala2
/
traits
/
ui2
/
Button
.
scala
package progscala2
.
traits
.
ui2
import progscala2
.
traits
.
ui
.
Widget
class Button
(
val label
:
String
)
extends Widget with Clickable
{
protected def updateUI
():
Unit
=
{
/*
GUI
모양을
변경하는
논리
*/
}
}
예전에는 관찰을
Button
연결했지만, 이제는
Clickable
연결되어야 한다. 코드를 이런
방식으로 리팩토링하면, 실제로는 버튼을 관찰하려는 것이 아님을 분명히 있다. 우리가
있는 것은 버튼 눌림과 같은 이벤트관찰하는 것이다. 다음은 온전히
Clickable
관찰하
것에만 집중하는 트레이트다.
//
src
/
main
/
scala
/
progscala2
/
traits
/
ui2
/
ObservableClicks
.
scala
package progscala2
.
traits
.
ui2
import progscala2
.
traits
.
observer
.
_
trait ObservableClicks extends Clickable with Subject
[
Clickable
]
{
abstract override def click
():
Unit
=
{
//
super
.
click
()
notifyObservers
(
this
)
}
}
411
9
트레이트
abstract
override
키워드에 유의하라. 이에 대해서는 다음에 설명한다.
구현은 예전의
ObservableButton
예제와 매우 비슷하다. 중요한 차이 하나는
abstract
키워
드다. 예전에는
override
있었다.
메서드를 자세히 들여다보라. 메서드는
super
.
click
( )
호출하지만, 경우
super
엇인가? 여기서는
Clickable
슈퍼타입인 것처럼 보인다. 안에는
click
메서드 선언
어 있지만, 정의는 되어 있지 않다. 그 타입이 아니라면
Subject
수밖에 없는데, 그 안에는
click
메서드가 들어 있지 않다. 따라서
super
현재 시점에 실제 인스턴스와 연결될 없다.
그것이 여기서
abstract
필요한 이유다.
사실
super
트레이트가
click
메서드를 정의해주는
Button
같은 구체적 인스턴스
concrete
instance
안에 혼합될 연결된다.
abstract
키워드는 컴파일러에 (그리고 독자에게), 심지어
ObservableClicks
.
click
메서드의 본문이 있을지라도,
click
이 아직은 완전히 구현되지
았음을 알려준다.
NOTE
_
어떤 트레이트 안에 있는 메서드의 본문에서 부모 트레이트에 있는 다른 메서드를 호출하는데, 그
(부모 트레이트의) 메서드가 구체적인 구현은 되어 있지 않은 경우, 반드시 (부모 트레이트의 메서드를 호출하
본문이 있는) 메서드 앞에
abstract
붙여야 한다.
트레이트를
Button
안의 구체적
click
메서드에 대해 사용해보자.
//
src
/
main
/
scala
/
progscala2
/
traits
/
ui2
/
click
-
count
-
observer
.
sc
import progscala2
.
traits
.
ui2
.
_
import progscala2
.
traits
.
observer
.
_
//
Button
내에서
click
오버라이딩할
필요는
없다
.
val button
=
new Button
("
Click Me
!")
with ObservableClicks
class ClickCountObserver extends Observer
[
Clickable
]
{
var count
=
0
def receiveUpdate
(
state
:
Clickable
):
Unit
=
count
+
=
1
}
val bco1
=
new ClickCountObserver
val bco2
=
new ClickCountObserver
412
2
기본기 다지기
button addObserver bco1
button addObserver bco2
(
1 to 5
)
foreach
(
_
=
>
button
.
click
())
assert
(
bco1
.
count
==
5
,
s
"
bco1
.
count
${
bco1
.
count
}
!
=
5
")
assert
(
bco2
.
count
==
5
,
s
"
bco2
.
count
${
bco2
.
count
}
!
=
5
")
println
("
Success
!")
이제
Button
인스턴스를 선언하면서
click
메서드를 재구현하지 않으면서
ObservableClicks
혼합할 있음에 유의하라. 그뿐 아니라 우리가 마우스 버튼 눌림을 관찰하고 싶지 않은
우라도,
Clickable
혼합하면 눌림을 지원하는
GUI
객체를 만들 있다.
이런 식으로 믹스인 트레이트를 활용해서 미세하게 기능을 합성하는 것은 강력하지만, 종종
사용이 지나칠 있다.
트레이트가너무많으면컴파일시간이오래걸린다.컴파일러가출력바이트코드를합성하기위해더많은
작업을해야하기때문이다.
스칼라독을볼때트레이트의목록이너무길면라이브러리사용자가지레겁먹을수있다.
번째 트레이트를 추가하면서 예제를 끝내자. 자바빈즈 명세(
http
://
bit
.
ly
/
1E8xIGM
)에는
‘거부할 있는
vetoable
이벤트에 대한 개념이 들어 있다. 그런 이벤트의 경우, 리스너
listener
바빈의 상태를 변경하는 것을 거부할 있다. 그와 비슷한 것을 미리 지정한 횟수 이상 마우스
버튼이 눌리면 이벤트를 무시하는 트레이트를 만들어서 구현해보자. 금융 거래 등을 일으키는
버튼을 실수로 이상 누르지 못하게 막는 것을 구현하는 상상을 있을 것이다. 다음은
VetoableClick
트레이트다.
//
src
/
main
/
scala
/
progscala2
/
traits
/
ui2
/
VetoableClicks
.
scala
package progscala2
.
traits
.
ui2
import progscala2
.
traits
.
observer
.
_
trait VetoableClicks extends Clickable
{
//
//
허용하는
최대
눌림
횟수의
기본값
val maxAllowed
=
1
//
private var count
=
0
abstract override def click
()
=
{

Get 프로그래밍 스칼라: 실용적인 스칼라 활용법을 익히는 가장 확실한 실전 바이블 (2.11.x 버전 기반) now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.