402
2
부
기본기 다지기
9.1
자바
8
의 인터페이스
자바
8
은 이런 양상을 바꿨다. 이제 인터페이스에 메서드를 정의해 넣을 수 있다. 이를 기본 메
서드
default
method
또는 방어적 메서드
defender
method
라고 부른다. 여전히 클래스에서 자신만의 구
현을 제공할 수 있지만, 그런 구현이 없는 경우에는 기본 메서드가 사용된다. 따라서 자바
8
의
인터페이스는 이제 스칼라의 트레이트에 더 가깝게 작동한다.
하지만 한 가지 차이는 자바
8
인터페이스가 여전히 정적 필드만 정의할 수 있는 반면, 스칼라
트레이트는 인스턴스 수준의 필드를 정의할 수 있다는 것이다. 이는 자바
8
의 인터페이스가 인
스턴스 상태의 일부를 관리할 수 없다는 의미다. 인터페이스를 구현하는 클래스가 상태를 저장
하기 위한 필드를 제공해야 한다. 이는 또한 기본 메서드의 구현에서 아무런 상태 정보도 읽을
수 없다는 의미다. 그에 따라 기본 메서드가 할 수 있는 일이 제한된다.
다음 논의에서 나오는 트레이트나 클래스를 자바
8
의 인터페이스나 클래스를 사용하면 어떻게
구현할 수 있는지 생각해보라. 자바
8
로 쉽게 이식 가능한 부분은 무엇이며, 그렇지 않은 부분은
무엇인지 판단해보라.
9.2
믹스인으로서의 트레이트
스칼라 코드를 모듈화하는 데 있어 트레이트가 어떤 역할을 하는지 살펴보기 위해
GUI
툴킷에
서 버튼을 사용하는 다음 코드로 시작해보자. 버튼은 콜백을 사용해서 클릭이 발생했음을 통지
한다.
//
src
/
main
/
scala
/
progscala2
/
traits
/
ui
/
ButtonCallbacks
.
scala
package progscala2
.
traits
.
ui
class ButtonWithCallbacks
(
val label
:
String
,
val callbacks
:
List
[()
=
>
Unit
]
=
Nil
)
extends Widget
{
def click
():
Unit
=
{
updateUI
()
callbacks
.
foreach
(
f
=
>
f
())
}
403
9
장
트레이트
protected def updateUI
():
Unit
=
{
/*
GUI
모양을
변경하는
논리
*/
}
}
object ButtonWithCallbacks
{
def apply
(
label
:
String
,
callback
:
()
=
>
Unit
)
=
new ButtonWithCallbacks
(
label
,
List
(
callback
))
def apply
(
label
:
String
)
=
new ButtonWithCallbacks
(
label
,
Nil
)
}
Widget
은 나중에 설명할 ‘표지’ 트레이트다.
//
src
/
main
/
scala
/
progscala2
/
traits
/
ui
/
Widget
.
scala
package progscala2
.
traits
.
ui
abstract class Widget
버튼이 눌리면
( ) =
>
Unit
타입 (완전히 부수 효과만을 위한 것 )의 콜백 함수 목록이 호출된다.
이 클래스에는 두 가지 책임이 있다. 시각적인 모양을 갱신하는 것 (이 부분은 생략했다 )과 콜백
동작을 처리하는 것이다. 콜백 동작을 처리하는 것에는 콜백의 목록을 관리하는 것과 버튼이 눌
렸을 때 콜백을 호출하는 것이 포함된다.
우리가 만드는 타입 안에서 관심사의 분리
separation
of
concerns
를 위해 노력해야 한다. 이는 모든 타
입이 오직 ‘한 가지 일만 담당’하고, 여러 책임을 한꺼번에 담당해서는 안 된다는 단일 책임 원칙
Single
Responsibility
Principle
으로 달성할 수 있다.
버튼에 대한 논리와 콜백을 처리하는 논리를 분리했으면 한다. 그러면 두 부분 모두 좀 더 단순
해지고, 더 모듈화할 수 있고, 테스트나 변경이 쉬워지며, 재사용하기 더 쉬워질 것이다. 콜백 논
리는 믹스인을 만들기 좋은 후보다.
트레이트를 사용해서 콜백 처리와 버튼 논리를 분리하자. 우리의 접근 방식을 약간 더 일반화할
것이다. 콜백은 실제로는 관찰자 디자인 패턴
Observer
Design
Pattern
의 한 가지다. 따라서 이 패턴의
주체와 관찰자를 나타내는
Subject
와
Observer
를 선언하고 일부 구현하자. 그 후 이들을 사용
404
2
부
기본기 다지기
해서 콜백의 동작을 처리할 것이다. 단순화하기 위해 처음에는 버튼이 눌린 횟수를 세는 간단한
콜백을 하나만 사용할 것이다.
//
src
/
main
/
scala
/
progscala2
/
traits
/
observer
/
Observer
.
scala
package progscala2
.
traits
.
observer
trait Observer
[
-
State
]
{
//
➊
def receiveUpdate
(
state
:
State
):
Unit
}
trait Subject
[
State
]
{
//
➋
private var observers
:
List
[
Observer
[
State
]]
=
Nil
//
➌
def addObserver
(
observer
:
Observer
[
State
]):
Unit
=
//
➍
observers
::
=
observer
//
➎
def notifyObservers
(
state
:
State
):
Unit
=
//
➏
observers foreach
(
_
.
receiveUpdate
(
state
))
}
➊ 상태 변경을 전달받고 싶은 고객을 위한 트레이트다. 고객들은
receiveUpdate
메서드를 구
현해야 한다.
➋ 통지를 관찰자에게 보낼 주체를 위한 트레이트다.
➌ 통지를 받을 관찰자의 목록이다. 이 목록은 변경 가능하지만, 스레드 안전하지는 않다!
➍ 관찰자를 추가하기 위한 메서드다.
➎ 이 식의 의미는 ‘
observer
를
observers
앞에 넣고, 그 결과를
observers
에 대입하라’는 것
이다.
➏ 상태 변경을 관찰자에게 통지하는 메서드다.
Subject
를 혼합한 클래스의 타입을
State
타입 매개변수로 사용하는 것이 가장 편한 선택이
되곤 한다. 그런 경우
notifyObservers
메서드가 호출되면 해당 인스턴스는 그냥 자기 자신
(
this
)을 넘기면 된다.
405
9
장
트레이트
Observer
는 모든 메서드를 선언만 하지 정의하지는 않으며, 메서드가 아닌 멤버도 선언하지
않는다. 따라서 이는 자바
8
이전의 인터페이스와 바이트 코드 수준까지 완전히 동일하다. 메
서드 본문이 없기 때문에 추상 메서드라는 점이 너무 분명해서,
abstract
키워드를 트레이
트 앞에 넣을 필요가 없다. 하지만 메서드 본문이 구현되지 않은 추상 클래스에서는 반드시
abstract
키워드를 사용해야 한다. 예를 들면 다음과 같다.
trait PureAbstractTrait
{
def abstractMember
(
str
:
String
):
Int
}
abstract class AbstractClass
{
def concreteMember
(
str
:
String
):
Int
=
str
.
length
def abstractMember
(
str
:
String
):
Int
}
NOTE
_
추상 멤버가 있는 트레이트를
trait
앞에
abstract
키워드를 추가해서 추상 트레이트로 선언할 필
요는 없다. 하지만 하나 이상의 멤버가 정의되지 않은 추상 클래스에서는 반드시
abstract
를 사용해야 한다.
다섯 번째 항목의 식을 확장하면
observers
가 변경 가능하기 때문에, 다음 두 식은 동일하다.
observers
::
=
observer
observers
=
observer
::
observers
이제 단순화한
Button
클래스를 정의하자.
//
src
/
main
/
scala
/
progscala2
/
traits
/
ui
/
Button
.
scala
package progscala2
.
traits
.
ui
class Button
(
val label
:
String
)
extends Widget
{
def click
():
Unit
=
updateUI
()
def updateUI
():
Unit
=
{
/*
GUI
모양을
변경하는
논리
*/
}
}
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.