여섯번째는 인터페이스에 대한 이야기이다. 

나는 오래전부터 추상클래스와 인터페이스의 문법적인 차이 이상의 문제에 대해 궁금해 왔다.

기본적으로 인터페이스는 다중 상속이 가능하고 추상클래스는 속성을 가질수 있다는 차이점 말고도 무언가 더 많은 차이가 있을것만 같았다. 

앞서 말한바와 같이 일단 속성을 가진다는 특성때문에 명사형 객체는 추상클래스가, 동사나 형용사는 인터페이스로 만들 확률이 높다. 물론 Thread와 Runnable처럼 두가지 형태를 모두 가질수도 있다. 보통 이정도 기준으로도 70%정도는 맞을 수 있다. 

다만 명사형임에도 인터페이스를 할 때가 있는데 그건 레이어의 바깥에 노출되는 객체 즉 구현에 있어서 매우 많은 유연성을 필요로 하기 때문에 - 여기서 더 유연이라는 의미는 인터페이스의 다중상속에 의지하는 바가 크다. - 명사형임에도 인터페이스를 고려해야 할 때가 있다. 

여기서 상속의 의미를 구분해야 할 것다. 인터페이스의 implements와 추상 클래스의 extends 모두 보통 상속이라고 부르고. 보통 부모 자식 관계라고 하는데 이 말은 명사형 추상클래스에 적합하고 인터페이스는 미묘하게 다른 의미를 갖는다. (영어 서적을 읽으면서 느껴지는 이 미묘한 뉘앙스의 차이가 번역을 거치면 일괄적인 부모-자식으로 변역되어 버린다.)

concrete 클래스에 있어서 인터페이스는 부모라기 보다는 일종의 이름이다. 흔한 수수께끼처럼 자기꺼지만 자기가 사용하지 않는걸 이름이라고 하는데 인터페이스는 이 수수께끼처럼 자기것이지만 자기가 사용하지 않는 것이고 우리가 사용하는 부모라는 의미로 보기 힘들다. (이게 영어와 한국어의 뉘앙스 차이일수도 있다.)

인터페이스와의 관계를 부모-자식 관계로  보지않고 이름 혹은 별명으로 보면 인터페이스의 의미에 대해 더 이해하기 쉬워진다. 인터페이스라는 의미 그 자체로 보면 접점-통로의 의미이므로 이름처럼 자기것이지만 불려지는 것이 되고 따라서 존재적 의미보다는 보여지는 의미가 더 중요해진다. (별로 상관없는 이야기지만 김춘수의 꽃이라는 시를 보면 존재적 의미보다 선언적 의미가 더 앞선다.)

추상클래스는 앞서 말한대로 명사형에 치우쳐 있고 속성을 가지기 때문에 상속이라는 용어와 부모자식관계라는 번역에 위화감은 없지만 인터페이스와 구현체는 좀 달리 이해할필요가 있다. 하지만 이러한 관계에 새로운 용어를 여기서 다시 만든다는 것은 더 많은 혼란을 야기할수 있기 때문에 그냥 외부에 불려지는 이름정도라고 넘어가자. 

따라서 인터페이스는 추상클래스보다 외부에 보여지는 모습이 중요하다. 아니 그게 전부라고 해도 좋을 정도이다. 이제 여섯번째 계를 말할때가 왔다. 잘 알려진 구현은 숨기고 대신 의도는 드러내라가 여섯번째 계이다. 본질과 작동원리에 대한 이해보다 인터페이스는 표층이 더 중요하고  특히나 다른 객체에게 더 중요하다. 자신의 것이지만 남에게 불리는 이름처럼 인터페이스는 객체가 다른 객체에게 불리어지는 이름이기 때문에 자신의 구현과는 상관이 없다. 다만 구현 객체들은 이런 이름으로 불리어야 한다는 걸 제시할 뿐이다. 

6계는 이미 다른 객체지향 책에서 많이 언급하고 있으므로 좀더 자세한 예제가 필요하다. 



'Framework > 아키텍쳐 일반' 카테고리의 다른 글

일곱번째 계 - 호출의 빈도(1)  (0) 2009.07.24
Second Program Effect  (0) 2009.07.17
호출의 빈도  (0) 2009.07.08
한줄짜리 함수  (0) 2009.07.06
예언?  (0) 2009.06.12
Posted by bleujin

이전글대로 변화의 속도에 다르기 때문에 소스포지나 코드서치를 통해서 얻은 Lib를 직접 사용하는 것보다 중개자 클래스(이것이 좀더 확장되면 새로운 Layer가 된다.)를 도입하는 것은 현명하다. 그런데 변화의 속도만으로 판단한다면 아마도 모든 Java Core Lib에 대해 중개자 클래스를 도입해야 할지도 모른다. 

그래서 변화의 속도 못지않게 중요한게 한가지 더 있다. 언젠가 다시 나올 7계인 호출의 빈도를 말하는 - 객체는 객체자체로서의 아닌 다른 객체에서 어떻게 그리고 얼마나 자주 보이는가가 더 중요하다.이다. 이전에서 말한바와 같이 양적인 차이는 질적인 차이를 유발한다. 만약에 단지 몇번 호출하고 말것이라면 굳이 번거로운 길을 갈 필요는 없다. 그러나 아주 많다면 그것이 new ArrayList() 라도 중개자 클래스(혹은 메소드)를 만들어서 하는게 도움이 된다. 


인간은 분류하는 동물

호출 빈도의 증가는 단순히 양적인 증가 이상의 의미가 있다. 서랍안에 물건이 단 6개만 있다면 책장에 책이 7권정도라면 그럭저럭 괜찮다. 하지만 하나의 서랍안에 물건이 37개라거나 한칸에 책이 76권이라면 서랍과 책장이 어질러져 있다고 느낀다. 그래서 Brian Buchanan은 Theory of library classification = 문헌 분류 이론의 한국어판 서문에서 “아마도 패턴과 순서에 대한 관심은 우리 인간들이 타고 나는 모양이다. 이러한 의미에서 인간을 분류하는 동물 (classifying animal) 이라고 정의할 수 있을 것이다. 이것은 카오스(혼돈) 에 대한 두려움을 바탕으로 하고있고, 또한 통제할 수 없는 환경을 통제함으로서 불안정한 세계에서 안전하다는 망상을 갖고자 하는 욕망에서 생겨나는 듯 하다.”라고 그의 분류관을 개관해 주고 있다. 


양이 많아지면 우리는 분리하고 싶어한다. 사실 System.out.println의 중개 클래스를 만드는 게 좋은 이유는 첫째의 변화의 속도 보다 우리가 그 메소드를 너무나 자주 쓰기 때문에 의존하는 바가 더 크다. 인터페이스를 만드는 이유를 생각해봐도 이는 당연한 말이다. 주로 인터페이스가 되는 객체는 인터페이스라는 말 그대로 Front Object 즉 바깥에서 가장 많이 노출되는 객체일 경우가 많고 핵심 알고리즘이 담겨서 있어서나 단지 양이 길어서가 아닌 가장 많이 불리는 객체에 대해 객체이름과 인터페이스 설계를 가장 고민해야 한다. 

사실 객체는 어떻게 보면 보여지는 모습 (인터페이스)이 바로 그것의 본질이다. 현상을 이해하는데 본질과 작동원리 (구현)에 대한 이해보다 표면의 관찰, 즉 통신수단으로서의 인터페이스가 중요하다.

'Framework > 아키텍쳐 일반' 카테고리의 다른 글

Second Program Effect  (0) 2009.07.17
여섯번째 계  (0) 2009.07.14
한줄짜리 함수  (0) 2009.07.06
예언?  (0) 2009.06.12
패키지의 설계 원칙  (0) 2009.06.08
Posted by bleujin
함수의 적정 최대 라인수가 얼마냐 하는 것은 아직까지 논의가 되고 있는 문제인데 보통의 경우 모니터의 화면을 넘기지 않을 정도라는 대체적인 합의는 있다. (대략 20라인 이내) 

Max는 그렇다 치고 Minimum은 어떨까? 
한줄짜리 함수는 가치가 있을까? 이전의 강의에서 나는 종종 그건 지나친것 같다라는 질문을 받곤 했다. 



한줄짜리 함수의 첫번째 유용성은 리팩토링 책에서 소개하듯이 가독성때문이다. 

1. if (date.before(SUMMER_START) || date.after(SUMMER_END))를

2. if (notSummer(date)) 와 같이 바꾼다면 (Decompose Conditional)

Reading하기가 좀더 쉽다. 아마도 notSummer는 private 함수일것이고 함수명으로 좀더 의미를 명확하게 전달하기 때문에 굳이 함수를 들여다봐야 해서 복잡해지진 않는다.(이런 가독성에 대해서는 이전글에서 언급했듯이 동전의 양면이다. 만약 함수명이 적절하게 지어지지 않는다면 좀더 복잡해질 것이다. Framework에서 구조의 문제도 마찬가지이다. )

그럼에도 한줄짜리 함수를 싫어하는 사람들은 꽤 있었지만 현재는 그 수가 많이 줄었다. 이는 툴의 발전에도 많이 영향을 받았다. 이전에 text editor에서 코드를 작성하던 쥬라기 시대에는 툴에 함수 바로가기 등의 assist 기능이 없어서 text search로 함수 내용을 확인해야 하던때라 가능한 하나의 함수에서 해주는게 미덕인 시절이 있었다. 

한줄 함수를 싫어하는 다른 이유로는 C 시절에는 Function Call Stack이 쌓이게 됨으로써 발생하게 되는 비효율을 이유로 들었으나 자바에서는 C와 달리 이러한 비효율이 성능상에서 차지하는 부분이 아주 작다. 이미 자바 라이브러리 자체가 수십개의 Function Stack Call을 하고 있으므로 거기에 몇개쯤 더한다고 거의 영향이 없다. (그러나 C나 C++은 영향이 있다. 그 영향 자체는 상대적이기에 많다 적다고 할수 없지만 확실히 자바보다는 영향이 많다.)




잘 알려지지 않는 한줄 함수의 두번째 유용성은 일종의 중개자로서의 역할인데
예컨데 System.out.println()을 예로 들어보자. 

최근에는 Unit시리즈의 유닛테스트를 많이 사용하고 있긴 하지만 그럼에도 유닛테스트하기 어려운 Swing이나 Persitence가 있고 또 가끔은 System.out.print으로 Debugging을 할때가 있다. 문제는 코드 중간중간에 써 놓은 System.out.println은 확인이 끝나고 처치가 곤란할때가 종종 있다. 

그렇다고 Logger를 쓰자니 잠깐 확인을 위해서 jar도 path에 걸어야 하고 설정도 해야하니 웬지 배꼽이 더 커 보인다. 

그럴때 Debug.debug() 클래스를 선언해서 쓰면 유용하다. 

예컨데 
public class Debug extends OutputStream{

private static void out(String message) {
System.out.println("   : " + message) ;
}

public static void error(Class cls, Object...objs) {
debug(cls, objs) ;
}

public static void debug(Object... objs) {
out(Arrays.deepToString(objs)) ;
}
public static void debug(Class cls, Object... objs) {
debug(Arrays.deepToString(objs) + " at " + cls.getName()) ;
}

public void write(int b) throws IOException {
System.out.print(b) ;
}
public static void happy(Object... objs) {
// TODO Auto-generated method stub
}
public static void happy(Class cls, Object... objs) {
// TODO Auto-generated method stub
}

}}
와 같이 별거 없는 클래스이다.(함수도 하나였는데 자꾸 쓰다보니 늘어났다.)

System.out.println() 대신 Debug.debug() 한줄함수로 바꾸면 생기는 이득은 멀까?

첫번째로 Reading이 쉬워지는 것외에 툴의 Call Hierarchy 기능을 통해 사용되고 있는 곳을 알수 있다. (System.out.println의 Call Hierachy를 찾으면 지나치게 많은 것을 찾는다.) 또는 잠깐의 조작을 통해 특정 메시지만 다른 outted message와 다르게 보이게 만들수도 있다. (System.out.println 함수를 바꿀수는 없지만 debug함수는 수정이 가능하다.)

둘째로 언제든 Logger 클래스를 사용하는 걸로 변신할수 있다. 이를테면 error() Method만 Logger 클래스를 사용하도록 이후에 수정에 가능하다. 시점이 나중이라는 것이 중요하다. 처음에는 그냥 쉽게 화면에 print 하는 방식으로 사용하다가 필요가 생기면 해당 함수를 수정하면 그만이다. 

한줄짜리 함수에 이런 장점이 생기는 근본 이유를 들여다 보면 좀더 흥미롭다. 왜 Debug.debug() 함수가 유용성을 가지는가? 그것은 우리가 System.out.print를 수정할 수 없기 때문이다. 만약 우리가 System.out.print를 컨트롤 할 수 있었다면 debug함수를 작성해서 수정할 필요없이 out class의 println 함수를 직접 수정하면 그만이다. 

앞서(4달전에:) 변화의 속도에 대해서 말한적이 있는데 현재 작성하고 있는 코드와 자바의 java.lang 패키지는 변화의 속도가 다르다. 그리고 그 변화의 속도가 다를때 부동산 중개인의 존재이유와 마찬가지로 우리는 중개자 클래스 혹은 함수가 필요하다. 다시 말해서 단 한줄짜리 함수라고 하더라도 해당 함수의 변화의 속도가 다르다면 그 함수는 유용성을 가질때가 많다. 

사실 대부분의 프레임워크와 마찬가지로 마치 빅뱅이론처럼 Logger Framework는 이러한 한줄짜리 함수에서 출발했다. 누군가 System.out.println으로 화면에 메시지를 보여주는게 관리상 효율이 좋지 못하다는 걸 깨달았고 한줄짜리 함수를 만들었는데 거기에 기능을 덧붙이고 유연성을 더하고 예외도 고려하고 등등을 하다보니 이게 Logger Framework가 되었다. 

그러나 그렇다고 해서 우리는 처음부터 System.out.println과, 보다 설정하기도 귀찮고 사용하기 어려운 Logger Framework 두가지중에서 꼭 하나를 결정해야 할 필요는 없다. 그런 선택은 미루다가 나중에 고를수 있거나 혹은 제 3의 길의 선택할 수 있는 방향을 열어두는게 좋다.

즉 한줄짜리 함수는 변화의 속도가 다른 Layer사이에서 그 속도의 조정과 완충을 담당하는 중재자 역할을 할때 새로운 효용성을 발견할 수 있다. 자신이 컨트롤 할수 없는 함수(System.out, Logger 등등)을 5번 이상 호출해야 한다면 이런 한줄짜리 중개자 함수를 고려해 보자. 



'Framework > 아키텍쳐 일반' 카테고리의 다른 글

여섯번째 계  (0) 2009.07.14
호출의 빈도  (0) 2009.07.08
예언?  (0) 2009.06.12
패키지의 설계 원칙  (0) 2009.06.08
양화 구축  (2) 2009.06.08
Posted by bleujin