최고 엔지니어의 꿈을 향해서

'2007/03'에 해당되는 글 4건

  1. 2007.03.18 <OPCODE> (1)
  2. 2007.03.13 두번째
  3. 2007.03.05 ARM System Develop's Guide <Designing and Optimizing System Software>
  4. 2007.03.02 Let's get it started (1)

<OPCODE>

2007.03.18 15:23 : Computer Science/ARM
1. ADC = Add with Carry
2. ADD
3. AND
4. B, BL = Branch | Branch and Link
5. BIC = Bit Clear
6. BKPT = BreakPoint
8. BLX(2) = Branch with Link and Exchange
9. BX = Branch and Exchange
10. CDP = Coprocessor Data Processing
11. CLZ = Count Zero Leadings
12. CMN = Compare Nagative
13. CMP = Compare
14. EOR = Exclusive OR
15. LDC = Load Coprocessor
16. LDM(1) = Load Multiple
17. LDM(2)
18. LDM(3)
19. LDR = Load Register
20. LDRB = Load Register Byte
21. LDRBT = Load Register Byte with Translation
22. LDRH = Load Register Halfword
23. LDRSB = Load Register Signed Byte
24. LDRSH = Load Register Signed Halfword
25. LDRT = Load Register with Translation
26. MCR = Move to Coprocessor from Arm Register
27. MLA = Multiply Accumulate
28. MOV
29. MRC = Move to Arm Register from Coprocessor
30. MRS = Move PSR to General Purpose Register
31. MSR = Move to Status Register from Arm Register
32. MUL = Multiply
33. MVN = Move Negative
34. ORR = Logical OR
35. RSB = Reverse Subtract
36. RSC = Reverse Subtract with Carry
37. SBC = Subtract with Carry
38. SMLAL = Singed Multiply Accumulate Long
39. SMULL = Singed Multiply Long
40. STC = Store Coprocessor
41. STM(1) = Store Multiple
42. STM(2)
43. STR = Store Register
44. STRB = Store Register Bytes
45. STRBT = Store Register Bytes with Translation
46. STRH = Store Register Halfword
47. STRT = Store Register with Translation  
48. SUB = Subtract
49. SWI = Software Interrupt
50. SWP = Swap  
51. SWPB = Swap Byte
52. TEQ = Test Equivalence
53. TST = Test
54. UMLAL = Unsigned Multiply Accumulate Long
55. UMULL = Unsigned Multiply Long
Posted by 정해권 Trackback 1 Comment 1
State Pattern [2번째]

간만에 다음 디자인패턴에 대해서..글을 쓰게 되었다.
첫번째 Strategy pattern으로 시작하였기 때문에, 이번엔 무슨패턴으로 글을 쓸까 하다가 그래도 설명을 쉽게 하기 위해서, Strategy pattern과 가장 유사하다고 볼 수 있는 State pattern에 대해서 쓰기로 맘먹었다.

* 일반적으로 패턴이란것이, 꼭 써야 하는것은 아니고, 남용 해서도 안되고, 잘못 써서는 더더욱 안되는것이다. 일반적으로 내가 알고 있는 철학은 혹은 나만의 철학은, 프로그램 설계 시, 먼저 프로그램이 나중에 어떻게 변할 것인가를 먼저 염두해 둔다. 내가 만들고자 하는것이 프로그램이던 비지니스상의 프로세스이건, 나중에 어떤식으로 확장이 되고, 특히 어느 부분(코드)이 나중에 사용자의 수에 따라서 혹은, 사용자 요구상황에 따라서, 변경이 될 것인가, 라는 것을 항상 염두 해 두고, 그 부분을 고려해서 설계 해야 한다. 결국 그 말은, 변경의 여지가 다소 없는 부분들은 구태여 패턴의 사용으로 코드 복잡성을 높일 이유가 없다는 것이다. 왜냐면 확장성 있는 로버스트 한 코드들은 대부분 퍼포먼스랑은 상반되기 때문에, 아무리 퍼포먼스의 영향이 적다 할지라도, 그리고 코드 이해하는 부분에서도 그리 쉽지 않기 때문에..등등..처음 예상했던 것과 달리, 어떤 부분의 코드가 매우 비효율 적이라던지, 예측못한 부분에서 코드를 변경해야 할 경우라던지, 등등 나중에 리펙토링단계에서 갈아 어플수 있기때문에..구태여 설계단계에서부터, 너무 모든 코드 곳곳에 확장성 고려 목적으로 패턴적용을 할 필요는 없고 해서도 안된다는것이 나의 마인드이다. 결국 패턴은 아주 큰 틀에서, 생각해야 한다..

[State pattern]

이 패턴은 매우 간단하다. 그리고 전략패턴과 매우 유사 하기때문에 이해하기 쉽다.
다만 전략패턴과 차이점을 이해하는 것이 중요하다고 볼 수 있겠다.(* 패턴의 차이점이란, 코드의 구현방법차이에서 도 물론 포함하지만, 사용목적, 용도의 의미 모두를 포함하므로, 코드구현은 거의 비슷한데 왜 별도의 패턴이름을 명명 했을까? 라는 의문은 패턴의 근본 탄생배경, 목적이 다르기 때문이라고 보면 된다.

먼저 상태 패턴이란 말그대로 , 상태에 따라서 동작을 바꾼다는 것인데, 상태와 행위가 서로 밀접한 관계에 있을때 사용한다. 그리고 상태관리의 변수가 많고, 그에 의존하는 행위가 복잡할 경우, 사용하면
깔금한 디자인을 만들수 있다 하겠다. 그렇지 않을 경우엔 조건문 등을 이용해야 하는데, 시스템이 커질수록 코드가 지저분해진다.

한가지 예를 들어서 설명하면( GOF책에서)
TCPConnection 이란 객체가 있다고 생각하자.
저 객체는 사용자의 접속 요청을 관리해 주는 객체인데, 현재 네트워크 Tcp의 상태에 따라 사용자의 요구를 어떻게 처리해줘야 할지 바뀌게 된다. 예를들면, Open()이란 함수를 콜 하였을때도, Tcp네트웍 상태가 TCPEstablished인지 TcpListen상태인지 TcpClosed상태 인지에 따라서 같은 함수처리에 대한 내용이 바뀐다는것이다. 패턴적용 안하면, 상태변수를 이용하여 조건문으로 해줘야 한다. (즐~)
물론 뭐가 좋다 안좋다는 아니고, 상황에 맞게 맞는 디자인을 적용해야 한다 이것이다.
그럼 TCP상태를 나타내는 클래스를 만들때 일단, 추상 클래스 한개 만들자.
TCPState 를 한개 추상으로 만들고 각각 서브클래스로 TCPEstablished , TCPListen, TCPClosed 를 만들자.
다음 TCPConnection객체를 만들어 사용자의 리퀘스트에 응답한다, 함수는 Open(), Close() 정도 만들고, 각각의 함수에서 각 행위를 상태객체에 위임한다. (델리게이션, 전략패턴이랑 같다.)
넘길때, 인자로 TCPConnection의 객체도 같이 넘긴다.(상태객체가 TCPConnection객체의 데이타 이용을 할수있기 위함이며, TCPConnection같은 객체가 여러개 존재할경우, 각각 상태객체를 공유할때, 상태객체에서 누가 호출했는지 구분하기 위해서).

TCPConnection객체는 TCPState추상 클래스의 인스턴스 변수를 가지고 있고, 그 변수를 통하여 실제 상태 객체들에게 접근한다. 할당은 TCPConnection 생성자나 뭐 자기가 하고싶을때 적절히 해준다.
TCPConnection클래스에는 Setter클래스를 하나 만들어, 상태를 바꿀수 있는 함수 한개 만들고, 각각 상태 클래스에서 상태의 전이를 위해, 필요시 TCPConnection클래스의 상태전이변경함수를 호출해준다. 여기서 주의할점은, 상태전이를 상태객체 클래스내부에서 처리하느냐, 아니면 TCPConnection에서 해주느냐 인데, 전자의 경우, 클래스 상호 의존도가 높아지는 단점이 있는 반면(구현종속), 좀더 동적으로 구성할수 있고, (변수의 값에 의한 상태전이라는지..), 후자의 경우는 의존도가 낮아지는 대신, 좀더 유연한 대처가 불가능하다.)

수도 코드를 작성해보겠다

class TCPConnection {
   TCPState state; // 추상클래스 인스턴스변수
   
   TCPEstablished estState;
   TCPListen listState;     // 각각 상태클래스 인스턴스 변수;
   TCPClosed closeState;

   public void TCPConnection() {
         state = new TCPEstablised(); // 초기화
         estState = new TCPEstablished(this);
         listState = new TCPListen(this);          // 요런식으로 초기화
         closeState = new TCPClosed(this);
   }

   void open() {
        state.open();        // 단순히 행위 위임만 하였지만, 행위가 시퀀셜하게 뒷따라나와야 하는 경우
   }                             //  함수 호출이후 다른 예를들면, close()를 여기서 호출하거나 할수있다.
   void close() {
        state.close();
   }
   void setState(TCPState s) {
       state = s;
   }
////////////////////////////////////////////////////////////

class abstract TCPState {
   public abstract void open();  
   public abstract void close();
}
위에서 함수가 추상함수뿐인데, 인터페이스로 왜 안만들었느냐에 대해선, 다른 실질 함수가 올수 있다는것을 의미. 물론 여기에서는 귀찮아서 뺀것뿐. 그리고 변수들도 당근 올수있음.

class TCPEstablished extends TCPState {
   TCPConnection con;  // 요거 주목. 이유는 상태객체에서도 TCPConnection객체에 접근하기위함.
   public void TCPEstablished(TCPConnection con) {
      this.con = con;
   }
public void open() {
   // 연결이 확립되었을경우의 open코드 처리.
}
public void close() {
  // 연결상태에서의 close()동작
}
}
///////////////////////////////////////////////////////////////     
class TCPListen extends TCPState {
   TCPConnection con;  
   public void TCPListen(TCPConnection con) {
      this.con = con;
   }
public void open() {
   // 리스닝상태의 open코드 처리.
}
public void close() {
  // 리스닝상태에서의 close()동작
}
}
/////////////////////////////////////////////////////////////////
class TCPClosed extends TCPState {
   TCPConnection con;  
   public void TCPClosed(TCPConnection con) {
      this.con = con;
   }
public void open() {
   // 클로즈상태의 open코드 처리.
}
public void close() {
  // 클로즈상태에서의 close()동작
  // 이미 닫힌 상태입니다 라는식의 관련 코드.. 혹은 엠티
}
}

머 일단 이런식인데, 위의 코드에서는 상태 이전에 관련된 코드가 없다.
상태 이전이란  클로즈상태에서 open을 호출하였을경우, 더이상 클로즈상태가 아니라 리스닝 상태라든지 뭐 될것이다. 그럼 그 함수 내부에서 다음 상태를 세팅해준다. TCPConnection클래스 내부의 setter를 이용해서 해준다. 위의 상태 클래스에서 TCPConnection의 인스턴스 변수를 가지고 있었던 이유가 바로 여기.

암튼 설명을 엄청 허접하게 하였다. 암튼 전체적인 그림이 이런식으로 흘러간다는 것만 알면 된거같다.
Posted by 정해권 Trackback 0 Comment 0

자~ARM에 대해서 앞으로 쭉쭉 같이 공부 해 나가도록 합시다.
ARM processor에만 치우치기 보다는 전반적인 기본 원리, 배경상식, 등등 기초지식이 없는 사람들도 보고 감이 올수 있도록 같이...왜냐면 나도 ARM을 공부하는 중이므로, 아주 조금;;-_-;;

임베디드에 관심 있는 사람들이라면, 당연히 알고 있어야 할 칩, 썬칩도 아니고 ARM칩에 대해서..나 요새 심히 이상스럽다..


먼저 하드웨어 프로그래밍이다 보니, 어쩔수 없이 C코딩과 더불어 어셈블리 코드가 들어갈 수 밖에 없다. 물론 어셈블리 코드로 모두다 짜야 하는건 아니지만, 최소한 어셈블리 코드를 보고 이해할 수 있어야 한다.

어셈블리 부터 설명하고 싶지만, 사실은 지금 나도 배우는 단계에 있으므로....예를 들면 이러한 코드를 해석 할 수 있어야 한다.

<<ARM ASSEMBLY-C CONNECTION>>

<C code>..........If else statement

int min(int x, int y) {
   if (x < y)
       return x;
   else
       return y;
}

<ASM>

min:
    cmp r0, r1
    movge r0, r1
    bx lr
------------------ or
min:
   cmp r0, r1
   bxlt lr
   mov r0, r1
   bx lr
------------------
여기서---> 첫벗째 3줄짜리 어셈코드와 두번째 4줄짜리 코드에 대해 어떤 코드가 더 좋은코드인가?

<C code>..........While statement

int div2(int x) {
   int count = 0;
   while(x) {
      count++;
      x = x >>1;
    }
  return count;
}

<ASM>

div2:
    mov r3, #0
    cmp r0, #0
    beq .L8

.L9:
    movs r0, r0, asr #1
    add r3, r3, #1
    bne .L9

.L8:
    mov r0, r3
    bx lr
--------------------------

<C Code> .......Do-While

int dowhile(int x, int y) {
   do {
       x = x - y;
   } while( x > y);
   return x;
}

<ASM>

dowhile:
.L41:
    sub r0, r0, r1
    cmp r1, r0
    blt .L41
    bx lr
-----------------------------

<C code>.....For

int div2for(int x) {
   int count;
   for(count = 0; x; count++)
      x = x >>1;
   return count;
}

<ASM>

div2for:
    mov r3, #0
    cmp r0, #0
    beq .L16

.L17:
    movs r0, r0, asr #1
    add r3, r3, #1
    bne .L17

.L16
    mov r0, r3
    bx lr
-------------------------------
 <C code> .......For-CONTINUE

int forcontinue(int x) {
   int count;
   for (count = 0; x; count++) {
       if( x == ocunt)
           continue;
       x  = x -1;
      }
    return count;
}

<ASM>

forcontinue:
   mov r3, #0
   cmp r0, #0
   bxeq lr

.L32:
   cmp r0, r3
   beq .L33
   sub r0, r0, #1

.L33:
   add r3, r3, #1
   cmp r0, #0
   bne .L32

   mov r0, r3
   bx lr
----------------------------
<C code>.......For-Break

int forbreak (int x) {
   int count;
   for (count = 0; x; count++) {
       if (x == 3)
          break;
       x = x -1;
    }
    return count;
}

<ASM>

forbreak:
   mov r3, #0
   cmp r0, #0
   bxeq lr

.L35
   cmp r0, #3
   beq .L36
   
   sub r0, r0, #1

   add r3, r3, #1
   cmp r0, #0
   bne .L35

.L36
   mov r0, r3
   bx lr
-----------------------
<C code >.....Switch

int switchtest(int x) {
   switch (x) {
      case 3:
         return 1; break;
      case 5:  
         return 2; break;
      default:
         return 3;
     }
}

<ASM>

switchtest:
   cmp r0, #3
   beq .L103
   cmp r0, #5
   beq .L105
   b .L110

.L103:
   mov r0, #1
   b .L111

.L105:
   mov r0, #2
   b .L111

.L110:
   mov r0, #3

.L111:
   bx lr
-------------------------
이제는 구조체에 해당하는 코드..

<C code>

Struct {
  int real;
  int imag;
} c;

int i;

int test(int j) {
   c.real = i;
   c.imag = j;
   return i + j;
}

<ASM>

test:
   mov r2, r0
   ldr r3, .L3
   ldr r0, [r3, #0]
   ldr r3, l3+4
   stmia r3, {r0, r2}
   add r0, r0, r2
   bx lr

.L4:
   .align 2

.L3:
   .word i
   .word c
   .size test, .-test
   .comm c, 8, 4
   .comm i, 4, 4
------------------------------
이제는 함수호출에 대한 코드

<C code >

int inc(int i) {
   return i+1;
}

int simple(int i) {
   return i + inc(i);
}

<ASM>

inc:
   add r0, r0, #1
   bx lr

simple:
   stmfd sp!, {r4, lr}
   mov r4, r0
   bl inc
   add r0, r4, r0
   ldmfd sp!, {r4, pc}
----------------------------------
이번엔 배열에 해당하는 코드

<C code>

void swap(int v[], int k) {
   int tmp;
   tmp = v[k];
   v[k] = v[k+1];
   v[k+1] = tmp;
}

<ASM>

swap:
   mov r1, r1, asl #2
   add r2, r1, r0
   ldr ip, [r1, r0]
   ldr r3, [r2, #4]
   str r3, [r1, r0]
   str ip, [r2, #4]
   bx lr
-----------------------------------
자 이번엔 마지막으로다가 포인터

<C code>
clear (int *array, int size) {
   int *p;
   for (p = &array[0]; p < &array[size]; p++)
      *p = 0;
}

<ASM>

clear:
   add r3, r0, r1, asl #2
   cmp r0, r3
   bxcs lr
   mov r2, #0

.L31:
   str r2, [r0], #4
   cmp r0, r3
   bcc .L31
   bx lr
---------------------------------------
---------------------------------------
여기 있는 어셈코드는 ARM용 이다. 뭐 다른거랑 비슷비슷 하겠지만서도 차이점도 분명이 있을것이다(추측). 일단 위에 있는 코드를 해석해 보자.....

일단 ARM칩에는 r0~ r15 까지의 레지스터와 그 이외 cpsr이라는 레지스터도 제공한다.
r13, r14, r15는 거의 관용적으로 (conventional) 용도가 정해져 있다. r13은 스택포인터(sp), r14는 링크레지스터(lr), r15는 프로그램카운터(pc).이다.. 용도야 다들 아실테고 , r14같은 경우는 서브루틴 호출시 리턴어드레스를 저장한다고 생각하면 된다. 보통 우리가 손쉽게 많이 이용하는 레지스터는 r0~ r4까지 많이 쓰는것 같다..(나의 생각임...). 위의 코드를 잘보면, #x 같은게 있는데 저게 맨뒤의 레지스터를 먼저 Arithmetic logic unit에 보내기 전에 Barrel shifter에서 처리 하기 위함인데, ARM 아케텍쳐 자체가 그렇다. 퍼포먼스 땜시... 아 일단 ARM에 관심이 있다면, 책을 사든지, 인터넷에서 매터리얼을 구하기 바란다.  

암튼 당분간 기초를 다뤄야 겠다...왜냐면 내가 아는게 없으므로, 혹시 ARM관련 관심있는사람은 컨택바람..

Posted by 정해권 Trackback 0 Comment 0

처음부터 끝까지 나열식으로 줄줄이 분류해가며 설명하기 보다는 (그럼 재미없소) 각개격파식으로다가
한개씩 야금야금 (음..) 설명할께요, 전혀 사전지식이 없더라도 이해할 수 있게 쉽게 풀어쓸께영..

다만 그림을 그리지 못하니 양해바람
스캔해서 까지 올릴만한 시간이 없어성...
--------------------------------------------------------------------------------------------
0. 시작 하기에 앞서서. [Prelude]
   어느 패턴이건 단독으로 쓰이는 경우 보다는 복합적으로 사용되는 경우가 훨씬 많고, 지금 설명하는 패턴들은 배우는 단계의 전형적인 코드 형식이다. 그러니 실제 프로젝트(바깥세상) 에서는 변종 형태의 디자인을 많이 볼수 있다. 그러나 기본 패턴의 토대를 바탕으로 이루어져 있으므로, 기본을 확실이 다지자.
  패턴 종류에 상관없이 제네랄한 컨셉이 있다. 바로 특정형식, 특정클래스, 특정 콘크리트(concrete)클래스 지정을 지양하고, 상위형식(Super class), 인터페이스(Interface), 추상클래스(Abstract class)쪽의 상위레벨 중심으로 프로그래밍 하는것이다. 이유는 대부분 알고 있을 것이다, 바로 다형성(Polymorphism)을 사용하기 위해서 이다. OOP의 핵심 아닌가!. 또한 디자인패턴하고는 별개된 내용일 수 는 있으나, 요새 트렌트가 Generic class or function 사용이다. Java 5에도 추가되었다. 사실은 C++유저들은 알수 있겠지만, 제네릭 컨셉이 C++의 Template이다. 피상적으로 보면, 기본 핵심 컨셉, 역할은 같으나 디테일하게 보면, 내부 작동원리는 다소 차이가 난다.(Java는 계속 업글되면서 디자인도 최신을 따라가므로) C++보다는 한단계 진보했다고 볼 수 있다. whatever! 여기서 말하고자 하는 바는
특정형식, 특정상황의 구현보다는 점점 general, flexible, robustic 한 코드쪽으로 흘러 가고 있다.
물론 퍼포먼스랑은 상반되는 이야기이지만 말이다. 퍼포먼스만 고집하면, ASM으로 코딩하시든지..

그런 취지에서 시작한 첫번째 맛보기 패턴
1. Strategy pattern. [전략패턴]
  "나의 설명방식, 먼저 적용할 수 있는 사례부터 알아보자."
    a. 게임에 관심있는, 혹은 제작에 관심있는 사람들이 꼭 알아야 할것들
      ㄱ. 게임 캐릭터가 가지고 있을 수 있는 행위들(점프하기, 몸비틀기, 칼던지기, 땅바닥에 자빠지기, 죽은척 하기, 하품하기, 적들 주먹으로 때리는 시늉하기.........곱하기 백만개?) ==> 핵심사항 (행위가 언제든지 나중에 쉽게 추가 할수 있다는 것에 별다섯개): 개발단계에서 이것쯤은 예상하고 플밍하겠지요.
      ㄴ. 게임을 하다가 (피파축구?) 컴퓨터가 나의 플레이 스타일을 간파하고, 나의 스타일(잘하고 못하는지 파악)에 따라서, 컴퓨터 인공지능을 자유자재로 (키포인트) 런타임시 바꿀수 있는 것을 생각해보자. 내가 게임플레이를  잘하는지 간파하고 피파축구게임에서 "님은 플레이를 잘하시는것 같습니다 난이도 올려볼까요?" 라는 식의..
    b. 실제 회사에서 유아용의 교육용프로그램을 개발하였다. 프로그램은 동물들의 울음소리, 동물들의 행위들을 실제로 디스플레이하거나, 들려주기도 하고, 등등등의 그런것이다. 내가 현재 사자의 울음소리를 어르릉으로 지정해 놓았다면, 10년후의 사자가 유전자가 변형되서 야옹야옹 거릴 수도 있는것이다(과연? ㅋㅋ) 혹은 원래 먹이사슬구조에서 본다면, 뱀이 개구리를 먹는다, 허나 개구리중의 대마왕 황소개구리가 나타나 뱀들을 먹어 삼키기 시작했다. 이전에 개발할 때, 처음부터 fixed한 코딩방식으로 구현해 놨다면, 나중에 다 갈아 어퍼야 한다.

위 예뿐만 아니라, 엄청 많을 것이다. 위에서 설명한 것의 공통분모는 바로 가변적인 행위를 디자인 단계에서 독립적인 모듈로, (모듈이라는 용어보다는, 클래스가 낫겠다.(oop니깐)) 만드는 것이다. 이제 왜 전형적인 23가지 패턴중의 한가지인 전략패턴을 공부 해야 하는지 알것 같다.

이제 좀더 디테일 하게 기술적인 이야기 하겠다. (예는 위의 피파축구를 들어서 설명)
 
1. 새로 게임개발에 착수 했다. 박지성의 개발슛, 베컴의 오른발 휘둘러차기슛, 설기현의 역주행, 등등 캐릭터 마다 독특한 행위를 잘 표현해 보려한다. 그러나 게임 출시후에도 계속 버젼업해서 팔아먹어야 한다. 몇년 후에는 박지성이 이영표의 헛다리 집기 기술을 습득할지 누가 알겠느냐. 혹은 어떤선수는 슛 자체를 못한다.(이영표) 고로 캐릭터의 행위를 별도의 클래스로 만들어야 한다. 그 행위를 좀더 세분화 해서 "슛" 의 행위인지, "코너킥" 의 행위인지, "파울" 의 행위인지 디테일하게 세분화 해서 만들면 좀더 oop에 가깝다 할수 있겠다.

일단 예로 허접한 코딩스타일 보여주겠다.

class player {
   int age,
   int height;
   ...
   void shoot() { real coding..}
   void foul() { real coding..}
   void throwing() {...}
   ...
}

class park extends player {
    void shoot() { // 오버라이딩 }
    void foul() { // 오버라이딩 }
}

class kihyun extends player {
    void shoot() { // 오버라이딩 }
    void foul() { // 오버라이딩 }
}

class youngpyo extends player {
    void shoot() { // 에잉 난 슛 못하는데. 누가 나 호출하면 어떻겡 ㅜ.ㅠ 걍 익셉션 던져버려? }
    void foul() { // 오버라이딩 }
}

첫번째 super 클래스 격인 player가 concrete클래스이건 abstract 이건 사실 별반 도움 안된다.
물론 abstract가 물론 저 위의 경우 당연히 낫다고 볼수 있다. 그럼 그렇게 바꾸도록 해봐라.
허나, 플레이어 선수들이 한두명도 아니고, 코드 재사용을 볼수도 없거니와, 나중의 변경할때도 실제 클래스를 변경해야 한다는 위험성을 가지고 있다. 장점보다는 단점이 훠벌나게 많다고 볼수 있다. 그러나 저 위의 코드는 oop입문단계에서 허벌나게 많이 볼수 있는 코드 아닌가? ㅋㅋㅋ 오버로딩의 전형적인 코드이기 때문에, 왓에버

자 이제 저위의 스타일의 문제점을 알고 한단계 업그레이드 버젼을 사용해 보자. (상속방법은 될수 있으면 하지 말자)

위의 player클래스에서 shoot(), foul() 함수가 flexible한 것이기땜시, 별도로 빼야 할것같다는 생각이
머리속에서 용솟움 치고 있다.

그럼 어떻게 해야 할까?

자 이렇게..

class player {
   int age,
   int height;
   ...
   void throwing() {...}
   ...
}

class park extends player {
    void shoot()  // 더이상 오러라이딩 아니에엽!!!
    void foul()  // 오러바이딩 아님 밑에꺼도 마찬가지
}

class kihyun extends player {
    void shoot()
    void foul()
}

class youngpyo extends player {
    void shoot()
    // void foul()  => 헛 실수로 주석처리 해버렸네, 그래도 에러 안나서 몰랐엉(compile time)
                             흑.. (runtime에러때 깨달았지) 조금만 더 빨리 알았더라도...
}

자 여기서 문제점, 머야 이거? 이건 더문제잖아? 선수마다 꼭 shoot()하고 foul()을 가져야 한다는 개런티도 없고, 더 악화됐네?

아 ~ 그래 이럴때 인터페이스 이용해야지...(인터페이스 왈: 내 자식들은 내가 가지고 있는거 만들어 주지 않으면 컴파일 안시킬꺼야!!)

그럼 이렇게...

interface shootable {
  public void shoot();
}

interface foulable {
  public void foul();
}

자 박지성, 이영표, 설기현 아그들아 내 인터페이스 가져다가 써라, 그래야만 내가 너네들이 꼭 저함수를 구현했다는 사실을 믿을 수 있걸랑. ~

so....

class kihyun extends player implement shootable, foulable {
    void shoot()
    void foul()
}

class youngpyo extends player implement foulable {
     //void shoot()  -> 난 슛을 못쏘니, 차라리 날 빼주삼..응 그래서 너 뺐어 ㅋㅋ
     void foul()  //인터페이스를 상속받았으니, 난 무조건 파울만 하라는거야?? 어쩌라구? ㅋㅋ(이영표
                        안티 제작)
}


오~~그래 바로 이거야... yikes~
근데 문제는.....더 심각하다. 코드 재사용은 말할 것도 없거니와 player라는 상위 클래스를 가지고 내가 자식넘들이 가지고 있는 shoot()슛소기 와 foul()파울저지르기 에 접근할 수 없다니...내가 자식넘들을 잘못 키웠구나 (ㅋㅋㅋㅋ -_-;;) (((잠깐 여기서;; 이문제는 정말 커다란 문제다. 다형성의 이점을 살리지 못하는 코드로 바뀔 우려가 있다. shoot()을 호출할 수 있는건 자식넘들 뿐이니 자식을 직접 생산해야 하지 않겠는가...설기현이면 설기현 , 박지성이면 박지성을 내가 손수 코딩으로 , new 박지성() 이렇게 쳐야 하다니...말도 안대.....아니 그럼 너가 new 박지성() 안하고 박지성을 탄생시킬 수 있다는 말이야??? 아 -_- 그건 아니고, 언젠가 당연히 저런 코드야 있어야 겠지, 근데 저런 스페시픽한 코드를 최소한으로 줄이면서 코드 곳곳에 흩어져 있기보다는, 한곳으로 몰고, 최소한으로 줄이고..그래야 나중에 유지보수,버그최소화,확장성, 등등 쉽게쉽게 할수 있으니 ....아 먼말인지 감이온다..소프트웨어설계 란게 이런거였구나? ㅋㅋ)

자 그럼 두번째 버전에는 더 많은 문제점이 있었으므로 세번째 단계로 가보자

아 아무리 생각해봐도, 가변적인 저런 행동하고, 원래 축구선수로서 가지고 있어야 할 다른 행동하고 어떻게 구분한단 말이야...상속을 통해서는 전혀 해결할 수 없었고 말이야.. 두번째 버젼은 자식들만 가변적인 행위를 가지고 있었기 때문에, 부모가 자식의 함수에 접근할 수가 없었고....흠...도무지 모르겠어.
아무래도 gg쳐야 할꺼같애..그때 갑자기 산신령이 나타나서 한마디 왈, 함수를 모두 밖으로 빼버려라. 툭 한마디 내뱉고 사라졌으니.... 아 ...그렇게 하면 될꺼 같은데? 놀구있네..ㅋㅋ 미리 알고있었으면서..ㅡㅡ;;

그래 그럼 어떻게?

별도의 가변적인 행위에 해당하는 클래스를 제작하기에 이르렀다.

물론 인터페이스의 장점을 살려서.. 이렇게

class Shoot_JisungPark implements Shootable {
       public void shoot() { // real coding such as 공이 떼굴떼굴 굴러간다 슛이였으나...
}

class Foul_JisungPark implements Foulable {
       public void foul() { // real coding such as 반칙을 하더라도 얌전하게 반칙을 한다...
}

자 감이 오는가? 그럼 맨 상위 클래스를 다시 가보면..

class player {
   int age,
   int height;
   ...
   void throwing() {...}
   ...
}

자 여기에서 무엇을 추가시켜 줘야 겠는가? (자식들의 가변행위함수에 접근 하기 위해서)...
바...바로 인스턴스 변수만 추가해주면...으윽 이렇게 쉬운것을..
이렇게..

class player {
   int age,
   int height;
   ////////////////// the part added
   Shootable shootBehavior;
   Foulable foulBehavior;
   //////////////////

   ///////////////// the part added
   void performShoot() { shootBehavior.shoot(); }  // 참조변수 객체에 실제 행위 위임
   void performFoul() { foulBehavior.foul(); } //
   /////////////////
   ...
   void throwing() {...}
   ...
}

자 위에 새롭게 추가된 두개의 변수를 가지고 이제 새로운 클래스인스턴스를 가리킬 수 있게 되었다.
저 코드를  살펴 보면, 내가 가지고 있는 넘이 박지성 슛클래스인지, 설기현껀지 신경 안써도 된다.
단지 정해진 인터페이스를 통해서 호출만 하면 끝.

박지성 클래스 코드를 살펴 보면,

class park extends player {
    public park() { // constructor
        shootBehavior = new Shoot_JisungPark();
        foulBehavior = new Foul_JisungPark();
    }
}
요런식으로 ...

아 근데 저기에서도 특정 박지성슛을 할당하고 있네? 특정스페시픽한 클래스 할당 안좋다메? 아~~
물론 맞는말인데, 다음 패턴을 배우고 나서 설명을 해야 하기 땜시 일단은 저걸로 만족하자.

자 그럼 나중에라도, 박지성이 박지성슛만 쏘지 않고, 가끔 설기현 슛도 싸줘야 하고, 아주 100번에 한번꼴로, 베컴슛도 쏴줘야 하기 때문에, 동적으로 재할당 해줘야 하는 메서드만 추가해주면 거의 끝난다.

class player {
   int age,
   int height;
   Shootable shootBehavior;
   Foulable foulBehavior;  
   void performShoot() { shootBehavior.shoot(); }  // 참조변수 객체에 실제 행위 위임
   void performFoul() { foulBehavior.foul(); } //
   ///////////////// this part added now
   void setShootBehavior(Shootable sh) { this.shootBehavior = sh ;}
   void setFoulBehavior(Foulable foul) { this.foulBehavior = foul;}
   /////////////////
   ...
   void throwing() {...}
   ...
}

아 위의 클래스를 abstract로 만드는것이 좋겠군요..좀더 이치에 닿네요.

자 거의 완성이다 그럼 실제 클라이언트에서는 어떻게 호출하는지 알아보자.

public class StrategyPattern {
  public static void main(String[] args) {
     Player parkJiSung = new park();
     parkJiSung.performShoot();  // 박지성슛을 쏘고있습니다...
     .....
     parkJiSung.performFoul(); // 박지성이 상대편을 얌전히 반칙을 저지르는 군요..
     .....
     .....
     // 박지성도 여러번 박지성 슛 쐈으니 가끔 베컴슛도 싸줘야 하지 않겠어?
     parkJiSung.setShootBehavior(new 베컴스타일클래스());
     parkJiSung.performShoot(); // 오 박지성이 베컴스타일 슛을? 오~
     ...
}}
감이 오는가요?
내가 박지성슛클래스, 베컴슛클래스 이렇게 재미를 위해서 클래스 이름을 지었지만,
실제 만들때는, StyleofShoot0, StyleofShoot1...이렇게 해도 될것이고, 저런것들을 미리미리 만들어놔서 컨테이너에 보관해 놓은다음, 아무거나 빼서 실행시켜도 되고, .....
자 이 패턴의 장점은 알았을테고....단점은 없느냐?  패턴세계에서는 단점없는 패턴 없다. 모든지 트레이드오프가 있기마련이라.. 앗 근데 박지성슛 클래스와 박지성 클래스와 의존관계가 없다는 것은 알겠는데(좋은것임), 박지성의 기분상태에 따라 슛의 정확도라든지, 슛의 날카로움이라든지 그런건 어떻게 해야하지? 아 이런것까지 고려해서 당현히 게임만들어야 하는거 아닌가? 당근이죵.. 그건 박지성의 컨디션이란 변수를 가지고(있다고 가정) 그 변수에 따라 행위가 변하는 것을 만들면 되겠네요...(State pattern) 나중에 설명하겠노라.. 일단 이패턴은 여기까지 하고 나중에 다른패턴도 하고 나서 실질적인 세세한 테크닉과 함께..다음기회에..

다른 패턴을 하나하나 살펴 보면서 이런 코드드를 좀더 나이스하게 바꿀수도 있다.
단점에 대해선 다음장에서 설명하겠다.

일단 오늘은 여기까지하고....
그리고 읽어보신 분들은 피드백 부탁해영......

 




 

Posted by 정해권 Trackback 0 Comment 1


티스토리 툴바