Download VCard

© 최병일 1981-‘10

'OOP'에 해당되는 글 11건

  1. 2008/10/01 Jeff Bay의 객체 건강유지법.
  2. 2008/05/19 객체간 소통에 관한 낙서(2)
  3. 2008/05/18 객체 지향 소프트웨어 일주 - 5
  4. 2008/05/08 객체 지향 소프트웨어 일주 - 4
  5. 2008/03/28 객체 지향 소프트웨어 일주 - 3
  6. 2008/03/15 객체 지향 소프트웨어 일주 - 2(2)
  7. 2008/03/11 객체 지향 소프트웨어 일주 - 1
  8. 2008/02/12 AOP와 직교성, 그들의 도구로 RAII와 Proxy의 활용
  9. 2008/01/04 Eiffel 과 DBC.(2)
  10. 2007/05/26 Overloading과 Overriding [Part 2]

Jeff Bay의 객체 건강유지법.

작성자 : yagur rev : 1

  작고 단단한 객체를 작성하기 위해 프로그래머들은 고민을 많이 합니다. 이에 조금은 도움이 될만한 연습방법이 눈에 띄더군요. Thought Works Anthology란 수필 모음집에 객체 건강유지법(Object Calisthenics)이란 흥미로운 수필이 바로 그것이었습니다.

 아래 목록은 잘알려진 7가지 코드 품질에 관련된 항목들입니다.
응집력cohesion, 느슨한 결합loose coupling, 무중복zero duplication, 은닉encapsulation, 테스트 가능성testability, 가독성readability, 집중력focus

 그리고 Jeff Bay는 위의 7가지 코드 품질의 향상을 위한 9가지 실천 항목을 소개하고 있습니다.

 1. 메소드당 한 단계 깊이 이상의 들여쓰기를 하지 않는다.
 2. else 키워드를 사용하지 않는다.
 3. 모든 원형primitive과 문자열을 래핑Wrapping한다.
 4. 한 라인당 하나의 점(.)을 사용한다.
 5. 약어를 쓰지 않는다.
 6. 엔티티를 작게 유지 한다.
 7. 두개 이상의 인스턴스 변수를 가진 객체는 사용하지 않는다.
 8. 일급 객체 모음을 사용한다.
 9. Getters/Setters/properties를 사용하지 않는다.

 제 해석이 틀릴수도 있으니, 원문을 참조 하시고 싶은분은 아래 more를 누르세요.

more..


  일단 이 9가지 실천항목은 Jeff가 제안하는 객체 건강유지법입니다. 규칙과 달리 꼭 지켜야할 강제성이 없는 원칙과 비슷하지만, 그 강제성이 좀더 약한 수준이라고 보고있습니다. 그는 간단한 1000 라인 크기의 프로젝트를 만들어 연습을 시작하되, 위의 실천항목 9가지를 매우 엄격하게 적용하는것을 조건으로 내걸고 있습니다. 그리고 이 건강유지법을 가이드라인 정도로 활용하라고 그는 말하고있습니다. 다시 한번 본다면 몇가지 실천항목은 그다지 새롭지 않은것들도 있습니다.

1. 메소드당 한 단계 깊이 이상의 들여쓰기를 하지 않는다.

 메소드 크기가 클경우 보통 응집도가 떨어지는 경우가 많습니다. 분리해야할 시점에서 분리하지 않고, 관련없는것이 계속 더해져 비대해지는 경우입니다. 이를 피하는 좋은 방법은 작게 작성하는 것입니다. 작게 작성하려다 보면 해당 문제 영역안에서 해결하려는 경향이 생겨납니다. Jeff는 작게 작성하는 버릇을 들이기 위해 한 메소드를 5줄 이내로 작성하라고 권합니다. 물론 이는 그의 건강유지법에 해당하며, 실무에서 모든 메소드를 5줄 이내로 작성하는것은 불가능에 가깝다고 생각합니다. 하지만 좋은 연습이 될것입니다.
  5줄이내로 작성하는것이 들여쓰기와 무슨 상관이 있는가 반문하시는분도 계실것입니다, 적은 줄수로 작성하려면 일단 들여쓰기를 줄일수 밖에 없습니다. 또한 조건 분기로 인한 들여쓰기가 많아지는 경우 CC가 올라가고 높은 CC는 테스트 가능성testability을 떨어트립니다.

2. else 키워드를 사용하지 않는다.

 if-else 문은 조건에 따라 선택적인 수행을 하기 위한 것입니다. 경우에 따라 switch문을 사용할수도 있습니다.

선행 조건문의 예>
if(condition)
{
    DontDoIt();
}
else
{
    DoPreprocess();
    Process();
}
위의 것은 아래와 같이 고칠수 있습니다.
if(condition)
{
    DonDoIt();
    return;
}

DoPreprocess();
Process();
 다른책에서도 너무나 많이 봐온 방법입니다.

전략적 조건문의 예>
if(conditionA)
{
    WriteLog();
    return false;
}
else if(conditionB)
{
    DoSomethingByStrategyA();
    return true;
}
else if(conditionC)
{
    DoSomethingByStrategyB();
    return true;
}
else
{
    WriteLog();
    return false;
}
  혹은 Switch문을 통해 저런 선택을 했을수도 있습니다. 이런경우엔 StrategyPattern을 통해 구조를 개선해볼수 있습니다.
사용자 삽입 이미지



3. 모든 원형
primitive과 문자열을 래핑Wrapping한다.

 "Where everything's an object"라는 Ruby와 같은 언어와 달리 Java나 C++과 같은 언어들의 원형primitive은 객체가 아닙니다. 정수, 실수, 문자열과 같은 것들은 객체로서 접근하지 않습니다.

  Ruby

returnedNumber = -42.abs
   Java
returnedNumber = Math.abs(-42);

  극단적인 예였지만, 가독성면에서 본다면 원형을 객체로 다루는것은 컴파일러와 사람에게 좀더 의미있는 형을 부여하는 셈이 됩니다. 원형인 정수(int)로서의 dollar(미 화폐단위)보다, 객체로서 다루작성된다면 좀더 유지보수성이 높은 코드가 됩니다. 저 dollar를 pound로 환산하려면 어떻게 해야할까요? int 라면 getter를 이용해 값을 받아온후 환율을 곱해 pound에 집어넣어야 할것입니다. 이럴 경우 클래스로 화폐를 표현하였다면, 좀더 유연하고 유지보수성과 확장성을 지니게 된다고 생각합니다. 물론 이는 명세와 관련이 깊습니다. 필요 없는 구현을 유연성을 위해 구현한 꼴이니까요. 하지만 이 연습은 작은 객체들로 작성함으로서 어떤것을 객체로 분류하는것이 좋을지를 판단해보는 좋은 연습이 되리라생각합니다.
   분해를 해봄으로서 좋은 연습이 되지만, 과도한 객체화로 인해 응집력을 잃어버려서는 않됩니다. 예를 들어 3D vector를 표현할때 vector의 float x, y, z를 멤버로 지니고 있는것은 딱 적당한 수준입니다. 하지만 x, y, z조차 객체로 분해해 버린다면 제가 보기엔 과도한 분해라고 생각합니다. 하지만 적당히 일반화 시킬순 있습니다.
template <typename T>
class vector3
{
    ....
    T x;
    T y;
    T z;
};
  멤버의 유연성을 위해 객체화 시키는 방법도 있지만, 과도한 분해를 피하기위한 방법중엔 일반화란 방법도 존재합니다. 멤버 타입 변경이 필요하고, 동일한 행위를 유지시킨다면 일반화 선택도 그리 나쁘지 않습니다. 이 연습엔 일반화 연습을 포함시키는것도 유익할듯 합니다.

4. 한 라인당 하나의 점(.)을 사용한다.
  일단 . 이나 ->가 여러개 거치고 있다면 캡슐화를 위배하는지 의문을 품어봐야합니다. 참조에 참조를 거듭한다는것은, 참조자가 너무 깊은 관여를 하고 있다는 느낌을 지울수 없습니다. 캡슐화 하고 행위를 요청합시다.

5. 약어를 쓰지 않는다.
  두말 할것 없습니다. 동료의 코드가 암호화 되기 원하시는건 아니겠죠.

6. 엔티티를 작게 유지 한다.
  이것 역시 작게(응집력있게) 작성하기의 또다른 연습입니다. 50줄 넘이 넘지 않는 클래스, 열개의 파일이 넘지 않는 패키지가 목표입니다.(저자는 자바를 기준으로 이야기하고있으니 C++의 경우는 헤더도 있고 하니, 줄수와 파일수를 2.5배정도 하면 적당할듯 합니다.). Jeff의 내용을 요약하자면, 길이가 길어지면 SRP를 위배하게 될 확률이 높다입니다. 그리고 객체의 길이가 길면 보통 책임의 양도 증가합니다. 객체를 이해하기에도 노력이 더 필요하고, 여러가지 기능이 복합적으로 모여있다면 재사용시에도 문제가 될 소지가 있습니다.

7. 두개 이상의 인스턴스 변수를 가진 객체는 사용하지 않는다.
  제목만 보고 화가 날수도 있습니다. 하지만 이건 연습입니다. 일단 이 연습의 의도는 분해Decomposition입니다.
응집력을 떨어트릴수 있는것들을 분해해서 그 책임을 분리해버리는 것입니다. 예를 들면 컨터에너에 들어갈수 있는것을 장황하게 멤버로 나열하였거나, 의존성을 역전해야할부분을 결합해버린 경우를 분해하는 것입니다. 멤버가 줄수록 그럴 확률이 높아집니다. 의도적으로 SRP를 실천하는것입니다. 원칙은 필요에 의해 적용해야 하지만, 적용하는것도 연습이 필요합니다. 그를 위한 연습인것으로 보입니다.

8. 일급 객체 모음을 사용한다.
  원형을 래핑해서 쓰는것과는 조금 다른 연습입니다. 원형 배열이나 동종 객체들의 모음(예> container에 담긴 개체들)을 지니고 있는 경우, 이들을 위한 행위만을 갖는 객체를 작성하는 연습입니다. 다른 멤버 변수를 사용하지 않고 저 응집체에만 집중하는 것입니다. C++에서 찾아볼수 있는 예로는 STL의 container 객체들이 아닐지.. 물론 일반화된 컨테이너 객체라, 지극히 일반화된 행위들만을 모아놓았지만 말이죠. 그 컨테이너를 멤버로 채용하는 객체는 일단 컨테이너에 관련된 멤버함수들만을 내부적으로 갖고있습니다. 컨테이너의 원소에 요구하거나 행해지는 행위들만을 모은 객체를 작성하는 연습입니다. SRP(Single Responsibilty Principle)에도 부합합니다. 많은 멤버를 갖는다는 것은 그 만큼 클래스가 책임질것이 많다는것을 뜻하니까요.
  이 연습 역시 또다른 작은 객체 작성하기 연습입니다.

9. Getters/Setters/properties를 사용하지 않는다.
  일단 getter/setter가 많다면, 캡슐화를 한 의미가 줄어듭니다. 거기다 객체에 행위를 요청하면 될것을 멤버를 참조해서 별도의 로직을 만들기 때문에, 아주 다양한 오류에 직면하게 됩니다. 또한 그 책임은 참조자인지, 제공자인지 구분하기도 힘들며, 스파게티가 되는것은 순식이라 생각합니다. "Tell, Don't Ask"라고 Jeff의 마지막 문장에 쓰여있군요. 캡슐화, 책임 지정의 좋은 연습이 될것같습니다.

'개발' 카테고리의 다른 글

Jeff Bay의 객체 건강유지법.  (0) 2008/10/01
이럴때 테스트 셋을 만든 보람이 있습니다.  (0) 2008/04/12
Eiffel 과 DBC.  (2) 2008/01/04
Overloading과 Overriding [Part 2]  (0) 2007/05/26
Overloading과 Overriding [Part 1]  (0) 2007/05/22
Unicode 2  (1) 2006/08/11
Tag
card.ly
Top
Comment 0 Trackback 1

Trackback : http://yagur.impon.net/trackback/141 관련글 쓰기

  1. 강성희의 생각

    paranoiase's me2DAY | 2008/10/01 15:20 delete

    Yagur의 Juggling - Jeff Bay의 객체 건강유지법 - '두개 이상의 인스턴스 변수를 가진 객체는 사용하지 않는다.' ㄷㄷㄷㄷ

객체간 소통에 관한 낙서

작성자: yagur Rev : 1

 두명의 사람이 대화를 한다면...
사용자 삽입 이미지


세명의 사람이 대화를 한다면...
사용자 삽입 이미지

열명의 사람이 서로 전부 정보를 전달하게 된다면...

사용자 삽입 이미지

이 그룹에 한명이 정보를 바꾸면 어떤 상황이 벌어질까? 전부 변경된 정보를 얻을 확률은? 정보의 변경 영향력을 추측하기 어려울것이다.
이들의 중간 매개체가 존재한다면 어떠할까?

사용자 삽입 이미지

 예를 들면 위키 같은것....
 하지만 구성원이 위키를 확인하기전까지, 구성원에게 정보가 갱신이 되지 않을수도 있다. 그래서 이들 구성원은 때론.... 탑다운 형태를 지니기도 한다.
사용자 삽입 이미지
  이들은 모두 역활로 정의된 것들을 실체화 하는 구성원들이다. 모두 대체될수 있는것이 이상적일것이며, 저들의 정보 소통경로의 효율성에 따라 해당 그룹의 품질은 평가 될수 있다.
  지금 작성하는 객체들은 어떠할까? 모두가 모두를 알아야 할정도로 정보 소통의 경로가 복잡하지 않은가? 정보 공개의 제한을 어디까지 해야 객체간의 효율적인 정보 교환이 이루어질까...
 
Tag
card.ly
Top
Comment 2 Trackback 0
  1. 황상철 2008/07/11 00:18 address edit & delete reply

    이야 참 재밌네요. 사람을 더 추가하게는 프로젝트를 도와주는게 아니라는 말이 생각납니다. ^^

    • yagur 2008/07/12 23:20 address edit & delete

      존재 목적 그리고 정보 소통 방법과 소통량이 인적자원 혹은 객체자원에게 중요한것 같습니다. 구멍난 배에 사람을 더 태우는면 더 빨리 가라앉을것 같습니다. =)

Trackback : http://yagur.impon.net/trackback/140 관련글 쓰기

객체 지향 소프트웨어 일주 - 5


작성자: yagur Rev : 2

Around Object Oriented Software - 5

  은닉(Encapsulation, Information Hiding)

 정보 은닉에 관해서는 파나스가 옳았고 저자가 틀렸다. 저자는 7장에서 파나스의 개념을 '참사로 이끄는 처방'이라고 보고 배제했다. 그 때는 파나스가 옳았고, 저자가 틀렸다. 하지만 저자는  이제 객체 지향 프로그래밍에 흔하게 구현되는 정보 은닉이 소프트웨어 설계 수준을 높이는 유일한 길이라고 확신한다.
- 프레더릭 브룩스

  모듈 혹은 객체의 은닉은 소프트웨어에서 매우 유용한 개념으로 자리잡고 있다. '감추다'라는 은닉의 사전적 의미를 그대로 받아들이면 된다. 감추어서 얻는 이득은 무엇일까? 활용의 시각(응용 영역)에서 보았을때 은닉은 생산성의 향상과 변경 영향력의 감소를 가져온다. 자동차에서 이 은닉의 적절한 예를 볼수 있다.

사용자 삽입 이미지
  좌측은 수동기어, 우측은 자동기어

  '수동 기어'와 '자동 기어'의 차이점은 무엇일까? 자동 기어는 사용자로 하여금 주행시 기어 조작의 기능을 은닉해버렸다. 덕분에 아래와 같이 인터페이스가 작아졌다.

사용자 삽입 이미지
좌측은 수동기어 차량의 패달, 우측은 자동기어 차량의 패달

  자동차량을 몰게 되면 차량 운행의 본 목적에 좀더 집중할수 있다. 왼쪽 발을 쉬게 하고, 주행시 기어 변경을 할 필요가 없으므로 오른손으로는 다른 행동을 취할수 있다. 신경 쓸것이 적어 졌으므로 차량 응용(운전, 길찾기, 주변상황에 따른 방어운전 등) 자체에 좀더 집중할수 있게 되는것이다. 그리고 차량 운행에 필요한 학습량을 줄일수 있다. RPM과 속도에 따라 어떤 기어를 선택해야하는지, 그리고 클러치의 사용방법과 같은 것은 이제 필요 없어졌다.
  기능이 은닉된 자동 기어 탑제 차량의 판매량이 수동 기어 탑제 차량의 판매량을 월등하게 압도하고 있다. 이를 보았을때 사용자는 자동 기어 차량(기능 은닉이 이루어진 제품)을 선호(생산성 향상의 이유)한다고 볼수있다.
  작은 인터페이스는 은닉과 관련이 있다. 인터페이스가 작아짐으로서 사용자는 응용 대상과의 정보 소통량이 줄어들기 때문이다.
  이전 글의 마무리 부분에 은닉의 방법에 따라 다른 시점에서 구성력을 다룰수 있다고 끝맺음 하고있다. 의존 대상와 연관되어있는 모듈(혹 객체)들을 은닉한다면 사용자는 모듈의 독립성을 느끼기 때문이다. 포도송이 들여오기(전글의 예)의 시각화된 그림을 빙산으로 생각해볼수 있다.

사용자 삽입 이미지
포도송이를 빙산으로

  수면위의 모듈은 사용자에게 공개된 공개 부분이고, 수면아래의 모듈들은 의존성을 맺고 있는 은닉된 모듈들이다. 하지만 사용자는 이 정보들이 은닉되어 있기 때문에 공개된 모듈의 인터페이스에 의존하면된다. 빙산의 일각만을 보면 되기때문에 작은 인터페이스의 효과를 갖게 된다.
  객체를 대상으로 바라 보았을때 C++에 pimpl idiom이란 아주 간단한 예가 있다.

class Engine;
class Gear
{
public:
    void SetGear(...);
private:
    Engine* m_pEngine;
};
 pimpl idiom의 예

  위의 소스에서 Engine을 멤버로 갖고 있지만 그것이 무엇인지 사용자는 몰라도 된다. Gear를 변경하면 Gear의 구현부인 소스만이 Engine 객체에 의존성을 둔다. 사용자는 헤더에서 빙산의 일각 즉 Engine이 Class이다 란것만 알면 된다. 그리고 Gear가 어떻게 Engine에 영향을 미치는지는 은닉된다.
  여기서 좀더 은닉하기 위해 의존성 줄이기Depedency Break를 시도할수 있다. Gear를 Interface로 분리해 공개 영역으로 노출하고 구현은 은닉하는것이다.

사용자에게 노출되는 인터페이스
class GearInterface
{
public:
    void  SetGear(...) abstract;
};

은닉되는 구현부
class Engine;
class ConcreteGear : public GearInterface
{
public:
    void  SetGear(...) override;
protected:
    Engine* m_pEngine;
};

  사용자는 공개 노출되는 인터페이스만으로 접근하기 때문에 Engine을 알필요가 없다. Engine 객체는 수면아래로 은닉되는것이라 할수 있다. pimpl 관용구를 통해 구현부안에서도 의존성을 줄이고 있고, 사용자의 응용 영역에 노출되는것은 인터페이스이기때문에 구현을 또한번 은닉하게 된다.
   이 은닉과정을 객체뿐 아니라 이들이 응집된 결과물인 모듈에도 같은 원리로 적용될수 있다. 무엇을 노출하고 무엇을 은닉할지는 결코 쉬운 문제가 아니란것은 아쉬운 부분이다. 하지만 일반화와 추상화를 통해 작은 인터페이스를 만들고, 구현을 은닉함으로서 해당 모듈의 교체를 쉽게 하는것은 모듈의 독립성, 구성력에 큰 도움이 되리라 믿어 의심치 않는다.

'개발 > 수필' 카테고리의 다른 글

테스트와 장인 - 10  (0) 2008/09/30
테스트와 장인 - 9  (0) 2008/07/12
객체 지향 소프트웨어 일주 - 5  (0) 2008/05/18
객체 지향 소프트웨어 일주 - 4  (0) 2008/05/08
객체 지향 소프트웨어 일주 - 3  (0) 2008/03/28
테스트와 장인 - 8  (0) 2008/03/19
Tag
card.ly
Top
Comment 0 Trackback 0

Trackback : http://yagur.impon.net/trackback/139 관련글 쓰기

객체 지향 소프트웨어 일주 - 4


작성자: yagur Rev  : 1

 Around Object Oriented Software - 4

  1.모듈성(Modularity)

  객체 지향적인 소프트웨어를 작성하는 커다란 이유 중 하나는 재사용성Reusability과 확장성Extendibility이다. 굳이 객체 지향 개발에 국한시키지 않아도 재사용과 확장은 소프트웨어 개발에 중요한 의미를 지니고 있다. 의도적이던 비의도적이던 어플리케이션 제작을 확장이란 관점에서 보면, 우린 수직적인 추상도를 아래와 같이 그릴수 있다.

사용자 삽입 이미지
어플리케이션 수직적 추상도

  위에서 밑으로 의존성Dependency을 지니고, 아래에서 위로 확장Extending된다 할수 있다. 이들 확장 과정에 생긴 라이브러리들은 일종의 모듈이고, 목적이나 명세에 맞게 그 기능들이 응집되어있다. 그렇다면 이 응집력Cohesiveness의 기준을 무엇에 둘것인지 생각해볼수 있다. 고려해야할 것들은 굉장히 많다. 공통적 특성, 명세, 프로젝트 상황등등 수도 없이 많은 이론과 시공간적인 조건들이 존재한다. 아쉽게도 명쾌하게 이들을 구분하고 구성하는것을 쉬운것과 어려운것으로 분류 해야한다면, 어려운쪽에 속할것이다.
  객체지향을 실현하는 것은 객체란 응집 대상을 디자인하는것이고, 또 이들이 응집되어 파생된 결과물을 모듈화하는것이다. 모듈성을 지니기 위해 여러가지 사항들을 고려해볼수있다.

   1-1 모듈의 독립성(Modular Independability)

  어떠한 바보라도 일을 더 크고, 훨씬 복잡하고, 격렬하게 만들 수 있네. 반대 방향으로 돌리기 위해선 천재의 손길과 엄청난 용기가 필요하지.
- 존 드라이든

  지금 생산중인 어플리케이션은 모듈화 되지 않고, 복잡하게 얽히고 설킨 한그릇의 스파게티인가? 나중에 가서 이들의 응집성을 고려해 모듈화하려하면 어떤 상황이 올지 상상해보자. 이 상상이 즐겁다면 작업물의 생산과정이 타의 모범이 될만한것이고, 이 과정이 괴롭다면 '천재의 손길'과 '엄청난 용기'의 필요성을 느끼기 시작한것이다. Unix의 철학인 KISS(Keep it simple s....)를 항상 염두하고 모듈화한다면, '용기'와 '천재의 손길'의 필요 횟수를 줄일수 있을것이다.
  일부를 수정하기 위해 작업을 할때 변경의 여파를 고려해보자. 이는 모듈화에 필요한 응집성의 기준을 정립하는데 적지 않은 도움이 된다. 이는 모듈의 독립성과 관련이 있는데, 서로 다른 두 모듈은 서로의 변경으로부터 자유로워야 한다는 것이다. 이 글의 수직추상도의 하부에 위치하는 레이어들은 주로 그런 특성을 지니고 있다. 하지만 많은 모듈은 다른 모듈에 연관성을 지니고 있으며, 이는 상위 레이어로 갈수록 더욱 그러하다.
  프로그램 모듈은 언어의 연관성의 세계에서 주로 확장되기 때문에 독립성을 너무 중시하면 생산의 효율이 떨어질수도 있다. 모듈의 위치를 잘분석하고 독립성이란 특징을 잘 해석해 적용해야할것이다.

사용자 삽입 이미지
연쇄 반응Chain Reaction

  위의 그림은 연쇄 반응에 관한 그림이다. 하나에 변경을 주었을때 연쇄적으로 다른것이 영향을 주게 되는것을 그림에서 볼수 있다. 모듈의 연관성이 수직적으로 이루어졌을때, 그 레이어가 높은곳에 있을수록 하위 레벨에 여러 레이어에 의존하고 있을 가능성이 크다. 이 경우 하위 레이어(혹은 모듈)의 변경이 있을경우 상위 레벨 레이어(혹은 모듈)들은 연쇄적으로 오동작하는 반응을 보인다.

사용자 삽입 이미지
변경 연쇄 효과 영향력

  이때 수평적인 관계에 있는 모듈(레이어 깊이 레벨이 같은경우)들이 연관되어 있다면 상하개념이 아닌 수평적인 위치에 있는 모듈들까지 이 연쇄 반응에 휘말리게 된다. 나비효과Butterfly Effect를 벗어나려면 수평적 위치(같은 레벨의 레이어)에 속한 모듈들은 서로간의 의존성을 끊고 서로간에 독립성을 보장해주는것이 좋다. 그렇다면 이들은 서로 변경의 영향력에서 벗어나, 변경의 연쇄 반응에서 벗어날수 있다.

사용자 삽입 이미지
좌측은 동일 레이어의 모듈간 연관성이 있는 경우, 우측은 독립성이 보장되는경우

  최악의 연쇄반응을 잃으키는 방법들중 몇가지를 소개하자면 소프트웨어 동작 플랫폼 교체, 언어의 컴파일러 버젼을 옛날것으로 바꾸기 등이 있다. 개발자에겐 핵폭팔과 같은 연쇄반응을 잃으키지 않을까? 이 독립성 보장은 모듈의 분해력, 구성력과 관련이 있다.



  1-2 모듈의 분해력(Modular Decomposability)

  거대한 모듈 혹은 여러 기능이 응집된 모듈은 공통의 문제 영역을 위해 응집되어있다. 이들은 더 작은 하위 문제 영역(subproblem domain)으로 분해될수 있다.

사용자 삽입 이미지
모듈 분해의 시각화

 보라색 원모듈이고 원을 이어주는 하늘색 선의존 관계를 나타낸다.

  너무 많은 분해는 의존성 복잡도를 증가시켜 관리하는데 어려움을 주지만, 적절한 분해는 재사용성을 증대시키고 변경 영향력의 확산을 줄여줄수 있다. 이때 고려해야할 사항은 모듈의 레이어상의 위치, 그리고 이들의 의존성 관계이다. 의존성은 최대한 줄여 독립성을 최대한 보장하고, 그 연결고리를 명확히 해야한다. 그래야 책임과 분배가 더욱 쉬워진다.
  명확한 의존성 정의에 관한 예를 하나 들어 보자. "이 수학 라이브러리를 업데이트 하려면 인텔 컴파일러가 필요하다. 하지만 컴파일된 결과물은 비주얼 C++에서도 링크해 사용할수 있다." 라는 의존성 정의는 사용자나 개발자가 절대 알아야할 의존 정보이다. 의존성이 명확하기 때문에 사용자는 인텔컴파일러를 써서 컴파일하고, 응용프로그램 제작시엔 비주얼 스튜디오에서 링크해 개발을 진행할수있다. 이런 사소한 컴파일 지침도 의존성이 존재한다. 복잡하고 엔트리가 많은 소프트웨어 모듈은 더욱 명확한 의존성 정의가 필요할것이며, 간결하고 그 수가 적을수록 구성Compose에 효율성을 띌것이다.
  분해력을 지니되, 역으로 분해된것이 어떻게 구성되어질지를 염두한다면 좋은 모듈의 구조를 이루는 첫발이 될것이다.


  1-3 모듈의 구성력(Modular Composability)

  모듈은 문제 영역에 따라 구분된 재사용 가능한 집합체이다. 이 모듈을 재사용해 하나의 시스템을 구성하지 못한다면 재사용성이 매우 떨어진다고 볼수있다. 그리고 이는 모듈이라 부르기에 부끄럽다.

사용자 삽입 이미지
포도송이 들여오기(응용 프로그램과 다른 영역의 모듈간의 링크)

   위의 그림은 응용 프로그램에서 하나의 모듈을 링크하려했는데 포도송이 같이 다른 모듈이 줄줄히 따라오는 경우이다. 이것이 잘된 구성일까? 과도한 분해와 잘못된 의존 관계 정립은 저런 결과를 가져올수도 있다. 필요에 의해 필요한것만 의존성을 지니게 하는것은 모듈의 독립성에 따라 좌우된다 할수 있다.

사용자 삽입 이미지
독립성이 보장된 모듈들의 링크

  독립성을 어느정도 지닌 모듈이라 하면 의존성을 아껴쓴 모듈을 말한다. 이들은 응용 영역에서 링크를 해도 다른것에 의존성이 생기거나, 원치 않는 기능이 딸려 올 확률이 적다. 하지만 지나치게 독립성을 강조하게 되면
기능의 중복이 생길 우려가 있다. 공통적이 기능은 다른 모듈에 의존하여 해결하면 되는데, 독립성 보장이란 이유로 기능을 중복해버리고 다른 모듈로의 의존관계를 끊어버리는 경우가 생긴다. 독립성과 연관성의 복잡함사이에 적절한 트레이드 오프는 필요하다고 보여진다.
  독립성을 최대한 살린 모듈들의 구성력이 좀더 낳으며, 변경의 영향력에 좀더 안전하다. 이는 모듈 자체의 엔트리가 대부분 공개된 경우이다. 하지만 모듈의 정보의 은닉을 어떻게 할지에 따라 이 기준은 또 다른 시점에서 다뤄질수 있다.

'개발 > 수필' 카테고리의 다른 글

테스트와 장인 - 9  (0) 2008/07/12
객체 지향 소프트웨어 일주 - 5  (0) 2008/05/18
객체 지향 소프트웨어 일주 - 4  (0) 2008/05/08
객체 지향 소프트웨어 일주 - 3  (0) 2008/03/28
테스트와 장인 - 8  (0) 2008/03/19
객체 지향 소프트웨어 일주 - 2  (2) 2008/03/15
Tag
card.ly
Top
Comment 0 Trackback 0

Trackback : http://yagur.impon.net/trackback/137 관련글 쓰기

객체 지향 소프트웨어 일주 - 3

작성자: yagur Rev : 1

 Around Object Oriented Software - 3

 다형성(Polymorphism)

  Poly는 '많은(多)'이란 뜻을 가지고 있고 morph는 '형(形)'이란 뜻을 가지고 있다. polymorph는 다형으로 번역되고 있으며 polymorphism은 다형성을 뜻하고 있다. 다형성이란 무엇일까? 다수의 형을 가진 성질이라고 해석하면 되지만 다수의 형을 갖는것이 어떤 것인지는 예를 보기전엔 알기 어려운 단어이다.

사용자 삽입 이미지
미녀와 야수

  미녀와 야수에서 야수는 저주에 걸린 왕자이다. '그'는 저주를 받은 인간으로 야수의 모습을 하게 되었다. 우리가 소설속 남자 주인공을 지칭하는 '왕자'란 추상적 객체는 시간에 따라 여러 가지 형을 지니게 된다. 저주 받기 전인 과거에는 인간으로서 '왕자'였고, 저주 받은 상태의 현재엔 야수의 형태를 지닌 '왕자'이다. 또 공주를 만나 시간이 흐른 미래에는, 저주가 풀리면서 다시 인간의 형태를 지닌 '왕자'가 된다.
  컴퓨터 언어의 코드 플로우에도 과거와 현재, 그리고 미래를 지니고 있다. 그리고 '그'(왕자)를 코드로 재미 삼아 표현해 본다.

미녀와 야수 코드 샘플
Queen* pQueen = g_pActorFactory->CreateQueen();
King* pKing = g_pActorFactory->CreateKing();
Prince* pPrince = pKing->CreateBabyWith(pQueen); // Birth of the prince

Actor* pHe = pPrince;
pHe->LiveLifeFor("SomeYears"); // #1

Wizard* pWizard = g_pActorFactory->CreateWizard();
MagicSpell* pMagicSpell = pWizard->CreateMagicSpell("MetamorphToBeast");

pHe = pMagicSpell->CastTo(pHe); // cursed
pHe->LiveLifeFor("SomeYears");   // #2

Princess* pPrincess = g_pActorFactory->CreatePrincess();
MagicEffect* pUndoEffect = pPrincess->CreateMagicEffect("Kiss");

pHe = pUndoEffect->AffectTo(pHe); // Princess kisses the prince.
pPricess->Speaks("I love you");
pHe->Speaks("Will you marry me?"); // #3

Priest* pPriest = g_pActorFactory->CreatePriest();
pPriest->AnnounceMarriage(pHe, pPrincess); // Marriage between the princess and prince.
Life* pLifeProcess = g_pLifeFactory->CreateHappilyEverAfter();
pLifeProcess->Add(pHe);
pLifeProcess->Add(pPrincess);

 #1에서 그(' pHe ')는 인간이다.
 #2에서 그는 야수이다.
 #3에서 그는 인간이다.

  pHe 개체의 객체인 Actor는 프로그램의 흐름에 따라 다형성을 지니는 것을 알수 있다. 역활의 추상화Abstraction으로 만들어진 Actor 객체는 대부분의 역활을 지칭하는데 쓰일수 있으며, 다형성을 지니고 있다. 이것이 주는 이점중 하나는 은닉Encapsulation이다. 사용자는 실체가 무엇인지 몰라도 추상 객체를 통해 흐름을 진행시킬수 있다. 위와 같이 추상화를 통해 실현된 다형성은 상속을 기반으로 한것이다.


 행위 기반 다형성(Behavior based polymorphism)

  정적 타입 언어는 상속 관계를 통한 다형성을 구현하지만 다른 방식의 다형성도 존재한다. 행위만 동일하다면 관계에 상관없이 다형성을 지니게 되는 경우이다. 이 행위 기반 다형성은 동적 타입 언어와 정적 타입 언어에서 모두 나타나지만 차이점이 존재한다. 그 차이점은 언제 다형성을 지니는것인가 이다.
  정적 타입 언어의 특징상 행위 기반 다형성을 컴파일 타임에 템플릿을 통해 지원한다. 동적 타입 언어는 형 변경의 제약이 없기 때문에 실행시간에 해당 다형성을 지원하고 있다. 동적 타입 언어에서 실현되는 행위 기반 다형성을 Duck-typing이라고 한다.
사용자 삽입 이미지
오리처럼 걷고, 오리처럼 소리낸다면, 난 그것을 오리라 부르겠다.
아래는 python의 duck-typing 예이다.

more..

  하지만 Duck-Typing 역시 약속된 행위를 객체가 지니고 있어야 다형성을 지닐수 있다. 상속을 배제하고 행위를 런타임중에 확인할수 있는 언어의 특징상 생긴 기능이라 볼수 있다. 이는 정적 언어와의 성능차이로 이어진다. 양쪽 타이핑 모두 일장일단을 지니고 있다.
 C++에서 duck typing과 상속의 차이는 C++의 정적 타입 시스템 때문에 생긴다. 그리고 우리는 C++ 프로그램이 빠르게 동작하것으로 그 비용의 일부를 지불하고 있다.
- Andrew Koenig and Barbara E. Moo(AT&T)

  이렇듯 여러 객체 지향 언어에서 다형성은 매우 중요한 추상화의 실제 장점으로 여겨지고 있다. 현제 객체지향 패러다임은 추상화를 매우 중요히 다루고 있다. 다형성은 모듈화와 재사용성에 큰 영향을 미치고 있으며 필수 테크닉이 되었다.

'개발 > 수필' 카테고리의 다른 글

객체 지향 소프트웨어 일주 - 5  (0) 2008/05/18
객체 지향 소프트웨어 일주 - 4  (0) 2008/05/08
객체 지향 소프트웨어 일주 - 3  (0) 2008/03/28
테스트와 장인 - 8  (0) 2008/03/19
객체 지향 소프트웨어 일주 - 2  (2) 2008/03/15
테스트와 장인 - 7  (0) 2008/03/12
Tag
card.ly
Top
Comment 0 Trackback 0

Trackback : http://yagur.impon.net/trackback/134 관련글 쓰기

객체 지향 소프트웨어 일주 - 2

작성자: yagur Rev : 2

 Around Object Oriented Software - 2

 2. 추상

컴퓨터 과학은 추상화의 과학이다. - A. Aho and J. Ullman


 추상화(Abstraction)

  프로그래밍을 접해본사람은 '추상'이란 단어를 아마 수백 수천번은 보고, 듣고 했을것이다. 이 추상화Abstraction란 기술은 객체 지향 프로그래밍에서 중요한 여러 기반 기술 중 하나이다.
  우리가 자주 하는 추상화는 말속에 들어있다. "이것" "저것"과 같이 대상을 지칭을 하거나, "과일" "동물"과 같이 공통점에 의해 개체Instance들을 객체Class로 분류 하거나, 숫자나 기호와 같이 약속된 의미를 두기도 한다. 이들은 모두 구체화되지 않고 그 자체만으로는 '개체화Instantiation가 불가' 하다는 공통점을 가지고 있다. 예를들어 "과일"이란 객체만으로 개체를 만들기엔 구체성이 부족하다. '내가 먹을 책상위의 바나나'란 개체로 과일을 개체화하려면 '노란 껍질에 모양이 휘고, 단맛을 지닌' 바나나라는 구체적인 객체가 필요하다.
  대상을 지칭하는 추상의 개념은 컴퓨터 언어속에도 존재하고 있다. C++과 Java에선 this 그리고 Python에서는 self라는 키워드로 객체 자신을 지칭할때 사용한다. 혹은 '이미 개체화된 객체'를 사용자가 레퍼런싱 함으로서 대상을 지칭하는 용도로 쓰인다.
 예를 들자면 아래와 같다.

ex> 자신 지칭
Human::Human(bool gender)
{
    this->gender = gender;
}


ex> 상위 분류로 지칭
 Human nancy;
 Animal* she = &nancy;
 she->Eats(hotDog);

 피카소의 추상화抽象畵를 보면 개체가 가지는 특징(윤곽)을 나타내 추상화Abstraction한다.
사용자 삽입 이미지사용자 삽입 이미지
  원본 그림을 몰랐다면 연상이 안될수도 있다. 하지만 구체적인 것(추상화 대상)을 안다면 추상화된 결과물을 보고 무릎을 탁칠수도 있는일이다. 피카소의 그림은 공통 윤곽을 잡아 기하학적 추상화를 하고있는것으로 보인다. 컴퓨터 언어의 객체 추상화도 다를것이 없다. 서브 클래스들의 공통 분모를 찾아 슈퍼 클래스로 일반화 하기도 하며, 추상 객체인 슈퍼 클래스를 미리 정의 해놓고 하위 클래스로 서브클래싱하기도 한다. 이들을 상속 관계Inheritance에 있다고 한다. 그리고 상속은 객체 지향 프로그래밍의 기반 기술중 하나이다.
  추상화는 단계적으로 이루어질수도 있다. 피카소가 소를 단계적으로 추상화하는것이 인상적이다.
화살표를 누르면 추상화가 되가는 과정을 볼수있다.

 피카소의 추상화는 공통점을 모아 기하학적 예술로 승화시키고 있다. 이 예술의 가치중 하나는 작품을 보고 '서로 다른 의미를 부여'하는 것으로 볼수 있다. 달리 말하면 의미적 다형성Semantic Polymorphism을 지니게 되며, 다형성이 추상화의 중대한 가치가 되는것이다.

사용자 삽입 이미지
최종 추상화 결과물

  피카소가 마지막까지 추상화한 결과물을 보고 여러가지를 연상할수 있다. 주로 네발달린 짐승으로 뿔을 가진 동물을 연상할것이다. 그리고 추상화된 성기 부분을 관찰해낸 사람은 수컷을 연상하기도 할것이다. 추상화 결과물의 의미가 다형을 갖는 가치는 컴퓨터 언어도 비슷하다.
  최종 결과물로 부터 추상화 단계를 역으로 볼수록 점점 그 형태가 구체적이 되서 의미적 다형성을 점점 잃어가는것을 볼수 있다. 컴퓨터 언어의 객체도 마찬가지이다. 추상화 계층의 깊이가 깊어질수록 객체의 다형성은 커지고 덜 구체적이 된다. 피카소의 그림은 기하학적인 추상화를 이루며 구체성을 줄이지만, 컴퓨터 언어의 객체는 행위Behavior와 구조Structure를 추상화 하며 구체성을 줄이는 점이 차이점이라 할수있다. 공통점은 이들이 최소한의 공통점으로 대상을 표현하려 함으로서 유용한 다형성을 지닌다는 점이다.
  컴퓨터 언어의 관점에서봤을때 추상화는 중복 제거의 의미보다 다형성의 가치가 매우 크다고 생각하며, 추상의 개념은 이에 매우 밀접한 관계에 있다. 이 객체들의 추상화 품질은 객체 지향 소프트웨어의 내부 디자인 품질을 판단하는 좋은 기준이 될수 있다.



 인터페이스(Interface)

  객체가 극도로 추상화 되다보면 속성이나 구조는 사라지고 행위Behavior만이 남는다. 이때 이 순수 추상 객체(Pure Abstract Class)를 인터페이스라고 부른다. 실생활에서 우린 좋은 인터페이스의 예를 가지고 살고 있다.

사용자 삽입 이미지
필립스사의 통합 리모콘

 통합리모콘이 그 예이다. 이 필립스 리모콘은 1100여개의 기기를 하나의 리모콘으로 접근한다. 각 기기들의 적외선 코드를 모두 가지고 있다. 우리가 보고 있는 것은 인터페이스이며, 그것의 구현은 생각하지 않아도 된다. 단지 기기를 선택하기만 하면 같은 버튼으로 동일한 기능의 행동을 할수 있다. 예를 들어 Sony TV의 채널을 바꾸는것과 Samsung TV의 채널을 바꾸는것은 단지 '채널 변경 버튼'만으로 가능해지는 것이다. 변하지 않는 모든 기기의 일반적인 레이아웃을 모은 인터페이스라고 보면 된다. 그리고 각 기기별 리모콘 적외선 코드 구현은 내부에 감추어져 있다.

사용자 삽입 이미지

 하단의 세가지 리모콘은 DVD-Player, TV, Amp 기기의 리모콘들이다. 통합리모콘이 없다면 기기들의 전원을 켜기 위해서 리모콘을 세가지 다 가지고 있어야 한다. 그리고 세가지 리모콘의 전원 버튼을 전부 한번씩 눌러야 전원을 킬수있다. 하지만 이제 공통의 기능 버튼을 모은 인터페이스인 통합 리모콘(상단의 필립스 리모콘)을 가지고 있으므로 나머지 리모콘은 옆에 보관하면 된다.
  인터페이스에 접근하면 모든 기기에 접근할수 있게 되었다. 즉 필립스 리모콘은 다형성을 지니고 DVD 리모콘으로 되었다가, TV 리모콘도 되고, 앰프 리모콘도 되는것이다. 예외의 경우는 공통 인터페이스가 아닌 고유의 기능을 조작하기 원할 경우, 해당 기기의 번들 리모콘으로 접근해야한다(다운 캐스팅을 생각하면 된다).
  번들 리모콘으로 접근하는것을 불편하게 생각해서, 다른 통합 리모콘은 수십개의 버튼을 지니고 있는 경우도 있다. 하지만 너무 많은 버튼때문에, 대부분의 버튼이 아무기능을 하지 않는 경우가 많다. 사용자는 어떤 버튼이 제대로 동작하는지 눌러보기 전까지 알수없다. 이것은 잘못된 추상화의 부작용이다.

'개발 > 수필' 카테고리의 다른 글

객체 지향 소프트웨어 일주 - 3  (0) 2008/03/28
테스트와 장인 - 8  (0) 2008/03/19
객체 지향 소프트웨어 일주 - 2  (2) 2008/03/15
테스트와 장인 - 7  (0) 2008/03/12
객체 지향 소프트웨어 일주 - 1  (0) 2008/03/11
테스트와 장인 - 6  (2) 2008/02/25
Tag
card.ly
Top
Comment 2 Trackback 0
  1. 황상철 2008/03/26 15:35 address edit & delete reply

    요즘 일이 바빠 오랜만에 들렸더니 좋은글을 많이 올리셨네요. 잘 읽고 갑니다.

    • yagur 2008/03/27 08:00 address edit & delete

      잡담성 생각들이 많은데, 좋게 봐주시니 감사합니다.

Trackback : http://yagur.impon.net/trackback/130 관련글 쓰기

객체 지향 소프트웨어 일주 - 1


작성자: yagur Rev : 2

 Around Object Oriented Software - 1

 1. 객체


 내 생각은 추상의 세계에서 오고 내가 하는 말은 연관성의 세계로부터 오기 때문에 나는 오류를 범하지 않고는 결코 얘기를 할 수 가 없다.
 - 칼릴 지브란

  근 십수년간 Object Oriented Programming의 패러다임은 바이러스와 같이 퍼져나갔다. 그리스 철학자 플라톤의 이데아론의 영향을 받은 패러다임은 사람들의 입담과 종이위의 글, 또는 소프트웨어의 코드란 실체 통해 그 철학을 전염시켜 나갔다. 그리고 지금 그것의 위치는 가장 널리 보급된 패러다임이 되었다.

현실 세계에 존재하는것은 객체Class의 개체Instance일 뿐이다. - 플라톤

  재미있는 글귀이다. 객체 지향 프로그래밍을 하던 사람에게 매우 친숙한 단어들이 보인다. 객체Class 그리고 인스턴스Instance가 바로 그것들이다. 칼릴의 격언처럼 우린 생각 영역에 있는 추상의 것들을 프로그래밍의 객체Class로 정의해 코드란 것으로 개체화Instantiation시킨다. 그리고 우리는 코드로 개체화된 컴퓨터 언어 영역의 객체를 메모리의 세계에 개체화 한다.
  카릴의 격언중 ''이란 부분을 컴퓨터 언어(C++, Java등)로 해석하면 꾀 그럴사 하다. 우리가 객체 지향 프로그래밍을 하며 가장 많이 하는 것이 무엇이 있을까? 바로 연관 관계를 디자인하거나, 그것들을 프로그램으로 실체화 하는 것이다. 이 연관 관계를 구체화한것들은 OOP에 매우 친숙한 객체 관계Class Relationship들이다.

사용자 삽입 이미지

  연관 관계를 위와 같은 모델링 언어로 표현하기도 한다. 상속Inheritance, 합성Composition, 집합Aggregation, 연관Assocation, 위임Delegation등의 관계는 경험자에게 친숙한 관계들일것이다. '직선과 화살표 그리고 << >>의 스테레오 타입'으로 표기한것은 '결합관계를 스테레오 타입'으로 나타내는 방언으로 봐도 무방하다.
  우리가 실체화한 객체는 추상의 세계로부터 나왔다. 그리고 추상적 개념을 띄고 소프트웨어의 내부 구성원인 객체로서 우리의 창조물이 된다. 우리가 말(Computer Language)을 할때 추상적인 것들 사이의 연관 관계를 정의함으로서 우린 오류를 범할수 밖에 없다.
하지만 연관성의 오류가 소프트웨어의 동작을 멈추게 하는것은 아니며, 소프트웨어에 나쁜 냄새(Refactoring의 냄새 혹은 OOD의 나쁜 설계)를 풍기게 할뿐이다.

  우린가 무엇으로 객체를 식별하는지, 그 기준을 생각해 보는것도 나쁘지 않다.
사용자 삽입 이미지
Plato and cats

  우린 무엇으로 위의 그림에 보이는 개체들을 고양이라고 구분짖고 있는것일까. 우리가 보고있는것들은 나이가 다르고, 생명체가 아닐수도 있고, 신체 특징도 다르며, 추상적이거나, 심지어 그저 모형이기도 하다. 하지만 우리는 고양이라고 저들을 구분 짖는다.
  우린 고양이란 개개의 개체Instance들의 특성을 종합하고, 그것들을 일반화해 '보편적인 고양이'라고 인식하고있다. 즉 이 '고양이'란 단어는 공간과 시간의 영향을 받는것이 아닌, 추상적인 개념으로서 객체Class 혹은 플라톤의 이데아現象인 것이다.
  소프트웨어는 객체를 컴퓨터 언어로 표현해야 하기 때문에 좀더 구체적이 되어야 한다. 행위와 속성을 정의해야하며 그것을 구체화해야한다. 객체의 멤버들은 선언시 실체가 없는 추상적 멤버가 될수 있다. OOP에서 추상 멤버Abstract Member를 지닌 객체를 추상 객체Abstract Class라 부르며, 순수 추상 객체Pure abstract class는 인터페이스Interface라 부르기도 한다.
  윗 단락에서 굉장히 많이 언급된 '추상'이란 단어가 있다. 이는 OOP를 하기 위해 상당히 중요한 개념이며 다형성Polymorphism과도 맞물려 있다.

'개발 > 수필' 카테고리의 다른 글

객체 지향 소프트웨어 일주 - 2  (2) 2008/03/15
테스트와 장인 - 7  (0) 2008/03/12
객체 지향 소프트웨어 일주 - 1  (0) 2008/03/11
테스트와 장인 - 6  (2) 2008/02/25
테스트와 장인 - 5  (0) 2008/02/22
테스트와 장인 - 4  (2) 2008/02/21
Tag
card.ly
Top
Comment 0 Trackback 0

Trackback : http://yagur.impon.net/trackback/129 관련글 쓰기

AOP와 직교성, 그들의 도구로 RAII와 Proxy의 활용


작성자: yagur  Rev : 2

 AOP의 소개
 프로그래밍 패러다임중 최근 성공적으로 보편화 된것은 객체지향프로그래밍이다. AOP(Aspect Oriented Programming)은 OOP를 대체할 패러다임이 아니라 OOP를 보완해줄수 있는 패러다임이다. 번역하자면 관점지향프로그래밍. 이를 비교적 간결한 문법으로 제공하는 편리한 비합성 프록시(non-composited proxy)라고 생각한다. 이것에 관한 생각은 이 글의 후반부에 위치한다.
 아직 C++은 지원되지 않고 Java와 C#이 이를 지원하고 있다. 하지만 패러다임 자체는 C++ 구현에서도 어느정도 가능하다(문법적으로 이를 지원하는 컴파일러도 있다고 한다.)

 AOP의 특징
 AOP의 가장큰 장점중 하나는 재사용성의 증대와 관심사의 분리(Speration of Concerns)이다. 컴포넌트의 디자인 변경과 리팩토링은 수정이 가해진 객체들의 재 합성을 불가피하게 한다. 그렇다면 객체나 컴포넌트들간의 의존성, 연관성이 복잡한 상태에서 특정 모듈의 디자인이나 클래스의 프로시저를 재가공하는일은 과연 재사용성을 증대시키는 일인지 생각해볼필요가 있다.

 횡단 관심사(Crosscut concern)
 위의 특징에서 언급된 재가공은 알고리즘이나 구조변경이 아닌 여러 관점에서의 관심사의 추가 삭제를 말하고 있다. 이를 테면 트랜잭션, 로그, 프로파일러, 인증, 검증등이 있다. 이 관심사들은 주로 객체의 프로시저안에 들어 가있으며 횡단 관심사(crosscut concern)라 한다. 하지만 그 객체의 내용에 정말 들어가야 하는 코드들인지 생각해보면 해당 객체의 본질과 달리 부가적인 관심사라는 것을 알수있다. 또 이 관심사들의 종류와 갯수는 로직의 변경에 따라 바뀐다. 아래의 코드와 그림이 어느정도 이해에 도움이 될것이다.

사용자 삽입 이미지
bool PlayerAccount::InsertCash(...)
{
    bool bResult;

    Lock();                 // 트랜잭션 시작
    StartProfiling();      // 프로파일링 시작
    ValidateAccount(); // 계좌 인증
    WriteLog();
    [DoSomeInsertion........................................]
    VerifyAccount();    // 계좌 검증
    EndProfiling();      // 프로파일링 종료
    UnLock();            // 트랜잭션 종료

    return bResult;
}

 사실 InsertCash는 DoSomeInsertion.. 이 주 관심사이고 나머지 트랜잭션, 프로파일링, 검증, 인증 사항은 로직이나 상태, 전략에 의해 언제든지 추가 삭제, 이동, 시점변경이 가능한 부분이다. 이들은 좌측 그림과 같은 관심사 방향으로 끼어들어있다. 그리하여 이들을 횡단 관심사(crosscut concern)라 부르는것 같다.
 재사용성 측면에서 보자면 횡단 관심사는 따로 분리되어 있어야 높게 평가될수 있다. 횡단 관심사를 포함하는 객체는 관심사의 변경이 있을때 마다 연관 혹은 합성이나 변경을 거쳐야 하기 때문이다.
 
 객체의 직교성
 느슨한 결합을 위해 의존성과 합성을 줄여 직교성을 살리는 것은 관심사의 분리(SoC)에서 시작될수 있다. 이것은 곧 객체지향으로 이어진다. 그것을 도와주는것이 AOP의 도입이다. 단 추상화 된 관심사들을 어떤것과 연관지을지 그 대상을 달리할수도 있다.("프록시의 활용"으로 넘어가 좀더 이야기하겠다.)
  템플릿 메소드(GoF)를 활용할때 프로시져들의 트랜잭션이 개별이 아니라 템플릿 메소드의 시작과 끝에 들어가길 원할수도 있다. 인증 또한 유저가 로그인할때 한번만 하도록 방침을 세울 수도 있고, 유저의 잦은 로그인 아웃이 가능한 시스템에서 보안이 요구되는 경우 보안에 관심있는 개별 퍼블릭 메소드마다 사용자 인증을 하는 방침을 세울 수도 있다. 이때 인증은 공인인증서나 핸드폰 인증을 요구할수도 있는데, 인증 자체를 추상화 하여 느슨한 결합을 한다 해도 추상화된것에 의존성을 갖기 마련이다(나쁜 것은 아니다). 클래스의 기본 관심사는 직교성(orthogonality)을 띄고 다른 횡단 관심사에 영향을 받지 않으면서 재사용성을 높히는 방법에 대해 생각해볼수 있다.

 C#과 Java의 AOP
 자바에선 extension이나 프레임워크로 AspectJ나 SpringAOP같은것을 통하여 지원하며, C#에서는 문법적으로 AOP를 지원하고있다. 정해진 문법으로 관심사의 편리한 분리를 이루어내는 이것들의 예를 본다면 AOP가 OOP가 말하는 재사용성을 도와준다는것을 알수 있다. 아래의 것이 가능하다면 어떨까?
class SomeClass
{
    [Log][Transaction]
    bool DoSomething();
    [Profile]
    bool SomethingComplex();
}
C#이나 Java에서는 가능하다. 하지만 C++도 불가능한것은 아니다.

 프록시(Proxy)의 활용 그리고 RAII
 다른 언어를 사용해보진 않았지만 AOP에 관련된 글들을 읽어 보면 일종으 프록시라고 이해된다. 프록시를 문법적으로 확장하는 조금 새로운 방법 정도로 볼수있다.
 프록시는 GoF에 의해 우리에게 친숙한 패턴이다. 이쯤이면 이미 AOP의 활용방안중 하나가 Proxy Pattern이 될수 있다는것을 눈치신 분도 있을것이다. 프록시(proxy)와 다형성(polymorphism)은 c++에게 AOP의 일부를이미 지원하고 있었다.
 단 여기서 사용할 프록시는 비합성 프록시(non-composited proxy)인데 GoF의 것과 아주 약간 다르나, 같은 인터페이스를 통해 실체를 접근한다는 근본은 같다. 그리고 이둘은 결합도의 차이로 나타난다. 합성은 상속보다 느슨한 결합이고 상속은 아주 단단한 결합인 셈이다. 그리고 좀더 접근가능한 멤버가 많아진다. 또 public 상속(is-a)보단 private 상속(is-implemented-in-terms-of)을 활용할수 있다. 이는 RealSubject의 횡단관심사를 확장이 아닌 정책으로 결합시키기 위함이다.

사용자 삽입 이미지
                         Non-Composited Proxy               GoF's Proxy(Composited Proxy)


사용자 삽입 이미지
                                              Non-Composited Proxy의 AOP

 비합성 프록시는 이글에서 proxy를 AOP에 맞게 사용하기 위해 일부 변경한것을 지칭하기 위해 사용됬으므로 양해 부탁드립니다.

 흥미로운 점은 프록시의 함수가 위빙(weaving)할때 조인 포인트(join-point)의 위치는 real subject의 함수의 전 후에 놓이며 같은 스코프(scope)에서 일어나는 것을 알수 있다. 이 경우엔 C++의 관용구인 RAII를 사용해 준다면 그 의미가 더 명확하고 안전해질수 있다.(RAII, ScopeGuard, ScopeLock)
 이런 프록시를 두게 된다면 RealSubject는 횡단관심사로부터 직교성을 띌수 있으므로 OOP 완성도를 높힐수 있을것이다. AOP를 로우레벨에서 지원해야 하는 불편함은 단점이지만 확장이나 프레임워크에 얽메이지 않고 좀더 자유롭다는 장점이 있다.

 DBC ASPECT
 DBC(Design by Contract) 역시 하나의 횡단 관심사로 일반화 시켜볼수 있다. proxy의 real subject의 멤버 함수 방어적 프로그래밍을 하기위해 전후에 배치한 조건들을 횡단 관심사로 바꾸면 된다. 그렇다면 real subject는 좀더 직교적일수도 있다. 실제 사용자(개발자)가 Proxy에 의존 한다면 DBC를 횡단 관심사로 분리하고 Proxy 클래스 구현 속에 포함하게 된다면 realsubject는 좀더 간결하고 본래 구현 목적에 부합하는 것들로 구성될수 있다.

Tag
card.ly
Top
Comment 0 Trackback 0

Trackback : http://yagur.impon.net/trackback/116 관련글 쓰기

Eiffel 과 DBC.

작성자: yagur  Rev : 1

Eiffel.
  Eiffel은 Bertrand Meyer의 DBC(Design by Contract)를 구현하고 있는 객체 지향 언어입니다. 아래의 그림 같이 인텔리센스를 지닌 IDE도 제공하고 있습니다.(듀얼 라이센스를 지니고 있습니다. 상용은 유료입니다)
사용자 삽입 이미지

Contract(계약)
 여기서 Contract(계약)란 사용자와 제공자사이의 합의점을 말합니다.

특징
 Eiffel의 가장 큰 특징이라 한다면 단위 실행 혹은 프로시져의 책임을 문법적으로 지원한다는것에 있습니다.

Design by Contract
 1. 호출자(Caller)와 제공자(Callee) 사이의 프로시져의 선행조건(Pre-condition)과 후행조건(Post-condition).
 2. 사용자와 제공자 사이의 책임에 관한 계약과 그 장점
   1번과 2번은 설계에서 당연한 관계를 가지고 있는 것들입니다. Eiffel이 아니라 C/C++만 하더라도, 방어적인 프로그래밍(Deffensive Programming)을 하시는 분들은 인자의 유효성이나 몇몇 선행조건을 대부분 작성하고 있습니다. 하지만 코드에서 이를 단번에 알기란 어렵습니다. 문법적 키워드가 있거나 분리되어 있지 않기때문입니다. Eiffel은 이것을 requireensure로 그 계약(조건들의 모음)들을 분리하는것을 문법적으로 지원 하고 있습니다. 이는 코드를 읽는 사람으로 하여금 직관적인 구분을 가능하게 하여 가독성을 높혀주는 효과도 지니고 있습니다.

Precondition 과 Postcondition
 Eiffel에서 require과 ensure를 통한 선,후행조건은 실행중 동적으로 확인될수 있습니다. 아래의 구조를 지니고 있습니다.
procedure_name(argument) -- comments...
require
    Precondition
do
    Procedure Body
ensure
    Postcondition
End
 Eiffel에서 두가지 조건(선행,후행)은 assertion(단정문)입니다. 단정문의 기능을 어찌보면 강요하게 됩니다. 특정 작업이 멈추는것이 좋을지 잘못된 상태정보를 리턴하는것이 좋을지에 선택이 가능했다면 어떨지 생각해보는것도 괜찮을것 같습니다.
 이 DBC의 선후행 조건은 그 책임을 아래와 같이 설명하고 있습니다.

사용자 삽입 이미지


 방어적 프로그래밍 DBC
 방어적 프로그래밍은 모듈이나 함수가 예상하지 못한 상황에 대비하기 위해 확인 조건 코드들을 작성하여 대비합니다. 이것은 과도한 조건 확인을 초래하며, 복잡하고 유지하기 어려운 소프트웨어를 낳을수도 있습니다.
 그에 비해 DBC는 책임 부여가 확실하고 그것이 문법적인 인터페이스로 이미 자리잡고 있습니다. Eiffel에선 이렇게 말하고 있지만 Code Complete 2nd의 ch8. 방어적 프로그래밍을 보면 단정문을 이용한 선후행 조건에 관하여 언급하고 있습니다. 방어적인 프로그래밍의 기법중 일부로 DBC를 채택하여 사용할수 있다는 것입니다.
 DBC와 방어적 프로그래밍은 서로 다른것 같지만 다르지 않으며 방어적 프로그래밍의 한 방법으로 DBC를 활용할수 있습니다. DBC에 관한 더 자세한 내용은 Object-Oriented Software Construction(Meyer 1997)에서 찾아 볼수 있습니다.

'개발' 카테고리의 다른 글

Jeff Bay의 객체 건강유지법.  (0) 2008/10/01
이럴때 테스트 셋을 만든 보람이 있습니다.  (0) 2008/04/12
Eiffel 과 DBC.  (2) 2008/01/04
Overloading과 Overriding [Part 2]  (0) 2007/05/26
Overloading과 Overriding [Part 1]  (0) 2007/05/22
Unicode 2  (1) 2006/08/11
Tag
card.ly
Top
Comment 2 Trackback 0
  1. hey 2008/01/04 23:51 address edit & delete reply

    좋은 글 감사합니다.

    • yagur 2008/01/05 22:37 address edit & delete

      비슷한 관심사를 지닌분을 만나 방갑습니다.

Trackback : http://yagur.impon.net/trackback/113 관련글 쓰기

Overloading과 Overriding [Part 2]

목차
1. 오버로딩과 오버라이딩의 간략한 정의 [Part 1]
2. C++ 오버로딩 [Part 1]
3. C++ 오버라이딩 [Part 1]
4. 오버로딩과 오버라이딩을 혼용하여 활용한 예 [Part 2]

4. 오버로딩과 오버라이딩을 혼용하여 활용한 예
이 예는 항상 활용가능한 예는 아닙니다.
아래의 구조로 유닛 클래스의 상속관계가 존재한다고 가정한다.(회색은 추상 클래스)
                                                          [표 A]

대부분의 오브젝트들은 슈퍼 클래스 형(UNIT)으로 특정 컨테이너가 보관하고 있을 것이다. 예를 들자면 아래의 컨테이너가 지금 프로세스 해야할 대상들의 모음이다.
Array<UNIT *> kUnitArray;
kUnitArray안에는 BattleShip, Bunker, Human, Helicopter등 다양한 UNIT이 존재한다.
오버로딩과 오버라이딩을 혼용하면 사용자는 슈퍼클래스형(UNIT)을 별다른 캐스팅 없이 Attack() 메소드 하나만으로 공격 메소드를 간편하게 수행할수 있다. 하지만 이에는 RTTI(Run-Time Type Identification)정도가 필요하다.
아래와 같이 정의한다.
Unit.h
class VEHICLE;
class STRUCTURE;
class CREATURE;
class UNIT
{
public:
   ...
   void Attack(UNIT* pUnit);
protected:
   ...
   virtual Attack(VEHICLE* pVehicle) = 0;
   virtual Attack(STRUCTURE* pStructure) = 0;
   virtual Attack(CREATURE* pCreature) = 0;
};
VEHICLE, STRUCTURE, CREATURE의 선언은 UNIT의 추상 메소드를 구현한다.
class VEHICLE : public UNIT
{
public:
   ...
protected:
   ...
   virtual Attack(VEHICLE* pVehicle);
   virtual Attack(STRUCTURE* pStructure);
   virtual Attack(CREATURE* pCreature);
};
STRUCTURE와 CREATURE는 VEHICLE의 선언과 거의 동일하다.

UNIT의 void Attack(UNIT *) 메소드는 실체(구현부)를 가지고 있다. 하지만 그 함수안에서 호출하는 오버로딩된 함수는 순수가상함수이다. 즉 순수 가상 멤버 함수들은 인터페이스 역활을 할뿐이며 추상화된 상태이므로 반드시 그 자식들은 오버라이딩을 통해 메소드를 실체화 해야한다.
UNIT의 void Attack(UNIT *) 메소드는 오버로딩된 메소드들을 RTTI를 통해서 호출하고있다.
void UNIT::Attack(UNIT *pUnit)
{
   if(dynamic_cast<VEHICLE *>(pUnit))
       Attack(dynamic_cast<VEHICLE *>(pUnit));
   else if(dynamic_cast<STRUCTURE *>(pUnit))
       Attack(dynamic_cast<STRUCTURE *>(pUnit));
   else if(dynamic_cast<CREATURE *>(pUnit))
       Attack(dynamic_cast<CREATURE *>(pUnit));
}
void Attack(UNIT *)  메소드를  오버라이딩해야 할 경우엔 필요에 의해 Attack함수를 가상함수로 선언하면 될것이다.

오버라이딩과 오버로딩을 혼용을 하면 아래와 같이 간편한 코드를 사용할수도있다.
void GameFrameWork::ProcessAttack() // 사용자가 공격커맨드를 내림
{
   ...
   UNIT* pCurrentUnit = GetCurrentUnit(); // 사용자 유닛
   UNIT* pSelection = GetSelectedUnit(); // 사용자가 공격 선택한 대상
   if(pCurrentUnit && pSelection)
       pCurrentUnit->Attack(pSelection);
   ...
}

주의
위의 예는 오버로딩과 오버라이딩의 단순히 혼용한 예이며 잘못된 접근일수 있습니다.

'개발' 카테고리의 다른 글

Jeff Bay의 객체 건강유지법.  (0) 2008/10/01
이럴때 테스트 셋을 만든 보람이 있습니다.  (0) 2008/04/12
Eiffel 과 DBC.  (2) 2008/01/04
Overloading과 Overriding [Part 2]  (0) 2007/05/26
Overloading과 Overriding [Part 1]  (0) 2007/05/22
Unicode 2  (1) 2006/08/11
Tag
card.ly
Top
Comment 0 Trackback 0

Trackback : http://yagur.impon.net/trackback/84 관련글 쓰기