'Framework/아키텍쳐 일반'에 해당되는 글 23건

  1. 2009.07.24 일곱번째 계 - 호출의 빈도(1)
  2. 2009.07.17 Second Program Effect
  3. 2009.07.14 여섯번째 계
  4. 2009.07.08 호출의 빈도
  5. 2009.07.06 한줄짜리 함수
  6. 2009.06.12 예언?
  7. 2009.06.08 패키지의 설계 원칙
  8. 2009.06.08 양화 구축 2
  9. 2009.06.08 AOP
  10. 2009.06.01 Self-Signed Java Applets


대부분의 책에서 인터페이스와 추상 클래스에 기반한 프로그래밍이란 얘기가 너무 많이 나와서 흔히 인터페이스를 사용하는게 목적처럼 되어 버리는 경우가 있다. 

인터페이스를 사용하는 것은 수단일뿐 그 자체로 목적이 아니다. 그럼 인터페이스를 사용하는 목적은 무엇일까에 대해 의문을 가져야 한다. 그 답을 알기전에 먼저 인터페이스는 절대선이 아니라는 것을 이해해야 한다. 오히려 인터페이스는 프로그램의 문서화처럼 존재 자체의 손해 보다 더 많은 이득을 얻기 위한 필요악같은 존재이다. 

아이러니하게도 인터페이스는 복잡함을 감소시키기 위한 추가적인 복잡함의 장치이다. 인터페이스를 사용한다는 것은 관리해야 할 파일 하나가 늘어다는 것이므로 그 자체로 복잡함이 증가한다. 다만 인터페이스를 사용함으로서 구현자는 "인터페이스의 책임"의 구현에만 집중할 수 있고 호출자는 별로 관심없는 상세 구현보다 책임 그 자체에 집중할 수 있게 함으로서 "구현자과 호출자 사이의 복잡함"을 감소시킨다. 

여기서 ~사이의 복잡함이란 시간과 공간이라는 차원을 포함하고 있다. 애초에 구현자와 호출자가 같은 시간과 공간에 존재한다면 얻는 복잡함의 감소보다 잃는 복잡함의 증가가 더 크다. 


일단 "공간"에 대해서 말하자면 서울과 LA의 거리라는 물리적 감각보다는 나와 나 이외의 생각의 거리라는 의미가 강하다. 만약 혼자 하는 프로젝트라면 혹은 아주 마음이 잘 맞는 팀이라면 공간 사이의 거리가 크지 않을 수도 있다. 

그러나 "시간"의 거리는 시간이 갈수록 엔트로피처럼 항상 늘어나고 혼자하는 프로젝트라도 시간의 거리는 생긴다. 그래서 나는 인터페이스의 가장 큰 목적은 이 시간의 거리 복잡함을 줄이는 것이라고 생각한다. 다시 말해서 복잡함이란 현재의 특정 상황 혹은 시점에서의 복잡함만을 뜻하는 것이 아니라 지속적으로 변함으로써 생기는 복잡함이 더욱 많다는 말이다. 



시간의 거리로 생기는 복잡함이 보다 복잡한 이유는 이렇게 생긴 복잡함은 지속적인 파동의 다른 변화를 일으키고 이로인해 더욱더 복잡해지기 때문이다.. 게다가 앞날이라는 것은 본질적으로 예측하기 어렵기 때문에 미래를 대비하는 것 작체가 불필요한 복잡합이 야기될수 있다. 


특정 객체가 변해야 할 이유는 그 자체가 아니라 그가 사용하는 객체가 변해야 할때도 일어나고 이때문에 객체는 그 자신보다 보다 덜 변하는 것에 의존하여야 한다. (보다 덜 이라는 의미는 <=(작거나 같다) 의미인데 당연히 < (작다) 로 할수있으면 하는게 더 낫다. ) 

쉬워보이지만 이 원칙은 의외로 지키기 어렵다. 

이에 관해서 첫번째 알려진 것은 벽돌형 layer 아키텍쳐에서 상위 레이어는 하위 레이어에 의존해야 하고 하위 레이어는 상위 레이어에 의존해서는 안된다 라는 것인데 종종 이를 위반하는 것은 상위 레이어와 하위 레이어를 같은 시간에 혹은 같은 사람이 만들기 때문인데 주의하지 않으면 처음 생각보다 쉽지 않다. 

보통의 프로그램에서 하나의 새로운 레이어를 만드는 일은 그리 많지 않지만 하나의 레이어에서도 "보다 덜 변하는 것에 의존해야 한다는 원칙"은 중요한 것은 이것이 단순히 레이어간의 이야기만이 아니기 때문이다.


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

Second Program Effect  (0) 2009.07.17
여섯번째 계  (0) 2009.07.14
호출의 빈도  (0) 2009.07.08
한줄짜리 함수  (0) 2009.07.06
예언?  (0) 2009.06.12
Posted by bleujin

" 설계자의 첫번째 작업은 절제되고 깔끔한 편이 많다. 그는 자신이 하는 일을 아직 잘 모르는 상태라는 것을 인식한다. 그래서 매우 조심스럽고 절제하는 태도를 가진다. 

그가 첫번째 작업을 설계하는 과정에서 다양한 꾸밈과 장식이 떠오르지만 이것들은 "다음번"에 사용할 것으로 일단 옆으로 제쳐놓는다. 머지않아 첫번째 시스템은 끝이 나고 일정한 수준의 시스템을 달성했다는 것을 입증한 설계자는 자신감이 충만한 상태로 두번째 시스템을 구축할 준비에 나서게 된다. 

이 두번째 시스템이 누구에게나 가장 위험한 시스템이다. ~ 두번째 시스템에서는 첫번째 시스템에서 옆으로 제쳐두었던 모든 아이디어와 치장들을 사용하여 과도하게 설계하는 경향이 나타나게 된다.  "


브룩스는 두번째 시스템 효과에 대해 말하면서 성공한 제품의 두번째 제품은 과도한 설계로 과도하게 복잡한 시스템이 될 가능성이 많음을 경고하였다. 여기서 과도하게 복잡하다는 것은 시스템을 세련되게 만들기 위한 장치가 시스템의 기본 장치보다 더 앞서가게 되는 것을 말한다. 

보통의 경우 두번째 시스템은 첫번째의 그것보다 기능이 더 많다. 그리고 이것들은 적절히 분류 관리되거나 절제되어 있지 않음으로서 UI가 복잡해지고 옵셔널한 기능이 많아지면서 설정이 많아지는 등이다. 

솔류션 시스템의 경우 첫번째 시스템에서는 직접적으로 고객과 맞닿는 일이 많지 않다. 대부분 실험실에서 소수 인원의 설계로 인해 통일되고 간결한 형태를 지닌다. 첫번째 버전이 고객에게 팔리게 된 이후에 많은 추가 요구사항이 생기게 되고 그것들의 일부는 서로 이율배반적이다. 

설계자는 그것들을 잘 조절하려고 노력하겠지만 그러함에도 많은 요구사항을 어쩔수없이 받아들임으로서 두번째 시스템은 500라인짜리 XML 설정 파일을 가지게 되는 경우는 흔하다. 아마도 그 설정파일의 대부분 사용되지 않을 옵션 기능을 설정하고 활성화 시키는 역할들을 담당하지만  너무도 길고 복잡해서 그에 관심을 쏟는 사용자는 거의 없다. 

이후 이것들은 문제가 생겼을 경우에 문제의 해결에 장애가 되고 덧데기와 IF 코드로 빠르게 부패되는 시스템이 된다. 

이러한 것이 근본적으로 피할수 없는 문제는 아니다. 브룩스는 그 해결방법의 하나로 두개 이상의 시스템 작업을 한, 노련한 설계자를 선택하는 것을 들었지만 사실 최근 대부분의 프로젝트는 과거와 같이 오랜 시간동안 초기 설계자가 이후에도 꾸준히 참여하는 경우는 극히 드물다. 프로젝트는 대부분 1년 이내이며 그동안 인원이 바뀐다. 첫번째 시스템이 성공하든 실패하든 말이다. 

개인적으로 시스템의 발전 방향은 기능의 개수를 늘리는데 집착할 것이 아니라 좀더 똑똑한 시스템이 되어야 한다고 생각한다. 기존의 기능과 상충되는 요구사항이 들어왔을 경우 혹은 목적은 같지만 처리가 달라질 경우에 이후에 개발자도 까먹을 유연성(?)을 이유로 XML 설정 파일을 늘리는 것은 해결책이 될 수 없다. 

똑똑한 시스템이란 사용자의 행동에 주목하는 시스템을 말한다. 사용자가 말하는 것이 아니라 원하는 것을 하는 시스템을 만들어라 라는 말이 있는데 아웃룩의 메일관리와 지메일의 메일관리의 차이이다. 아웃룩은 메일을 분류하기 위해 4단계 이상의 설정을 거치지만 지메일은 달랑 제목을 파싱해서 링크해주는 것 하나이지만 앞의 관점에서 지메일의 관리방식이 좀더 똑똑하다. 


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

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

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

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

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

앞서 말한바와 같이 일단 속성을 가진다는 특성때문에 명사형 객체는 추상클래스가, 동사나 형용사는 인터페이스로 만들 확률이 높다. 물론 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

이를테면 나는 내일 이시간에 내가 무엇을 하고 있을지 약 80%의 확률로 맞출수 있다고 하자. 아마도 컴퓨터 앞에서 자판을 투닥거리고 있을 것이다.

그럼 내가 1년후에 무엇을 하고 있을지 맞출수 있는 확률은 얼마정도일까? 간단한 확률의 문제이다. 다음날 무슨 일을 할지가 독립변수라면 1년후에 내가 무엇을 하고 있을지 맞출 확률도 80%확률이어야 한다. 하지만 다음날 무슨 일을 할지는 독립변수가 아니고 내일모래 무슨일을 할지는 내일 무슨일을 할지에 영향을 받는다. 즉 내일 예상과 다른 일이 일어날 확률이 20%이고 그중의 반은 다음 날들의 일에 영향을 미친다고 하자. 그럴경우 내가 1년후에 무슨일을 하고 있을지 맞출수 있는 확률은 고작 2%에 불과하다.(0.9^365) 그리고 이것도 내일 무슨 일을 하고 있을지 맞출수 있는 확률이 80%라는 아주 낙관적인 기대에서 출발한 것을 잊지 말자.

이런걸 흔히 불확실성의 연속이라고 하는데 그래서 프로젝트에서도 가능하면 3월 이상의 일은 예측이라기 보다 예언이라고 해두는게 좋다. 컨설팅의 비밀이라는 책에서 나오는 의미없는 차이 + 의미없는 차이 + 의미없는 차이.... 는 의미 있는 차이라고 하는 예와 비슷하다. 

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

호출의 빈도  (0) 2009.07.08
한줄짜리 함수  (0) 2009.07.06
패키지의 설계 원칙  (0) 2009.06.08
양화 구축  (2) 2009.06.08
AOP  (0) 2009.06.08
Posted by bleujin

패키지의 설계 원칙은 클래스의 설계원칙과 기본적인면에서 큰 차이가 없다. 현재까지 잘 알려진 설계 원리들은 다음과 같다.


CRP (Common Reuse Principle) - 패키지의 클래스들은 전체적으로 재사용됨.

CCP (Common Closure Principle) - 패키지의 클래스들은 동일한 유형의 변경에 대해서 닫혀있어야 함. 만일 클래스가 변경되어야 한다면 패키지의 모든 클래스들은 마찬가지로 변경되어야 함.

SOC (Separation Of Concerns) - 여러 관심사를 혼합시키지 마라.

세개 모두 High Cohension에 관한 얘기이다. 하나의 패키지는 하나의 클래스와 마찬가지로 Single Responsibility를 가져야 한다.



ADP (Acyclic Dependencies Principle) - 패키지들 간의 의존성 구조는 비순환구조이어야 함. A패키지의 일부 클래스가 B패키지를 참조하고 B패키지의 일부 클래스가 C패키지를 참조하고 C패키지의 일부 클래스가 A 패키지를 참조하는 경우를 순환 패키지 구조라고 하는데

SDP (Stable Dependencies Principle) - 패키지는 최소한 그 자체로 안정적인 패키지들에만 의존해야 함. 를 통해 예방할 수 있다. concrete 클래스가 추상클래스 혹은 인터페이스에 의존하는 것처럼 패키지도 좀더 안정적이고 추상적인 패키지에 의존해야 한다. 예컨데 java의 패키지나 apache의 stable project, 일반적인 프레임워크는 상대적으로 더 안정적이다.


SAP (Stable Abstractions Principle) - 패키지가 점점 더 안정될수록 점점 더 추상화되어야 함. 즉 SDP에 의거해 더 추상화된 패키지에 의존해야 한다는 말로 설명할 수있다.



패키지를 만들때 흔히 하는 잘못들은 A Interface의 구현클래스를 모두 하나의 패키지에 넣어야 한다고 생각하는 것이다. 물론 그럴 경우도 있지만 꼭 그래야 할 필요는 없다.

두번째로 클래스의 이름이 모든 패키지에 대해 전역으로 유니크해야 한다고 생각해서 매우 긴 클래스 이름을 만드는 경우이다. 역시 이는 늑대를 피하다가 호랑이를 만나는 실수가 될 수 있다.  

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

한줄짜리 함수  (0) 2009.07.06
예언?  (0) 2009.06.12
양화 구축  (2) 2009.06.08
AOP  (0) 2009.06.08
Self-Signed Java Applets  (0) 2009.06.01
Posted by bleujin


IT는 다른 학문에 비해 비교적 신입이기 때문에 다른 학문에서 나왔지만 IT에 적용되는 이런저런 법칙들이 있다. 잘 알려진 8:2이 법칙은 성능이나 오류의 문제를 설명하는데 사용되곤 한다.

그밖에도 양화구축 - 혹은 16세기 영국 재무장관 그레셤의 이름을 딴 그레셤의 법칙이라는 게 있다. 

16세기 영국의 주요 화폐는 금화와 은화였다. 처음에는 금화의 포함된 금과 은의 가치와 금화의 가치가 거의 차이가 없었다. 그러나 화폐의 유통이 활발해지면서 화폐를 만들 금과 은이 부족해지면서 점차 금과 은의 함량을 줄인 화폐를 발행하였다.

일반 사람들로서는 최악의 상황에서 언제든 동일한 가치로 교환할수 있는 이전의 금화 은화는 자신의 금고에 두고 이 후에 발행된 함량이 부족한 화폐(악화,惡貨, bad money)만을 사용하게 되는데 이는 화폐의 신뢰문제로 이어지게 되었다.

근세 화폐 경제 체제에서 악화(함량이 부족한 금화)가 양화(1:1비율의 금화)를 구축하는 현상이 심화되자, 사람들은 통용되는 금화와 은화를 믿지 않게 된다. 일일이 금과 은의 함량을 재면서 거래를 하다 보니 화폐의 의미가 크게 퇴색하고 말았다. 결국 근세 화폐 경제 체제가 붕괴 직전까지 가고 말았다.

대항해 시대의 영국은 당시 국제통화로서의 가치를 잃고 싶지 않았고 당시 영국의 재무관이었던 T. 그레셤은 엘리자베스 여왕에게 이런 상황을 개탄하는 편지를 올렸다. 악화를 개주(改鑄)하는 작업을 거치지 않고는 영국이 외환을 지배할 수 없다는 취지였다. 19세기 중반 H. 마크로드는 그레셤의 보고에 ‘그레셤의 법칙’이라는 이름을 붙였고, 이는 훗날 경제학사에 가장 유명한 법칙의 하나가 됐다. 오늘날에서는 나쁜 것이 좋은 것을 압도하는 현상 일반을 지칭하는 용어가 됐다.

최근의 신용중심으로 변한 금융시장에서 그레셤의 법칙은 그 의미를 찾기 힘들지만 IT에서는 종종 사용되곤 한다. 대표적으로 좋은 글이나 사이트가 자극적이고 저급한 글에 밀려 사라지게 되는 현상이나 좋은 아이디어나 프로그램이 상업주의에 밀려 없어질때 사용되곤 한다.(물론 단순히 변명으로 사용되기도 한다.)





Broken Window 혹은 깨진 유리창 이론은 사회학에서 유래한 용어이다. 깨진 유리창 이론(Broken Windows Theory)은 미국의 범죄학자인 제임스 윌슨과 조지 켈링이 1982년 3월에 공동 발표한 깨진 유리창(Broken Windows)이라는 글에 처음으로 소개된 사회 무질서에 관한 이론이다.

깨진 유리창 하나를 방치해 두면, 그 지점을 중심으로 범죄가 확산되기 시작한다는 이론으로, 사소한 무질서를 방치하면 큰 문제로 이어질 가능성이 높다는 의미를 담고 있다. 오랜 기간 수리하지 않고 방치된 창문 하나가 거주자들에게 버려진 느낌을 스며들게 한다. 담당자들이 그 건물에 별 관심이 없다는 느낌 말이다. 그래서 다른 창문이 하나 더 깨진다. 사람들은 이제 어지르기 시작한다. 낙서가 등장한다. 심각한 구조적 손상이 시작된다. 꽤 짧은 시간 안에 소유주가 그걸 고치려는 의지를 넘어설 정도로 건물이 손상되고, 결국 버려진 느낌은 현실이 되어 버린다.


이는 사람들이 쓰레기를 다른 쓰레기가 버려져 있는 곳에 버리는 것과 비슷한 이치이다. 길에 쓰레기가 하나도 없다면 처음으로 쓰레기를 버리고자 하는 사람은 망설여지지만 이미 쓰레기가 버려져 있다면 그다지 죄책감을 가지지 않는 다는 것에 기인한다.

깨진 유리창을 일소하듯이 경범죄 단속에 힘쓰는 것이 중범죄 예방에 효율적인 전략이냐에 대해서는 논란이 좀 있지만 Broken Window 이론은 리팩토링 논리의 바탕이 되는 이론이다. 

깨진 창문 - 즉 조악한 설계의 코드, 형편없는 경영상의 결정 등 프로젝트 기간 동안 팀이 동고동락해야 하는 것들 - 내리막길로 가는 첫걸음이다. 깨진 창문이 꽤 있는 프로젝트를 한다면, "나머지 코드가 전부 쓰레기니까 나도 그렇게 하지 뭐."라는 사고로 빠져들기 너무도 쉽다. … 같은 맥락에서, 코드가 청순할 정도로 아름다운(깨끗하고, 잘 설계되었으며 우아한) 프로젝트와 팀에 여러분이 속해 있다면, … 별도의 특별한 주의를 기울여서 엉망으로 만들지 않도록 노력할 확률이 높다. 비록 불길이 일어날지라도 (데드라인, 출하 날짜, 시사회 데모 등) 엉망진창으로 만드는 첫째 사람이 자신이 되는 것만은 피하려 한다. 그래서 지속적인 리팩토링이 필요하다는 논리이다.




물리학에서는 잘 알려진 엔트로피의 법칙이 있다. 엔트로피란 물질계의 열적 상태를 나타내는 물리량의 하나이다. 통계 열역학적 정의로 엔트로피는 열역학적 계의 통계적인 ‘무질서도’를 나타낸다.

열역학 제 1법칙은 에너지 보존에 관한 법칙이다. 즉 제 1법칙은 "에너지는 그 형태를 달리 할 수는 있으나, 없어지지는 않는다" 는 법칙이다

IT에서 주로 회자되는 원칙은 열역학 제 2법칙으로 우주의 진화 방향에 관한 법칙이다. 즉 제 2 법칙은 "자연계에 있어서 자발적인 진화 방향은 혼란도(또는 emtropy)가 증가하는 방향"이라는 법칙이다. 우주의 에너지는 일정하지만 변화는 계속되기 때문에 결국은 엔트로피가 더 이상 증가할 수 없는 극도의 혼란한 상황에 이르게 된다. 그렇게 되면 더 이상의 자연적인 변화는 불가능하다. 이것이 바로 열죽음이라고 부르는 우주의 종말이다.

이는 사람들이 bit의 조합은 코드는 영원히 순수하리라 기대하지만 실제 코드는 버그수정과 기능 추가등을 통해 부패한다. 따라서 코드의 부패를 막기 위해서는 임의적인 작업이 이루어져야 하며 이는 코드의 생존주기 내내 끝없는 리팩토링의 필요성의 근거이론으로 사용된다.



14세기 영국의 논리학자이며 프란체스코회 수사였던 오컴의 윌리엄에서 이름을 딴 오컴의 면도날이라는 원칙도 있다. 오컴은 보다 적은 수의 논리로 설명이 가능한 경우 많은 수의 논리를 세우지 말라. 라는 말을 자주했는데 이를 약간 변형하여 가장 단순한 것이 답이다. 라는 것으로 알려져 있다.

독일의 Bauhaus 운동의 아키텍트이자 리더인 Ludwig Mies van der Rohe(1886-1969)가 직접 오컴의 면도날 원칙을 참조했는지는 알수 없지만, 설계에 있어서의 최소주의 설계(minimalist design)라는 개념을 주장하였다.

그는 Less is More를 모토로 삼았는데 의미는 단순성(simplicity)과 명료성(clarity)이 좋은 설계를 만들게 된다라는 것으로 현대 설계의 아키텍처의 단순한 형태(style)와 관련된 용어이다.

SW 아키텍처에서는 견실한(consistent) 아키텍처는 동일한 것에 대해 수행하는 두가지 이상의 방법을 제공하지 않는다는 것을 의미하며, 이는 사용자로 하여금 어떤 것을 사용할지를 선택하도록 강요하는 시간 낭비를 유발시킬 수 있기 때문이다.
따라서, 견실한 SW 아키텍처는 배우기가 더 쉽고 빨라야 하며, 일단 처음에 배운 내용을 거의 알지 못한다고 하더라도 그 나머지에 대해서는 쉽게 예상할 수 있게 구성되어야 한다. 특별한 경우에 대해서 염두하고 처리할 필요가 없이 코드는 더 깔끔해지고 테스트 코드는 더 적어야 한다.



원칙까지는 아니지만 혹시 기타를 쳐본 사람들은 시나위의 기타리스트 신대철의 명언을 알것이다. "기타가 펜더면 뭐해? 손꾸락이 펜더여야지" (펜더(Fender)-Gibson 사와 더불어 세계 최고의 일렉기타 메이커)

이는 IT 세일즈의 주장과는 달리 좋은 툴을 든 바보도 그냥 바보다. 라는 말과 일맥상통한다. 이전에 적은대로 툴이든 방법론이든간에 기본적으로 어려운것을 쉽게 해주는 것은 무척이나 어렵다. (아주 가끔 일어나기는 한다.) 단지 복잡하거나 귀찮은 것을 단순하게 만들어 줄 수 있을 뿐이다.

이 주장이 조금 과격해지면 툴은 사람을 바보로 만든다 라는 주장에 쓰이기도 하는데 개인적으로는 그렇게까지는 생각하진 않고 좋은 툴이라도 바보를 똑똑하게 만들지는 못한다 정도로만 이해하는게 좋을 것 같다. 이는 소프트웨어 작업에서 가장 중요한 요소는 프로그래머들이 사용하는 도구나 기술이 아닌 프로그래머들 자신들의 자질(quality)임을 말해준다.


그 밖에도 IT에서 회자되는 타학문의 원칙들은 다음에.. ..






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

예언?  (0) 2009.06.12
패키지의 설계 원칙  (0) 2009.06.08
AOP  (0) 2009.06.08
Self-Signed Java Applets  (0) 2009.06.01
여섯번째 계 - Encapsulation  (0) 2009.04.14
Posted by bleujin

AOP


AOP는 프레임워크나 아키텍쳐보다는 패러다임에 더 가깝기 때문에 굳이 AOP 전용 언어가 아니더라도 그 의미를 살리는데는 큰 무리가 없다.

이를테면 Javascript의 경우
AOP = {
 addBefore : function(obj, fname, before) {
  var oldFunc = obj[fname];
  obj[fname] = function() {
   before(arguments, obj);
   return oldFunc.apply(this,arguments);
  };
 },

 addAfter : function(obj, fname, after) {
  var oldFunc = obj[fname];
  obj[fname] = function() {
   result = oldFunc.apply(this, arguments);
   try{
    return result;
   } finally{
    after(result, arguments, obj);
   }
  };
 }
};

// example 1
   function setRed(o){
    o.style.color = "red";
   }

   function altBeforeAdvisor(args, targetObject){
    alert("before");
   }
   AOP.addBefore(this, "setRed", altBeforeAdvisor);

   // example 2
   function setBlue(o){
    o.style.color = "blue";
   }

   function altAfterAdvisor(result, args, targetObject){
    alert("after");
   }

   AOP.addAfter(this, "setBlue", altAfterAdvisor);

와 같이 비교적 쉽게 구현할 수 있다.

좀더 나은 예제는

에서 볼수 있다.


자바에서 AOP을 적용 하는 것은 생각보다 쉽지 않다. http://www.voelter.de/data/articles/aop/aop.html 와 같이 AspectJ언어를 사용하지 않는다면 말이다.

스프링처럼 AOP패러다임을 제공하는 프레임워크를 만들기 위해서는 약간의 테크니컬한 기술이 필요하다. 가장 쉽게는 데코레이터 패턴을 생각해 볼 수 있다. 이러 상황에서 상속은 좀 위험하기 때문에 현명한 선택이 아니다.

다소 복잡하지만 Proxy를 사용할 수도 있다.
먼저 Proxy객체는 InvacationHandler를 구현해야 한다.

package com.bleujin.thinlet.sample.invocation;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;


public class NumObjectHandler implements InvocationHandler{
  
  private Object target ;
  NumObjectHandler(Object target){
    this.target = target ;
  }

  public Object invoke(Object proxy, Method m, Object[] argsthrows Throwable {
    System.out.println(m.getName());
    return m.invoke(target, args);
  }
  
}

NumObjectHandler 클래스는 모든 메소드의 호출전에  System.out.println(m.getName());를 실행시켜 준다.


package com.bleujin.thinlet.sample.invocation;

import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.List;

import junit.framework.TestCase;

public class TestInvoke extends TestCase {

  
  public void testSum() throws Exception {
    
    TestInvoke iv = new TestInvoke() ;
    
//    NumObject n3 = new NumObject(3);
//    NumObject n5 = new NumObject(5);
    
    NumObject no3 = new NumObject(3);
    NumObject no5 = new NumObject(5);
    
    INum n3 = (INum)Proxy.newProxyInstance(new SimpleClassLoader(""), no3.getClass().getInterfaces()new NumObjectHandler(no3)) ;
    INum n5 = (INum)Proxy.newProxyInstance(new SimpleClassLoader(""), no5.getClass().getInterfaces()new NumObjectHandler(no5)) ;
    
    iv.add(n3);
    iv.add(n5);
    
    assertEquals(8, iv.sum()) ;
  }
  
  
  private int sum() {
    int result = ;
    for (Object obj : data) {
      result += ((INum)obj).getValue() ;
    }
    return result;
  }


  private List<Object> data = new ArrayList<Object>() 
  private void add(INum numObject) {
    data.add(numObject;
  }
}


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

패키지의 설계 원칙  (0) 2009.06.08
양화 구축  (2) 2009.06.08
Self-Signed Java Applets  (0) 2009.06.01
여섯번째 계 - Encapsulation  (0) 2009.04.14
중복을 볼 수 있는 눈  (0) 2009.03.13
Posted by bleujin

Generate a new keystore "testkey.keystore" as follows:

keytool -genkey -keyalg RSA -sigalg MD5withRSA -keystore testkey.keystore -alias testkey -validity 1460

Using this command, the keytool will generate a new keypair and create a self signed certificate for its public key. To create the certificate, the keytool will prompt for the necessary bits of X.509 information.

To sign the applet, use code similar to this (this code snipped was copied out of an Ant script):

<exec executable="${env.JAVA_HOME}/bin/jarsigner.exe">

    <arg value="-keystore"/>
    <arg value="testkey.keystore"/>
    <arg value="-storepass"/>
    <arg value="changeit"/>
    <arg value="-signedjar"/>
    <arg value="output.jar"/>
    <arg value="input.jar"/>
    <arg value="testkey"/>
</exec>

In order to run an applet signed with this key, the generated certificate may have to be imported into the Java plugin. For this, the certificate must be exported and stored in a file (e.g. 'testkey.cer'). The command line used to do this is:

keytool -export -alias testkey -file testkey.cer -keystore  testkey.keystore

The default keystore password is 'changeit'


Sharing Java objects between class loader instances
http://tom.conjective.ch/tomtom/space/Sharing+Java+objects+between+class+loader+instances

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

양화 구축  (2) 2009.06.08
AOP  (0) 2009.06.08
여섯번째 계 - Encapsulation  (0) 2009.04.14
중복을 볼 수 있는 눈  (0) 2009.03.13
Here is Dragon  (0) 2009.03.12
Posted by bleujin