1. Programming Clojure
8 장 . 다중 메서드
아키텍쳐를 꿈꾸는 사람들 (café.naver.com/architect1)
김광중 (visualwizard@gmail.com)
2. 목차
1. 다중 메서드 없이 산다는 것 .
2. 다중 메서드 정의기
3. 다형성을 넘어서
4. 자유로운 메서드 선택
5. 언제 다중 메서드를 사용해야 할까
3. 8. 다중 메서드
Clojure 의 다중 메서드가 무엇인
지 ??
함수가 여러 형태의 인자를 입
력으로 받을 수 있게 하는 것 .
여러 형태의 인자 = 인자의 타입이 다
양 .!
4. 8. 다중 메서드
다른 언어 에도 여러 형태의 인자
를 받는 것이 있다 .
void TestFunc(int i){…}
void TestFunc(char a) {…}
void TestFunc(int i, int j) {…}
TestFunc(1);
TestFunc(10, 20);
TestFunc('b');
C++ 의 함수 오버로딩 (overloading)
5. 8. 다중 메서드
다른 언어 에도 여러 형태의 인자
를 받는 것이 있다 .
void TestFunc(int i){…}
void TestFunc(char a) {…}
void TestFunc(int i, int j) {…}
TestFunc(1);
TestFunc(10, 20);
TestFunc('b');
인자의 타입에 따라 다르게 처
리 .!
6. 8. 다중 메서드
Clojure 는 프로그래머가 지정한 함수
를
다중 메서드의 인자에 적용한 결과
에 따라 실행할 구현을 선택한다 .
9. 8.1 다중 메서드 없이 산 다는 것 .
입력 받은 문자열을 출력하는
함수 제작 .
(defn my-print [ob]
(.write *out* ob)
)
(defn my-println
[ob]
(my-print ob)
(.write *out*
"n")
)
=> 문자열을 출력하자 .
=> 문자열 출력 후 ,
=> 줄바꿈을 하자 .
10. 8.1 다중 메서드 없이 산 다는 것 .
입력 받은 문자열과 nil 을 출력하
는
함수 제작 .
(defn my-print [ob]
(cond
(nil? ob) (.write *out* "nil" )
(string? ob) (.write *out* ob)
)
)
11. 8.1 다중 메서드 없이 산 다는 것 .
인자의 타입이 벡터라도 동작하게
하자 .!
(use '[clojure.contrib.str-utils :only
(str-join)])
(defn my-print-vector [ob]
(.write *out*"[")
(.write *out* (str-join " " ob))
(.write *out* "]"))
12. 8.1 다중 메서드 없이 산 다는 것 .
인자의 타입이 벡터라도 동작하게
하자 .!
(defn my-print [ob]
(cond
(vector? ob) (my-print-vector ob)
(nil? ob) (.write *out* "nil")
(string? ob) (.write *out* ob)
)
)
14. 8.1 다중 메서드 없이 산 다는 것 .
또 다른 타입을 지원하려면 ..!
(defn my-print [ob]
(cond
(vector? ob) (my-print-
vector ob)
(nil? ob) (.write *out*
"nil")
(string? ob) (.write *out*
ob)
(TYPE? Ob) ( my-print-TYPE
ob)
…
…
새 타입을 다룰 보조 함수
my-print-TYPE 을 만든다 .
my-print 안에서 보조 함수를
호출한다 .
15. 8.1 다중 메서드 없이 산다는 것
.
다중 메서드가 없다면… ??
새 타입을 다룰 보조 함수를 만든다 .
my-print 안에서 보조 함수를 호출한다
.
16. 8.2 다중 메서드 정의기
기존의 코드는 수정하지 않고 , 새
로운 TYPE 에 대한 코드만 추가 하
자 .!
다중 메서드를 이용하자 .
( defmulti name dispatch-fn )
17. 8.2 다중 메서드 정의기
( defmulti name dispatch-
fn )
인자에 적용할 함수메서드 이름
18. 8.2 다중 메서드 정의기
( defmulti name
class)
클로저에서 제공하는 class 함수 이용
( class 함수는 자바의 getClass 함수의 래퍼
19. 8.2 다중 메서드 정의기
( defmulti name
TYPE )
class
“foo” nil
20. 8.2 다중 메서드 정의기
( defmulti name
TYPE )
class
“foo” nil
TYPE 에 대한 처리 함수는 정의하지 않았다 .!!!
21. 8.2 다중 메서드 정의기
TYPE 에 대한 처리 함수는 정의하지 않았다 .!!!
TYPE 에 대한 처리 함수를 정의하자 .!!
( defmethod name dispatch-val & fn-tail )
22. 8.2 다중 메서드 정의기
( defmethod name dispatch-val & param )
( defmulti name dispatch-fn )
dispatch-fn 을 수행한 결과 값
30. 다중 메서드 (my-print) 가 , 수행할
메서드를 선택할 때는 자바의 상속 관
계를 고려한다
(defmulti my-print class)
(defmethod my-print Number [n]
(.write *out* (.toString n) )
)
42(Integer)
1.2(Float)
내부적으로 isa? 함수가 사용되어 판단함 .
8.2 다중 메서드 정의기 ( 메서드 선택은 상속을 고
려한다 )
31. 8.2 다중 메서드 정의기 ( 디폴트 다중 메서드 )
( defmethod name dispatch-val & param )
( defmulti name dispatch-fn )
dispatch-fn 을 수행한 결과 값이
dispatch-val 에 해당하는 값이 없을 때 .
33. 8.2 다중 메서드 정의기 ( 디폴트 다중 메서드 )
(defmulti name dispatch-fn :default default-value )
(defmulti my-print class :default
:everything-else)
(defmethod my-print String [s]
(.write *out* s))
(defmethod my-print :everything-else [_]
(.write *out* "Not implemented yet..."))
34. 8.3 다형성을 넘어서
벡터를 더 좋게 표현하기 위해 defmethod 를 추가하다 보니 ..
(use '[clojure.contrib.str-utils :only (str-join)])
(defmethod my-print java.util.Collection [c]
(.write *out* "(")
(.write *out* (str-join " " c))
(.write *out* ")"))
(defmethod my-print clojure.lang.IPersistentVector [c]
(.write *out* "[")
(.write *out* (str-join " " c))
(.write *out* "]")
)
35. 8.3 다형성을 넘어서
(prefer-method multi-name loved-dispatch dissed-dispatch )
충돌이 생기는 경우 특정한 dispatch-fn 의 값이 다른
dispatch-fn 의 값 보다 우선이라고 선언하는 것 .
36. 8.3 다형성을 넘어서
(prefer-method multi-name loved-dispatch dissed-dispatch )
충돌이 생기는 경우 특정한 dispatch-fn 의 값이 다른
dispatch-fn 의 값 보다 우선이라고 선언하는 것 .
(prefer-method my-print
clojure.lang.IPersistentVector
java.util.Collection )
37. 8.4 자유로운 메서드 선택
타입에 따른 메서드의 수행이 아니라 , 임의의
기준에 의한 메서드 수행 .!
입력되는 값에 따라 적절한 메서드가 수행 .
38. 8.4 자유로운 메서드 선택
입력되는 값에 따라 적절한 메서드가 수행 .
(ns examples.multimethods.account)
(defstruct account :id :tag :balance)
(alias 'acc 'examples.multimethods.account)
(def test-savings (struct account 1 ::acc/Savings
100M) )
(def test-checking (struct account 2 ::acc/Checking
250M) )
39. 8.4 자유로운 메서드 선택
입력되는 값에 따라 적절한 메서드가 수행 .
(defmulti interest-rate :tag)
(defmethod interest-rate ::acc/Checking [_] 0M)
(defmethod interest-rate ::acc/Savings [_]
0.05M)
41. 8.5 언제 다중 메서드를 사용해야 할까
많은 클로저 프로젝트들이 다중 메서드를 그리 많이
사용하고 있지는 않다 . 클로저 오픈소스 프로젝트에서 사용된
다중 메서드는 대략 1000 라인에 하나 정도…
대부분은 ‘클래스에 의한 메서드 선택’
42. 8.5 언제 다중 메서드를 사용해야 할까
많은 클로저 프로젝트들이 다중 메서드를 그리 많이
사용하고 있지는 않다 . 클로저 오픈소스 프로젝트에서 사용된
다중 메서드는 대략 1000 라인에 하나 정도…
대부분은 ‘클래스에 의한 메서드 선택’
43. 8.5 언제 다중 메서드를 사용해야 할까
그 외…
inspector 라이브러리 : 시스템 속성에 대한 트리 뷰를 얻음
타입 값 ( 키워드 ) 을 반환하는 함수 (collection-tag) 와
트리 조건을 검사하는 세 가지 다중 메서드로 트리 타입
시스템을 추가함
test-is 라이브러리 : is 매크로를 사용하여 assertion 구현
is 에 넘겨지는 표현식을 내부적으로 assert-expr 이라는
다중 메서드로 처리하여 dispatch-val 값을 반환한다 .
44. 8. 장 요약
다중 메서드는 인자 값으로 , 결과를 반환하는 함수를
사용하겠다고 지정 하는 것 .
다중 메서드는 인자에 반환된 결과값을 처리하는 함수만
추가로 정의하면 된다 .
다중 메서드가 실제 수행을 할 때는 자바의 상속 관계를
알아서 고려한다 .
인자 값을 반환하는 함수의 리턴 값이 매치되는 것이 없을
경우를 처리하기 위해 디폴트 다중 메서드를 지원한다 .
반환 된 인자 값이 다형성을 가지고 있을 경우 , 이에 대한
처리법을 지원한다 .
타입 처리 뿐만 아니라 특정 값에 의한 메서드 실행도
가능하다 .