Function hook 테크닉 (템플릿 메소드 패턴)

Posted by epicdev Archive : 2012. 10. 8. 21:57

오늘은 Function hook 테크닉을 소개하고자 합니다.

테크닉의 명칭은 제가 그냥 이 테크닉을 알게된 코드의 주석에서 따온 거구요, 이 테크닉에 대한 정식 명칭이 있는지는 잘 모르겠습니다.

이 테크닉은 제가 JDK의 HashMap 코드를 보다가 접하게 되었습니다만 사실 예전부터 그냥 인지하지 못하고 가끔씩 사용하던 패턴이었고,

실제로 다른 분들도 많이 사용하시리라 생각합니다.


업데이트 (2013.03.20)

역시나 이 테크닉 또한 디자인 패턴이었군요. 템플릿 메소드 패턴이라고 불리는 디자인 패턴입니다.

자세한 내용은 아래의 링크를 참고하시길...

http://en.wikipedia.org/wiki/Template_method_pattern


우선 제가 접한 코드를 보도록 하겠습니다.



위의 코드에는 HashMap의 생성자들과 init이라는 함수가 있습니다.

init()은 HashMap의 생성자들의 맨 마지막에 호출이 되는 함수입니다.

그런데 이상하게도 init()의 body는 텅 비어있습니다.

텅 비어있는 함수를 왜 생성자 마지막에 호출하는 것일까요?


init()의 javadoc에는

Initialization hook for subclasses. This method is called in all constructors and pseudo-constructors

라고 적혀있습니다.


즉, 이 init()은 하위 클래스에서 추가적인 초기화 코드가 필요할 경우 init()을 오버라이드해서 사용할 수 있도록 미리 상위 클래스에서 만들어 놓은 함수입니다.

이렇게 하게되면 굳이 하위 클래스에서 모든 생성자를 override해서 초기화 코드를 넣지 않아도 됩니다.

그냥 간단하게 init()이라는 함수만 override해서 초기화코드를 넣으면 되는 것입니다.

이러한 function hook 테크닉은 초기화 코드 뿐만 아니라, 상속 후 기능 확장이 예상되는 부모클래스의 다양한 곳에서 사용 될 수 있습니다.


정리하자면, function hook 테크닉은 어떤 부모 클래스를 만들 때 하위 클래스에서 확장이 필요할 것 같은 부분을 부모 클래스를 설계 할 때 미리 파악해서

하위 클래스가 좀 더 수월하게 부모 클래스를 확장시킬 수 있도록 도와주는 테크닉이라고 할 수 있겠습니다.

'Archive' 카테고리의 다른 글

오픈소스 라이센스의 종류  (0) 2013.02.12
우분투 창 버튼 오른쪽으로 옮기기  (0) 2012.12.22
KAIST 셔틀 for Android  (0) 2012.09.15
Word Cloud 생성기  (0) 2012.09.08
SVN Branch & Merge 헷갈릴 때 참고할만한 글  (0) 2012.09.06
  

Java에서 사용되는 System.out의 경우 default는 console로 지정이 되어있습니다.

System.out은 System클래스의 static instance로써 PrintStream object 입니다.

따라서 이 System.out을 다른 PrintStream으로 바꿔준다면, System.out을 console이 다른 곳에 출력하도록 할 수 있을 것입니다.

만약 System.out을 잔뜩 사용해서 화면에 log를 출력하던 프로그램이 있는데

화면에 출력하는 방식이 아닌 파일로 출력하는 방식으로 바꾸어야 한다면

기존에 사용했던 System.out을 하나하나 파일로 쓰는 코드로 변환할 필요없이

System.out 자체를 다른 PrintStream으로 간단하게 변경하여

기존의 모든 코드를 수정하는 번거로움을 피할 수 있습니다.



위의 코드에서는

System.out은 console로 두고 System.err은 로그파일로 두어서

기본적인 내용은 화면에 출력하고, 중요한 에러들은 로그파일에 출력하도록 한 것입니다.

  

출처: http://stackoverflow.com/questions/10047802/public-static-final-or-private-static-final-with-getter


In Java, it's taught that variables should be kept private to enable better encapsulation, but what about static constants? This:

public static final int FOO = 5;

Would be equivalent in result to this:

private static final int FOO = 5;
...
public static getFoo() { return FOO; }

But which is better practice?


There's one reason to not use a constant directly in your code.

Assume FOO may change later on (but still stay constant), say to public static final int FOO = 10;. Shouldn't break anything as long as nobody's stupid enough to hardcode the value directly right?

No. The Java compiler will inline constants such as Foo above into the calling code, i.e.someFunc(FooClass.FOO); becomes someFunc(5);. Now if you recompile your library but not the calling code you can end up in surprising situations. That's avoided if you use a function - the JIT will still optimize it just fine, so no real performance hit there.


요약: 만약 어떤 코드가 caller와 callee로 되어 있을 때 callee의 상수가 public static final로 되어있을 경우, 그냥 그대로 사용할 경우 문제가 발생할 수 있다.

예를 들어 public static final int Foo = 5라고 정의가 되어있을 때 만약 someFunc(FooClass.Foo)라고 caller에서 사용한다면, JIT에서는 SomeFunc(5)로 치환해서 컴파일을 하게 된다. 그렇게 때문에 나중에 callee의 코드를 public static final int Foo = 10으로 고치게 되버리면 caller를 재컴파일하지 않으면 문제가 발생하게 된다. 이런 상황을 방지하려면, getter를 사용해야 한다.

non-static이나 non-final인 경우 이런 문제가 발생하지 않는다


아래는 추가 자료

출처: http://stackoverflow.com/questions/5173372/java-static-final-values-replaced-in-code-when-compiling


==fileA.java==
class A
{  
   
public static final int SIZE = 100;
}  

Then in another file i use this value

==fileB.java==  
import A;
class b
{
     
Object[] temp = new Object[A.SIZE];
}

When this gets compiled does SIZE get replaced with the value 100, so that if i were to down the road replace the FileA.jar but not FileB.jar would the object array get the new value or would it have been hardcoded to 100 because thats the value when it was originally built?


Another route to proving that the behavior is to looking at the generated bytecode. When the constant is "small" (presumably < 128):

public B();
 
Code:
   
0:   aload_0
   
1:   invokespecial   #10; //Method java/lang/Object."<init>":()V
   
4:   aload_0
   
5:   bipush  42
   
7:   anewarray       #3; //class java/lang/Object
   
10:  putfield        #12; //Field temp:[Ljava/lang/Object;
   
13:  return

}

(I used 42 instead of 100 so it stands out more). In this case, it is clearly substituted in the byte code. But, say the constant is "bigger." Then you get byte code that looks like this:

public B();
 
Code:
   
0:   aload_0
   
1:   invokespecial   #10; //Method java/lang/Object."<init>":()V
   
4:   aload_0
   
5:   ldc     #12; //int 86753098
   
7:   anewarray       #3; //class java/lang/Object
   
10:  putfield        #13; //Field temp:[Ljava/lang/Object;
   
13:  return

When it is bigger, the opcode "ldc" is used, which according to the JVM documentation "an unsigned byte that must be a valid index into the runtime constant pool of the current class".

In either case, the constant is embedded into B. I imagine, since that in opcodes you can only access the current classes runtime constant pool, that this the decision to write the constant into the class file is independent of implementation (but I don't know that for a fact).

  

객체지향 설계의 5원칙: OCP, SRP, LSP, DIP, ISP

Posted by epicdev Archive : 2012. 7. 29. 20:27

1) OCP (Open Closed Principle)

소프트웨어의 구성요소(클래스, 모듈, 함수 등) 들은 확장에 대해서는 열려있어야 하지만, 변경에 대해서는 닫혀있어야 한다.


2) SRP (Single Responsibility Principle)

모든 클래스는 하나의 책임만을 지녀야 한다.

이는 또한, 객체를 변경해야 하는 이유는 단 하나여야 한다는 것과도 상통한다.

예를 들어 클래스 A는 출력에 대한 책임만 가지고 있고, 클래스 B는 입력과 출력에 대한 책임을 모두 가지고 있다고 했을 때

SRP를 지키고 있는 클래스 A가 그렇지 않은 B보다 상대적으로 복잡도가 낮을것이며, 또한 코드가 수정 될 가능성도 적을 것이다.


3) LSP (Liskov Substitution Principle)

Parent형의 변수에 Child 클래스의 인스턴스를 대입해도 문제없이 사용할 수 있어야 한다.

http://epicdevs.com/119


4) DIP (Dependency Inversion Principle)

상위 레벨 모듈은 하위 레벨 모듈에 의존하지 않아야 한다. 상위 레벨, 하위 레벨 모두 추상에 의존하여야 한다.

추상은 구상에 의존하지 않아야 한다. 구상 또한 추상에 의존하여야 한다.

http://epicdevs.com/117


5) ISP (Interface Segregation Principle)

클라이언트가 사용하지 않는 인터페이스에 클라이언트가 영향을 받아서는 안된다.

여러개의 클라이언트가 함께 사용하는 매우 큰 인터페이스를 만들지 말고,

좀 더 작고 구체적인 인터페이스를 만들어서, 하나의 인터페이스가 하나의 기능만을 담당하도록 만들어야 한다.

http://extern.tistory.com/14

http://www.oodesign.com/interface-segregation-principle.html

  

지나친 테스트

Posted by epicdev Archive : 2012. 6. 16. 02:29

도가 지나친 수준으로 테스트에 관심을 갖는 경우도 있다.


  • 테스트를 기능하게 하려고 실제 코드의 가독성을 희생시킨다. 실제 코드 테스트를 가능하게 하는 것은 반드시 윈-윈 상황이 되어야 한다. 하지만 테스트를 가능하게 하려고 실제 코드에 지저분한 코드를 집어넣어야 한다면, 뭔가 잘못된 것이다.
  • 100% 코드 테스트에 집착하는 일. 코드의 90%를 테스트하는 노력이 종종 나머지 10%를 테스트하는 비용보다 적은 노력이 들기도 한다. 그 10%는 어쩌면 버그로 인한 비용이 별로 높지 않기 때문에 굳이 테스트할 필요가 없는 사용자 인터페이스나 이상한 에러 케이스를 포함하고 있을지도 모른다.
  • 사실, 코드를 100% 테스트하는 일은 일어나지 않는다. 테스트되지 않은 버그가 있을 수도 있고 테스트되지 않은 기능이 있을 수도 있으며, 요구사항이 달라졌다는 사실을 모르고 있을 수도 있기 때문 이다.
  • 버그가 야기하는 비용이 어느 정도인지에 따라서, 테스트 코드를 작성하는 시간이 의미를 갖는 부분이 있고 그렇지 않은 부분도 있기 마련이다. 만약 웹사이트의 프로토타입을 만든다면, 테스트 코드 작성 건은 전혀 의미가 없다. 한편 우주선이나 의료장비를 통제하는 프로그램을 작성한다면 아마 테스트 코드에 주된 관심을 쏟아야 할 것이다.
  • 테스트 코드로 실제 제품 개발이 차질을 빚게 되는 일. 우리는 단지 프로젝트의 일부분에 불과한 테스트가 프로젝트 전체를 지배하는 경우를 본 적이 있다. 테스트가 숭배되어야 하는 신의 자리를 차지하고, 프로그래머들은 자신의 시간이 다른 일에 쓰이는 것이 더 낫다는 사실을 망각한 채 자신을 위한 의식과 동작에 몰두한다. 




읽기 좋은 코드가 좋은 코드다

저자
더스틴 보즈웰 지음
출판사
한빛미디어 | 2012-04-06 출간
카테고리
컴퓨터/IT
책소개
이 책은 코드를 작성할 때 언제나 적용할 수 있는 기본적인 원리...
가격비교


  

테스트에 친숙한 개발

Posted by epicdev Archive : 2012. 6. 16. 02:20

테스트하기 어려운 코드의 특징과 이것이 설계와 관련된 문제에 미치는 영향

특징 

테스트 문제 

설계 문제 

전역변수를 사용한다 

테스트할 때마다 모든 전역 변수를 초기화해야 한다. 그렇지 않으면 테스트가 서로의 결과에 영향을 줄 수 있다.

어느 함수가 어떤 부수적인 효과를 가지는지 판별하기 어렵다. 각각의 함수를 별도로 고려할 수 없다. 모든 게 제대로 작동하는지 알려면 프로그램 전체를 생각해야 한다. 

코드가 많은 외부 컴포넌트를 사용한다 

처음에 설정할 일이 너무 많아서 테스트를 작성하기 힘들다. 따라서 테스트를 작성하는 일이 즐겁지 않아 테스트 작성을 회피한다.

이러한 외부 시스템 중에서 어느 하나가 제대로 작동하지 않으면 프로그램이 실패한다. 프로그램에 가한 수정이 어떤 효과를 낳을지 알기 어렵다. 클래스들을 리팩토링하기 어렵다. 시스템이 더 많은 실패 모드와 복구 경로를 가지게 된다. 

코드가 비결정적인(nondeterministic) 행동을 가진다 

테스트가 변덕스럽고 안정적이지 못하다. 가끔 실패하는 테스트가 그냥 무시된다. 

프로그램이 경합 조건이나 재생하기 어려운 버그를 가지고 있을 확률이 높다. 프로그램의 논리를 따라가기가 어렵다. 현장에서 발생한 버그를 추적해서 수정하기가 매우 어렵다.


테스트하기 좋은 코드의 특징

 특징

테스트 장점 

설계 장

클래스들이 내부 상태를 거의 가지고 있지 않다

메소드를 테스트하기 전에 설정할 일이 거의 없고 감추어져 있는 상태가 별로 없기 때문에 테스트 작성이 수월하다.

소수의 내부 상태를 가지는 클래스는 이해하기 더 간단하고 쉽다.

클래스/함수가 한 번에 하나의 일만 수행한다

더 적은 테스트 코드가 요구된다.

더 작고 간단한 컴포넌트는 더 잘 모듈화되어있고, 시스템이 서로 더 멀리 떨어져 있다

클래스가 다른 클래스에 의존하지 않고, 서로 상당히 떨어져 있다

각 클래스가 독립적으로 테스트된다 (여러 클래스를 동시에 테스트할 때에 비해서 훨씬 쉽다) 

시스템이 병렬적으로 개발될 수 있다. 클래스가 쉽게 수정될 수 있고, 혹은 시스템의 나머지 부분에 영향을 주지 않으면서 제거될 수도 있다.

함수들이 간단하고 잘 정의된 인터페이스를 가지고 있다 

테스트 대상이 잘 정의되어 있다. 간단한 인터페이스는 테스트를 위해서 더 적은 일을 요구한다. 

프로그래머가 인터페이스를 쉽게 배울 수 있어 해당 인터페이스는 재사용될 가능성이 더 높다. 




읽기 좋은 코드가 좋은 코드다

저자
더스틴 보즈웰 지음
출판사
한빛미디어 | 2012-04-06 출간
카테고리
컴퓨터/IT
책소개
이 책은 코드를 작성할 때 언제나 적용할 수 있는 기본적인 원리...
가격비교


'Archive' 카테고리의 다른 글

MapReduce  (0) 2012.06.23
지나친 테스트  (0) 2012.06.16
자기 주변에 있는 라이브러리에 친숙해져라  (0) 2012.06.16
변수의 범위를 좁혀라  (0) 2012.06.15
쇼트 서킷 논리 (Short-Circuit Logic) 오용 말기  (0) 2012.06.15
  

프로그래머는 이미 존재하는 라이브러리로 자신의 문제를 풀 수 있는 상황이 많다는 걸 모르고 있다.

아니면 라이브러리가 할 수 있는 일을 잊어버린다. 라이브러리가 할 수 있는 일을 알고 활용하는 것은 대단히 중요하다.


 매일 15분씩 자신의 표준 라이브러리에 있는 모든 함수/모듈/형들의 이름을 읽어라


여기에는 C++의 STL, Java API, 내장된 파이썬 모듈 등이 모두 포함된다.

라이브러리 전체를 암기하라는 게 아니다. 그냥 그 안에 무엇이 있는지 감을 잡아놓고,

나중에 새로운 코드를 작성할 때 "잠깐만, 이건 전에 API에서 보았던 것과 뭔가 비슷한데..." 하고 생각할 수 있기를 바라는 것이다.

이러한 습관을 들이려고 노력하면 생각보다 금세 좋은 결과를 얻을 수 있다.

코드를 직접 작성하는 대신 우선적으로 이미 존재하는 라이브러리를 사용하는 습관을 갖게 되기 때문이다.




읽기 좋은 코드가 좋은 코드다

저자
더스틴 보즈웰 지음
출판사
한빛미디어 | 2012-04-06 출간
카테고리
컴퓨터/IT
책소개
이 책은 코드를 작성할 때 언제나 적용할 수 있는 기본적인 원리...
가격비교


'Archive' 카테고리의 다른 글

지나친 테스트  (0) 2012.06.16
테스트에 친숙한 개발  (0) 2012.06.16
변수의 범위를 좁혀라  (0) 2012.06.15
쇼트 서킷 논리 (Short-Circuit Logic) 오용 말기  (0) 2012.06.15
설명 변수와 요약 변수  (0) 2012.06.15
  

변수의 범위를 좁혀라

Posted by epicdev Archive : 2012. 6. 15. 22:16


 변수가 적용되는 범위를 최대한 좁게 만들어라


많은 프로그래밍 언어는 모듈, 클래스, 함수, 블록 범위 같은 다양한 범위/접근 수준을 제공한다.

더 제한적인 접근을 이용하면 변수가 더 적은 줄 내에서만 '보이므로' 일반적으로 더 좋다.


왜 그럴까?

바로, 코드를 읽는 사람이 한꺼번에 생각해야 하는 변수 수를 줄여주기 때문이다.

모든 변수의 범위를 두 배로 축소시키면, 한 번에 읽어야 하는 변수의 수는 평균적으로 반으로 줄어든다.


많은 메소드를 static으로 만들어서 클래스 멤버 접근을 제한해라. 가급적 static 메소드는 코드를 읽는 사람에게

'이 코드는 저 변수들로부터 독립적'이라는 사실을 알려주는 매우 좋은 방법이다.


커다란 클래스를 여러 작은 클래스로 나누는 방법도 있다. 이 방법은 작은 클래스들이 서로 독립적일 때 유용하다.

만약 클래스를 두 개의 작은 클래스로 나누었는데 서로의 멤버를 참조한다면, 실제로 성취한 일은 아무 것도 없게 된다.




읽기 좋은 코드가 좋은 코드다

저자
더스틴 보즈웰, 트레버 파우커 지음
출판사
한빛미디어 | 2012-04-06 출간
카테고리
컴퓨터/IT
책소개
이 책은 코드를 작성할 때 언제나 적용할 수 있는 기본적인 원리...
가격비교


  

쇼트 서킷 논리 (Short-Circuit Logic) 오용 말기

Posted by epicdev Archive : 2012. 6. 15. 21:57

대부분의 프로그래밍 언어에서 불리언 연산은 쇼트 서킷 평가를 수행한다.

예를 들어 if (a || b)에서 a가 참이면 b는 평가하지 않는다. 이는 매우 편리하지만 때로는 매우 복잡한 연산을 수행할 때 오용될 수도 있다.



이 코드는 한 줄에 불과하지만 대부분의 프로그래머는 의미를 이해하기 위해서 손을 멈추고 생각해야 한다.



이 코드는 동일한 일을 수행한다. 코드가 두 줄로 늘어났지만 훨씬 이해하기 쉬워졌다.

그럼 첫 번째 코드가 굳이 한 줄짜리 거대한 표현으로 작성된 이유는 무엇이었을까?

코드를 작성하던 당시에는 그렇게 하는 게 매우 영리하다고 생각했기 때문이다. 짧은 코드에 논리를 집어넣는 행위에는 어떤 즐거움이 있기 때문이다.

우리는 모두 일을 하면서 어떤 즐거움을 얻기를 원한다. 문제는 바로 그런 코드가 나중에 코드를 읽는 사람에게는 정신적인 장애물이 된다는 데 있다.


'영리하게' 작성된 코드에 유의하라. 나중에 다른 사람이 읽으면 그런 코드가 종종 혼란을 초래한다.



읽기 좋은 코드가 좋은 코드다

저자
더스틴 보즈웰 지음
출판사
한빛미디어 | 2012-04-06 출간
카테고리
컴퓨터/IT
책소개
이 책은 코드를 작성할 때 언제나 적용할 수 있는 기본적인 원리...
가격비교


  

설명 변수와 요약 변수

Posted by epicdev Archive : 2012. 6. 15. 21:46
설명변수

커다란 표현을 쪼개는 가장 쉬운 방법은 작은 하위표현을 담을 "추가 변수"를 만드는 것이다.
추가 변수는 하위표현의 의미를 설명하므로 "설명 변수"라고도 한다.
요약 변수

request.user.id == document.owner_id라는 표현이 커다랗게 보이지는 않지만, 이는 변수 다섯 개를 담고 있다.
따라서 이 표현을 읽으려면 추가적인 시간이 필요하다.

다음 코드의 핵심 개념은 "사용자가 이 문서를 소유하는가?"이다. 이러한 개념은 요약 변수를 더하면 더 명확하게 표현할 수 있다.

대단한 개선처럼 보이지 않을지 몰라도 if (user_owns_document)라는 구문은 더 읽기 쉽다.
또한 user_owns_document라는 표현을 맨 위에 두어 코드를 읽는 사람에게 "이것이 바로 이 함수에서 생각해야 하는 주된 개념이로군"이라는 생각이 들게 한다.




읽기 좋은 코드가 좋은 코드다

저자
더스틴 보즈웰 지음
출판사
한빛미디어 | 2012-04-06 출간
카테고리
컴퓨터/IT
책소개
이 책은 코드를 작성할 때 언제나 적용할 수 있는 기본적인 원리...
가격비교


  
 «이전 1 2 3 4 ··· 6  다음»