디자인 패턴을 쓰시는지요?

mastercho의 이미지

요즘 패턴책을 읽고 있는데

.. 객체지향 설계에 도움이 되는것도 있지만

오히려 더 사고를 어지렵히는것도 있다는 생각이 듭니다

원래 제 객체지향 사고에는

각각의 객체 구성물를 가지고 더 큰 객체를 만들면서

설계를 하는게 맞다고 생각하고 있습니다

하지만

패턴중 옵저버 패턴을 보자면.....

객체 구성물이 더 큰 객체를 참조하는게 종종 보입니다

예를 들면 자동차 객체가 있고 구성물중 바퀴객체가 있다면

자동차가 바퀴를 제어한다고 봐야 된다고 보고 있는데

옵저버 패턴을 보면 바퀴에 자동차 레퍼런스[포인터]를 넘기는
일을 하더군요

사실 바퀴는 어떤 자동차인지 알지 못해도 굴러가는 것만 알면 되지
않습니까?

제가 설계하는 방식과 너무나 큰 괴리감을 느껴서 이해가 잘 안되더라고요

패턴을 공부하면서 객체지향적인 요소를 더 이해를 하게 되기도 하지만

이런 패턴들은 더 혼란스럽게 하기도 합니다

실제로 C++에서는 전방 참조방식밖에 지원 되지 않기때문에

[꽁수로 class 선언부에다가는 포인터만 선언 하고 ,cpp에서 참조하는 방식은 가능]

기본적으로 class간 상호 참조가 불가능하지 않습니까?

따라서 바퀴가 자동차를 참조하는 설계는 저절로 피하게 되는데

자바에서는 이런게 자유롭다보니 라이브러리도 이런 패턴들을 자주

사용되는거 같더군요

이게 꼭 좋은건지는 모르겠지만요

혹시 프로젝트에 디자인 패턴을 사용하시는분들이 있으시다면

패턴에 대해 한말씀 올려주시면 안될까요?

fender의 이미지

옵저버 패턴은 일반적인 이벤트 핸들러가 아닌가요? 자동차-바퀴의 구현에서 어떻게 옵저버 패턴을 적용하셨는 지 좀 더 구체적으로 알려주시면 도움이 될 것 같습니다.

----------------------------
[서명] 그놈 한국 사용자 모임 - 그놈에 대한 모든 것! - 게시판, IRC, 위키, 갤러리 등등...

jj의 이미지

mastercho wrote:
사실 바퀴는 어떤 자동차인지 알지 못해도 굴러가는 것만 알면 되지
않습니까?

이게 요구사항을 만족시키면, 옵저버 패턴을 안쓰면 되는거 아닌가요?

mastercho wrote:

실제로 C++에서는 전방 참조방식밖에 지원 되지 않기때문에
기본적으로 class간 상호 참조가 불가능하지 않습니까?

따라서 바퀴가 자동차를 참조하는 설계는 저절로 피하게 되는데

자바에서는 이런게 자유롭다보니 라이브러리도 이런 패턴들을 자주

사용되는거 같더군요

제가 C++를 자세히는 모르겠습니다만, 일방향 참조만으로 프로그램을 짤 수 있을지 모르겠네요. 또... C++가 Java보다 자유로운 정도는 지나치게 크지 않은가요?

mastercho wrote:
이런 패턴들은 더 혼란스럽게 하기도 합니다

mastercho님이 말씀하신것 처럼, 패턴이 프로그램 복잡도를 높히는데 큰 역할(?)을 하고 있는건 분명한것 같습니다. 필요할때, 필요한만큼만 쓰면되는데, 그 정도를 결정하기가 쉽지 않죠.

재즈뮤지션 찰리파커(Charlie Parker)가 어케 디자인 패턴을 알았는지^^ 다음과 같은 말을 했다고 하는군요.

Learn the patterns and then forget ‘em

--
Life is short. damn short...

mastercho의 이미지

Quote:
옵저버 패턴은 일반적인 이벤트 핸들러가 아닌가요? 자동차-바퀴의 구현에서 어떻게 옵저버 패턴을 적용하셨는 지 좀 더 구체적으로 알려주시면 도움이 될 것 같습니다.

예를 들면 그렇다는것입니다

패턴책에 나온 예를 보자면요

class A 와 class B가 있다면

A <---> B

이런식의 참조는 일반적인 제 설계 법하고 너무 다르다는 생각이드네요

프로그램에 옵저버 패턴을 적용시켜보신분 있으신지요?

아니 그걸 떠나서

A <---> B 클래스끼리 서로 참조하게끔 설계하시는분 계시는지요?

승자는 자기보다 우월한 사람을 보면 존경심을 갖고 그로부터 배울 점을 찾지만 패자는 자기보다 우월한 사람을 만나면 질투심을 갖고 어디 구멍난 곳이 없는지 찾는다.
- 하비스

fender의 이미지

mastercho wrote:

class A 와 class B가 있다면

A <---> B
이런식의 참조는 일반적인 제 설계 법하고 너무 다르다는 생각이드네요

객체지향적 설계에서 이런 식의 구조는 매우 일반적입니다. 가장 대표적인 경우가 도메인 객체에 대한 설계인데, 이 때는 A에서 B를 접근할 수 있는지, 혹은 반대로 B에서 A를 접근할 수 있는지, 아니면 양방향 모두 가능한지를 순수하게 분석된 비즈니스적인 요구사항에 의해 결정하는 경우가 많습니다.

예를들자면, 온라인 쇼핑몰을 구축하는데 상품과 분류의 관계에서, 화면 구성상 특정 상품을 선택했을 때 해당 상품의 분류를 알아야 하고, 반대로 특정 분류를 선택했을 때 해당 분류의 모든 상품을 뽑아야 한다면 일대다의 양방향 네비게이션으로 설계해야 합니다.

물론 이런 도메인 객체들 사이의 관계가 아니라 옵저버의 경우와 같이 순수하게 기능적인 필요에 의해 객체 참조를 주고 받는 구조도 있을 수 있습니다.

제 생각에는, 객체지향적 설계가 가능하면 실제 세계를 충실하게 반영하면 좋겠지만 어느 정도 유연성은 - 전체적인 설계의 명확성을 크게 떨어뜨리지 않는다면 허용할 수 있다고 봅니다.

그럼~

----------------------------
[서명] 그놈 한국 사용자 모임 - 그놈에 대한 모든 것! - 게시판, IRC, 위키, 갤러리 등등...

meteors의 이미지

mastercho wrote:

예를 들면 자동차 객체가 있고 구성물중 바퀴객체가 있다면

자동차가 바퀴를 제어한다고 봐야 된다고 보고 있는데

옵저버 패턴을 보면 바퀴에 자동차 레퍼런스[포인터]를 넘기는
일을 하더군요

사실 바퀴는 어떤 자동차인지 알지 못해도 굴러가는 것만 알면 되지
않습니까?

단순히 자동차에 바퀴라는 것이 달려있을 때에는 굴러가는 것만 알아도 됩니다만..

충격 흡수를 하는 shock absorber (일본식으로 쇼바라고 하지요)가 달린 경우 바퀴가 지면으로부터 받은 충격량을 shock absorber에 전달해야 되겠지요..

일부는 차체에 전달해야 하구요.

필요한 경우에 패턴을 쓰고 없어도 잘 돌아가면 안쓰면 됩니다. :)

mastercho의 이미지

Quote:
충격 흡수를 하는 shock absorber (일본식으로 쇼바라고 하지요)가 달린 경우 바퀴가 지면으로부터 받은 충격량을 shock absorber에 전달해야 되겠지요..

일부는 차체에 전달해야 하구요.

바퀴에서 자동차로 메세지를 보낸다는 개념보단

자동체에서 굴러가도록 명령 받은 바퀴에서 충격량을 리턴하는 개념으로
봐야 적당하지 않나요?

프로그램짠거에서 일부러 패턴을 적용할 코드들을 찾아볼려고 하는데
오히려 불필요하게 코드의 오버헤드가 생기는 감이 있어서
지금 시도를 못하고 있습니다

그리고 저는 현재까지 C++
설계할때 A---->B -----> C 이런 단방향 방식으로 밖에
참조를 사용하지 않았는데

C++로 자바처럼 후방참조를 및 클래스간 상호참조를 사용해 설계하시는분이 계신지요?

승자는 자기보다 우월한 사람을 보면 존경심을 갖고 그로부터 배울 점을 찾지만 패자는 자기보다 우월한 사람을 만나면 질투심을 갖고 어디 구멍난 곳이 없는지 찾는다.
- 하비스

jj의 이미지

mastercho wrote:

바퀴에서 자동차로 메세지를 보낸다는 개념보단

자동체에서 굴러가도록 명령 받은 바퀴에서 충격량을 리턴하는 개념으로
봐야 적당하지 않나요?

음, 정확히 지적하셨네요, 리턴하는 개념으로 사용하려면, polling을 하셔야 할것 같은데요? 언제 충격이 발생할지 모르니까요. 아니면 좀더 우아하게 callback을 사용하던가 해야겠죠. Observer의 사용을 Callback을 사용하는것처럼 이해하시면 될것 같은데요? 어떤 패턴책인지는 모르겠지만, Observer pattern의 Motivation과 Consequence를 잘 읽어보시는게 좋을것 같습니다.

mastercho wrote:

C++로 자바처럼 후방참조를 및 클래스간 상호참조를 사용해 설계하시는분이 계신지요?

상호참조가 C++에서 '특별'한 것인지는 오늘 처음 알았습니다. 음... pParentNode 같은것만 생각해봐도, 상호참조 없이 작업한다는건 도저히 불가능 한것 같습니다만....

--
Life is short. damn short...

mastercho의 이미지

Quote:
상호참조가 C++에서 '특별'한 것인지는 오늘 처음 알았습니다. 음... pParentNode 같은것만 생각해봐도, 상호참조 없이 작업한다는건 도저히 불가능 한것 같습니다만....

제가 말하는것은 포인터를 이용한 내부 자료나 메서드에 대한 참조를 말하는것입니다

포인터만 참조한다는게 아니고요

예를 들어보겠습니다

#include <stdio.h>

class B;

class A
{
    B* bb;
public :

// 선언부에서 인라인 함수로  B의 멤버 변수나 함수는 호출못함
};

class B
{
     A* aa;
public:
    // A에 대한 조작 메서드
};


int main(int argc, char *argv[])
{
	printf("Hello, world\n");
	
	return 0;
}

class A에서 클래스 B 참조하려면 내부 변수 bb를 B*형으로 선언하고
A.cpp 에서 include "B.h" 하는 방식으로 사용해야합니다

어떤분은 이렇게 상호 참조하는 설계는 잘못됐다는 분도 계십니다
[또한 이런식으로 참조하면 오버헤드가 크다나?]

전 이것을 지적하고 싶은것입니다

이런구조라면 어쩔수 없이 단방향 클래스 설계를 하게 되는것이지요

이게 논리상으로도 어느정도 맞다고도 느껴지고요

굳이 위의 예를 따지자면 클래스 A는 바퀴가 되는것이고 클래스 B는 차 자체가 되는것입니다

승자는 자기보다 우월한 사람을 보면 존경심을 갖고 그로부터 배울 점을 찾지만 패자는 자기보다 우월한 사람을 만나면 질투심을 갖고 어디 구멍난 곳이 없는지 찾는다.
- 하비스

지리즈의 이미지

제가 공장 자동화쪽에서 장비 통신을 주로하는 일을 해서...
모든 것이 통신으로 보고 설계하는 습관이 있습니다.
양해 바랍니다.

독립적인 두개체가 통신할 필요가 있을 경우...

저 같은 경우는 두 개체의 통신을 전달해줄 또 하나의 개체를 만듭니다.
왜부 개체로서 독립적으로 생긴 이 개체의 참조를 각각 두 개체가 가지는 거지요...

T:전달개체...

A <------ T -------> B

B가 T의 어떤 매소드를 호출하면...
T는 A가 등록한 콜백함수 따위로 A에게 이벤트를 넘겨주죠..

역시 역으로 A도 간접적으로 B에게 이벤트를 넘겨 줄 수 있습니다.

이런 디지인 패턴의 장점은 서로가 서로의 Interface를 전혀 몰라도 돼고,
또한 유사성도 없어도 됍니다. 오직 T만 알고 있으면 돼지요...
그리고 T를 이용하는 개체가 2개 이상일 때도 유용합니다.
2개이상이 되면 사실상 메세지큐와 같은 기능을 하게 됩니다.
단 메세지 큐가 큐를 감시해서 수동적으로 메세지를 받아와야 하지만...
T는 자신을 등록한 개체들의 콜백함수의 어드래스들만 가지고 있으므로
호출된 개체들에게 직접 메세지를 넘겨 줄 수 있습니다.
물론 1대1 1대다 기능도 잘하면 다대1기능도 구현할 수 있습니다.
(물론 다대1 기능은 구현할만큼의 대가가 있는 놈은 아니지만요)...

단 T를 잘 설계해 놓으면... 나중에 계속해서 써먹을 수 있지요...

There is no spoon. Neo from the Matrix 1999.

mastercho의 이미지

어떻게 보면 Mediator 패턴과 비슷하네요

A -----> T <------ B

T 통해 다른 클래스로부터 논리를 분리해 낸다

:)

A와 B는 T라는 조정자만 알면 되는 패턴

말씀 들으니 저도 결국 지리즈님하고 조금은 다르지만 비슷하게 작성한거 같기도 하고

역시 설계라는게 신기하면서도 어려운거 같습니다

승자는 자기보다 우월한 사람을 보면 존경심을 갖고 그로부터 배울 점을 찾지만 패자는 자기보다 우월한 사람을 만나면 질투심을 갖고 어디 구멍난 곳이 없는지 찾는다.
- 하비스

fender의 이미지

지리즈 wrote:
제가 공장 자동화쪽에서 장비 통신을 주로하는 일을 해서...
모든 것이 통신으로 보고 설계하는 습관이 있습니다.
양해 바랍니다.

독립적인 두개체가 통신할 필요가 있을 경우...

저 같은 경우는 두 개체의 통신을 전달해줄 또 하나의 개체를 만듭니다.
왜부 개체로서 독립적으로 생긴 이 개체의 참조를 각각 두 개체가 가지는 거지요...

T:전달개체...

A <------ T -------> B

B가 T의 어떤 매소드를 호출하면...
T는 A가 등록한 콜백함수 따위로 A에게 이벤트를 넘겨주죠..

역시 역으로 A도 간접적으로 B에게 이벤트를 넘겨 줄 수 있습니다.

이런 디지인 패턴의 장점은 서로가 서로의 Interface를 전혀 몰라도 돼고,
또한 유사성도 없어도 ㅤㄷㅙㅂ니다. 오직 T만 알고 있으면 돼지요...

음... 제 생각은 좀 다릅니다. 통신이나 브로드캐스팅과 같은 모델이 적합한 경우가 있지만 일반적인 설계에서 모든 것을 중계 역할을 하는 클래스를 통한 '통신'으로 처리하는 것은 바람직하지 않다고 봅니다.

서로의 인터페이스를 몰라도 호출을 할 수 있다는 건 장점이 아닙니다. 명확히 정의된 인터페이스를 사이에 두고 실제 구현에 관계없이 호출 가능한 것이 장점입니다.

인터페이스를 무시하고 임의의 '메시지' 들을 이용해서 클래스를 연동하면 위의 예제에서 매개 역할을 하는 'T'의 기능은 구현을 뜯어 보지 않으면 알 수 없는 블랙박스가 될 확률이 크고, 무엇보다 T 자체는 A와 B의 구현에 대해 종속적이 되기 때문에 불필요한 커플링이 생깁니다.

반대로, 구체적 A <- 추상적 클래스 A1 <- 인터페이스 IA <- B 의 식으로 호출을 하면 A나 A1의 구현을 마음대로 바꿔도 B를 작성하는 프로그래머 입장에서 전혀 문제될게 없고, A의 기능을 알기 위해서 어떤 임의로 정의된 '메시지 목록'이라 T의 실제 소스를 뜯어 보는 것이 아니라 IA에 대한 API 문서만 참조하면 되기 때문에 훨씬 안정적인 설계라고 생각합니다.

그럼~

----------------------------
[서명] 그놈 한국 사용자 모임 - 그놈에 대한 모든 것! - 게시판, IRC, 위키, 갤러리 등등...

crimsoncream의 이미지

jj wrote:
mastercho wrote:
사실 바퀴는 어떤 자동차인지 알지 못해도 굴러가는 것만 알면 되지
않습니까?

이게 요구사항을 만족시키면, 옵저버 패턴을 안쓰면 되는거 아닌가요?

이 말씀이 정답인 거 같은데요.
일반적인 자동차-바퀴 관계에서는 옵저버패턴이 필요없지 않을까 싶기도 하지만.
좀 어거지로 적용해 보면

자동차
{
attribute:
악셀상태
브레이크상태
바퀴갯수
method:
악셀상태체크
브레이크상태체크
}

바퀴
{
attribute:
나를 매단 자동차
method:
달려라
서라
}

여기서 "나를 매단 자동차" 쓰지 않으면
main
{
내자동차 = new 자동차 (바퀴4개)
if 자동차.악셀상태=밟음
for each 바퀴:
달려라
}

쓰면

main
{
내 자동차 = new 자동차 (바퀴4개)
run thread for each 바퀴
자동차.악셀상태=밟음
}

바퀴 thread
{
if 나를 매단 자동차.악셀상태 = 밟음
달려라
}

자동차:바퀴는 1:n관계이고 자동차의 state가 변할 때 종속된 바퀴들이 자동적으로 notify 받을 수 있어야 할 필요가 있다면 옵저버를 써야겠지요.
영 어설픈 예 같은데 :(

오늘 우리는 동지를 땅에 묻었습니다. 그러나 땅은 이제 우리들의 것입니다.
아직도 우리의 적은 강합니다. 그러나 우리는 그들보다 많습니다.
항상 많을 것입니다.

crimsoncream의 이미지

지리즈 wrote:
제가 공장 자동화쪽에서 장비 통신을 주로하는 일을 해서...
모든 것이 통신으로 보고 설계하는 습관이 있습니다.
양해 바랍니다.

독립적인 두개체가 통신할 필요가 있을 경우...

저 같은 경우는 두 개체의 통신을 전달해줄 또 하나의 개체를 만듭니다.
왜부 개체로서 독립적으로 생긴 이 개체의 참조를 각각 두 개체가 가지는 거지요...

T:전달개체...

A <------ T -------> B

B가 T의 어떤 매소드를 호출하면...
T는 A가 등록한 콜백함수 따위로 A에게 이벤트를 넘겨주죠..

역시 역으로 A도 간접적으로 B에게 이벤트를 넘겨 줄 수 있습니다.

이런 디지인 패턴의 장점은 서로가 서로의 Interface를 전혀 몰라도 돼고,
또한 유사성도 없어도 ㅤㄷㅙㅂ니다. 오직 T만 알고 있으면 돼지요...
그리고 T를 이용하는 개체가 2개 이상일 때도 유용합니다.
2개이상이 되면 사실상 메세지큐와 같은 기능을 하게 됩니다.
단 메세지 큐가 큐를 감시해서 수동적으로 메세지를 받아와야 하지만...
T는 자신을 등록한 개체들의 콜백함수의 어드래스들만 가지고 있으므로
호출된 개체들에게 직접 메세지를 넘겨 줄 수 있습니다.
물론 1대1 1대다 기능도 잘하면 다대1기능도 구현할 수 있습니다.
(물론 다대1 기능은 구현할만큼의 대가가 있는 놈은 아니지만요)...

단 T를 잘 설계해 놓으면... 나중에 계속해서 써먹을 수 있지요...

자동차-바퀴 케이스에서는 말씀하신 모델은 약간 문제가 있을 수도 있습니다.
A와 B의 사용자가 많이 중복되고 사용자 입장에서 A와 B의 사용이 반복되는 sequence가 보일 때는 말씀하신 T가 효율적으로 동작하지만 자동차-바퀴에서는 자동차의 사용자는 운전자, 바퀴의 사용자는 차축 혹은 자동차로 볼 때 일단 사용자가 다르다고 보이고요. 자동차 사용자의 입장에서 사실 사용하는 건 거의 자동차의 기능이기 때문에 자동차를 가리고 T를 만들어 주면 T는 사실 대부분의 일을 자동차로 바이패스 해주는 떼 쓸 뿐일 것 같습니다. 만약에 자동차-조향장치-바퀴라는 케이스라면 말씀하신 모델을 적용하는게 좋을 것 같구요.

오늘 우리는 동지를 땅에 묻었습니다. 그러나 땅은 이제 우리들의 것입니다.
아직도 우리의 적은 강합니다. 그러나 우리는 그들보다 많습니다.
항상 많을 것입니다.

onemind555의 이미지

...그냥 최고 앞에 질문만 읽어 보고 답변 다는 겁니다..

타이어가 어떤 자동차에 달려 있는지 타이어가 알 필요 있을까요...
자동차가 달리다 펑커가 나 버리면 예외 상황일 겁니다.

자동차에서 펑커 난걸 감지 한다고 한다면 예외를 받아서 잘 처리 하면 됩니다.

다르게 속도 감지를 한다면 이럴땐 타이어가 전해줄 게시판 정보만 가지고 있으면 됩니다. 자동차 까진 알 필요 없습니다.

-----------^^ ^^ ^^ ^^ ^^ ----------
..........................................................

mastercho의 이미지

제 생각에는 T를 통한 메세지 전달방법은

오히려 클래스간의 결합성을 떨어뜨리는 결과를 가지는거 같은데요?

A 와 B간의 인터페이스를 몰라도 된다는 이야기지

인터페이스를 전혀 몰라도 된다는건 아닌거 같습니다

A와 B는 T를 통해 서로 인터페이스 한다는 말씀이신거 같은데요

따라서 서로의 결합성을 확연히 줄일수 있다고 봅니다

예를 들어 바퀴가 자동차를 알아야 한다면

나중에 자동차를 바꾸게 될 경우 바퀴또한 자동차에 따라 내부가 변해야 하는게 아닌지요?

이렇게 서로 클래스간에 상호 참조를 하기때문에
결합성이 높아 , 결국 디자인 패턴의 목적과
전혀 다른 방형으로 가는게 아닌가 싶습니다

승자는 자기보다 우월한 사람을 보면 존경심을 갖고 그로부터 배울 점을 찾지만 패자는 자기보다 우월한 사람을 만나면 질투심을 갖고 어디 구멍난 곳이 없는지 찾는다.
- 하비스

eatist의 이미지

자동차와 바퀴 보다는.. 엔진과 바퀴 정도의 결합이.. 옵저버를 쓰시는데...
더 좋은 예가 아닐까 하는데요...

거 MFC같은데 보면.. Document - View나.. 이런거 쓰잖아요...

그러니까... Data에 해당하는쪽... MVC모델로 보면... Model에 해당하는
부분은.. 자기일만 하죠... 외부와의 연결을 최소로 하구요..
그래서.. 구태여 옵저버에 대한 생각을 하시겠다면... ..
엔진하고 바퀴와의 관계정도는.. 옵저버로 쓰일수 있을 거 같다는 생각이
듭니다.. 음.. 이것도 완전하게.. 일치 하지는 않을거 같은데...

옵저버 자체가 엔진이 바퀴의 상태를 알아야 돌아갈수 있는거는 아니니까...
엔진은.. 돌고.. 바퀴는.. 엔진에 붙어 있다면 도는 거고 붙어 있지 않다면..
안도는.. 방식으로.. 물론 엔진이 바뀌면 바퀴도 바뀌어야 겠죠...
이것을 위해서 트렌스미션을 붙이고 하면.. 바퀴와 엔진은 두고 자동차만
바꿀수 있겠죠...

하지만... 옵저버 패턴이 사용되기를 원하는 목적은... 바퀴의 종류나..
트렌스 미션의 종류에 상관 없는... 혹은.. 여러개의 바퀴 여러개의
문짝이.. 엔진을 중심으로 모여 있는 구조를 만들려고 할때 사용이
됩니다..

음.. 그냥.. 생각나는 데로 적느라고.. 글이 좀 이상해 지기는 했지만...

옵저버 당하는 녀석은... (Target..??: engine 인가요.. ) 외부의 것들에
영향을 안받을수 있도록 보호 해줘야 한다는 겁니다..

그렇기 때문에.. 엔진에 영향을 받는... 바퀴가... ... 엔진에 또다시 붙어있는
핸들과 전혀 상관 없이 동작 할수 있도록요...

옵저버들은... 엔진의 상태를.. 반영만 해주죠.. 영향을 미치는 것은.. 아니니까요.. 다시 말하면... 바퀴가 빵구가 난다고 엔진이 서버리게 된다면.. 그건..
이미 옵저버 패턴으로써의 모습을 잃었다고 생각할수 있습니다..

프로젝트에... 정말 패턴을 많이 사용하고 있는데.. 적절히 잘 사용하면...
뭐 복잡도가 증가하거나 그러지는 않습니다.. 특히나 Controller나..
Facade같은것들을 잘 사용한다면... 복잡도가 일정 범위 안으로 한정되기
때문에 프로젝트 전체로 봐서 최대 복잡도..( 전체 복잡도 말고.. 부분이 가지
는 복잡도의 최고...)는... 아주 낮은 상태로 유지 할수 있는 거 같습니다..

패턴에 관련된 문답이 있길레.. 그냥 재미있다는 생각이 들어서 답변 적었습니다.

fender의 이미지

mastercho wrote:
제 생각에는 T를 통한 메세지 전달방법은

오히려 클래스간의 결합성을 떨어뜨리는 결과를 가지는거 같은데요?

A 와 B간의 인터페이스를 몰라도 된다는 이야기지

인터페이스를 전혀 몰라도 된다는건 아닌거 같습니다

A와 B는 T를 통해 서로 인터페이스 한다는 말씀이신거 같은데요

따라서 서로의 결합성을 확연히 줄일수 있다고 봅니다

우선 인터페이스를 활용하는 방식은 :

ICar와 ITire를 잘 정의하고 문서화 합니다. 그리고 ICar를 상속받아 덤프트럭부터 경차까지 구체적인 구현을 합니다. 그리고 ITire를 상속받아 일반 타이어부터 스노우 타이어 등등을 구현합니다. 물론 ITire에는 타이어의 규격을 읽어오는 메소드가 정의되어야 합니다.

이 때 ITire와 ICar만 바뀌지 않는다면 - 사실 자동차와 바퀴의 관계가 혁신적으로 바뀌지 않는 한 이런 일은 흔하지 않습니다 - 개별적인 자동차와 타이어는 서로의 구현을 신경쓸 필요가 전혀 없습니다. 이 부분이 바로 커플링을 줄일 수 있다는 뜻입니다.

반면에 메시징을 활용하는 방식은 :

일단 스노타이어, 일반 타이어, 버스용 타이어 등등을 하나로 묶는 인터페이스가 존재하지 않습니다. 즉, A사의 "가"라는 제품은 규격을 알아오기 위해 int::getSize()를 제공하는데 B사의 "나"라는 제품은 float::getRadius();를 제공합니다.

그렇다면 매개자 T의 구현은 복잡해질 수밖에 없습니다. 즉,

public int handleMessage(final int message) {
  ...
  switch(message) {
  ...
    case GET_SIZE :
      if (tire instance of A) {
        return ((A) tire).getSize();
      } else if (tire instance of B) {
        return (int) ((B) tire).getRadius();
      }
    ....
  }
}

이런 식으로 T의 내부에 타이어의 개별 구현에 대한 정보가 하드코드되게 됩니다. 물론 자동차 쪽의 경우도 고려하면 더욱 커플링이 증가할 수밖에 없습니다. 만일 A사가 나중에 getSize()란 메소드 대신 getTireRadius()라는 메소드로 바구기로 하면 여기 맞춰서 T의 구현도 바뀌어야 합니다. 이런 구현에선 기본적으로 객체지향적으로 설계하는 의미가 없어집니다.

물론 두 번째 방법을 쓰더라도 A와 B의 메소드를 똑같이 맞추면 되는게 아닌가 하고 생각할 수도 있지만 그렇다면 애초에 T의 역할이란게 무의미 해지지 않나 생각합니다. 또 그런 느슨한 규약 보다는 명시적으로 타이어와 자동차의 인터페이스를 강제하는게 훨씬 혼동을 줄일 수 있는 방법이 아닌가 합니다.

다시 한번 정리하면, 객체 지향에서 커플링이란 상대방의 레퍼런스를 가지고 있는지 아닌지라기 보다는 특정 클래스가 다른 클래스의 구현에 영향을 받는지 안받는지를 말하는 경우가 많습니다.

----------------------------
[서명] 그놈 한국 사용자 모임 - 그놈에 대한 모든 것! - 게시판, IRC, 위키, 갤러리 등등...

onemind555의 이미지

등장하는 스케일 들이 뭐죠.. .. 앞에 글들 읽기가 귀찮아서...

자동차 , 타이어 4개 , 계기판 이렇게 아닌가요...
간단합니다. 자동차는 타이어 4개와 ,계기판을 포함합니다..
그리고 타이어를 포함 할때 계기판을 타이어에 연결 시킵니다...

그러면 타이어는 계기판을 알고 속도가 얼마 인지 알려 줄수 있습니다.

만약에 타이어를 갈아 치운다 그러면 자동차의 계기판 연결을 해제 하고 헌 타이어를 빼고 새타이어를 갈아 끼운 다음에 계기판을 새타이어에 연결 하면 됩니다..

-----------^^ ^^ ^^ ^^ ^^ ----------
..........................................................

mastercho의 이미지

글을 잘못봐서 -_-; 삭제했습니다

승자는 자기보다 우월한 사람을 보면 존경심을 갖고 그로부터 배울 점을 찾지만 패자는 자기보다 우월한 사람을 만나면 질투심을 갖고 어디 구멍난 곳이 없는지 찾는다.
- 하비스

fender의 이미지

mastercho wrote:

ITire의 인터페이스 클래스를 자동차로 상속받는것은 제대로 된 관계가
아닌듯 싶습니다 , 차라리 그 인터페이스 클래스를 포함하는 관계가
이루어져야 말씀하신게 제대로 어느정도 수긍이 갑니다만은...

헉... 제가 언제 그런 무지막지한 말씀을 드렸나요? 자동차가 타이어를 구현하다니요 :)

아토스 --> 승용차 - -> ICar, 스노타이어 -> ITire의 관계 입니다.

이런 관계를 염두에 두고 다시 한번 위의 글을 자세히 읽어 주셨으면 좋겠습니다.

그럼~

----------------------------
[서명] 그놈 한국 사용자 모임 - 그놈에 대한 모든 것! - 게시판, IRC, 위키, 갤러리 등등...

mastercho의 이미지

그림을 올릴수 없어서 자유게시판에서 다시 글을 올리겠습니다

승자는 자기보다 우월한 사람을 보면 존경심을 갖고 그로부터 배울 점을 찾지만 패자는 자기보다 우월한 사람을 만나면 질투심을 갖고 어디 구멍난 곳이 없는지 찾는다.
- 하비스

saxboy의 이미지

저도 한동안 패턴에 관심이 있어 무척 흥미있게 살펴보았던 기억이 있는데, 솔직한 제 심정으로는 이게 왜 필요한 것인가 싶더군요.

이렇게 쓰면 패턴을 좋아하시는 분들의 엄청난(!) 압박에 시달릴지도 모르겠습니다만, 제 느낌을 적습니다.

기본적으로 GoF 에서 시작된 패턴들을 살펴보면 그저 소프트웨어를 구성하는 공식정도에 지나지 않는다고 생각합니다. 마치 중고등학교 시절 수학 참고서에 적혀 있던 빨간색 네모안에 적혀 있는 공식들. 이 공식만 암기한다고 무언가를 해결할 수 있는 능력이 생기는 것은 절대로 아니지요. 또 공식을 직접 증명하고 도출해보고, 이해가 되는 순간쯤이 되면 이 공식은 쓸모가 없어지는 경우도 많이 경험하셨으리라 생각합니다. 더 좋은 다른 방식으로 문제가 해결가능한 경우도 많아지지요. 찰리파커의 Learn the changes and forget about 'em 이라는 커멘트도 이와 크게 다르지 않다고 생각합니다. (아마 learn the patterns는 아닐거예요. ;-) )

바로 여기에서부터 패턴의 비극이 시작된다고 <저는> 생각합니다. 모를때는 암호같이 보이고, 알고나면 별로 쓸모가 없는 것들. 좀 더 정확히는 패턴을 알고 있는 다른 사람들과 대화하기 위한 용어의 집합체 정도들.

어느정도 다른 사람의 코드를 읽어본 경험이 있고, 소프트웨어를 많이 작성해본 개발자라면 패턴책에 있는 많은 내용들을 이미 알고 있는 것이고 많이 사용해본 것이라고 생각합니다. 그렇지 않다면 또 경력만 내세우는 쓸모없는 개발자라고도 생각합니다. 틀림없이 GoF 도 비슷한 말을 적어 놓았던 것으로 기억합니다.

하지만 역시 패턴에 등장하는 용어 몇몇을 알고 있으면 요즘에는 코드를 읽는데 많은 도움이 되는 것이 사실입니다. Factory나 Iterator 같은 놈들은 C++이나 java 심지어는 C 코드에서도 아주 많이 등장하는 단어들이지요.

mastercho의 이미지

데브피아에도 같은 토론을 올려봤는데

데브피아의 C++ 프로그래머들은 아무래도 상호 참조에 관해서는
자바 프로그래머들하고 설계적으로 상당한 차이를 보이는거 같네요

당장 저 같은 경우라도 상호참조를 거부하고 있는데

자바 프로그래머는 자연스러운것으로
인식하고 있는거 같습니다

여기서 뭔가.... C++과 자바의 설계적 차이를 느낄수 있는거 같습니다

예를 들면 C++ 구조 자체가 자바처럼 후방참조를 쉽게 지원하지 않다보니

설계의 유연성은 떨어지지만 ,독립성이 좀더 뛰어나며 견고한 느낌이 나는 반면

자바는 자연스럽게 상호참조를 쓰다보니 설꼐에 있어서 유연성은 높아지지만
오히려 결합성은 증가하는 경향이 생기는게 아닌가 싶습니다

꼭 C++과 자바의 이 차이와 비슷한거 같네요

자바에서 포인터나 다중상속을 자바는 지원하지 않음으로써
견고함?을 늘린대신

C++에서는 포인터 사용이나 다중 상속을 자유롭게 사용가능한반면
책임은 프로그래머가 지는 방향인거와 경우와 비슷한 느낌입니다

그리고 실질적으로 브리지 패턴이라든지,어뎁터 패턴같은 경우의 몇몇
패턴을 제외하면 자바에서 잘 쓰이는게 있더라도
C++ 에서는 거의 잘 쓰이지 않는 패턴인게 많은거
같더군요, 특히 자바의 경우는 라이브러리자체가 패턴으로 만들어져서
패턴을 강제하지만 C++에서는 그런 경우가 없거든요
구조자체에서 오는 제약때문에 상호참조식으로 구현된라이브러리가
없는듯 싶습니다 [ 혹시알고 있는분은 알려 주시길 :o ]

승자는 자기보다 우월한 사람을 보면 존경심을 갖고 그로부터 배울 점을 찾지만 패자는 자기보다 우월한 사람을 만나면 질투심을 갖고 어디 구멍난 곳이 없는지 찾는다.
- 하비스

crimsoncream의 이미지

saxboy wrote:
저도 한동안 패턴에 관심이 있어 무척 흥미있게 살펴보았던 기억이 있는데, 솔직한 제 심정으로는 이게 왜 필요한 것인가 싶더군요.

이렇게 쓰면 패턴을 좋아하시는 분들의 엄청난(!) 압박에 시달릴지도 모르겠습니다만, 제 느낌을 적습니다.

기본적으로 GoF 에서 시작된 패턴들을 살펴보면 그저 소프트웨어를 구성하는 공식정도에 지나지 않는다고 생각합니다. 마치 중고등학교 시절 수학 참고서에 적혀 있던 빨간색 네모안에 적혀 있는 공식들. 이 공식만 암기한다고 무언가를 해결할 수 있는 능력이 생기는 것은 절대로 아니지요. 또 공식을 직접 증명하고 도출해보고, 이해가 되는 순간쯤이 되면 이 공식은 쓸모가 없어지는 경우도 많이 경험하셨으리라 생각합니다. 더 좋은 다른 방식으로 문제가 해결가능한 경우도 많아지지요. 찰리파커의 Learn the changes and forget about 'em 이라는 커멘트도 이와 크게 다르지 않다고 생각합니다. (아마 learn the patterns는 아닐거예요. ;-) )

바로 여기에서부터 패턴의 비극이 시작된다고 <저는> 생각합니다. 모를때는 암호같이 보이고, 알고나면 별로 쓸모가 없는 것들. 좀 더 정확히는 패턴을 알고 있는 다른 사람들과 대화하기 위한 용어의 집합체 정도들.

어느정도 다른 사람의 코드를 읽어본 경험이 있고, 소프트웨어를 많이 작성해본 개발자라면 패턴책에 있는 많은 내용들을 이미 알고 있는 것이고 많이 사용해본 것이라고 생각합니다. 그렇지 않다면 또 경력만 내세우는 쓸모없는 개발자라고도 생각합니다. 틀림없이 GoF 도 비슷한 말을 적어 놓았던 것으로 기억합니다.

하지만 역시 패턴에 등장하는 용어 몇몇을 알고 있으면 요즘에는 코드를 읽는데 많은 도움이 되는 것이 사실입니다. Factory나 Iterator 같은 놈들은 C++이나 java 심지어는 C 코드에서도 아주 많이 등장하는 단어들이지요.

패턴은 사실 경험있는 oo 프로그래머라면 익히 알고 있는 내용에 이름 붙여놓은 거에 불과 할때가 많습니다. 물론 이름이 뭐 중요하냐 싶을 수도 있지만 만약에 팀에 신참이 들어온다거나 해서 서로 다른 수준이나 분야의 개발자들이 모여서 대화할때 상호 이해가 명확한 언어를 쓰느냐 아니냐는 큰 차이를 가진다고 봅니다.

저도 패턴을 공부했다고 해서 문제를 바라볼때 패턴을 적용시킬 수 있는 능력이 생긴다고는 생각하지 않습니다. 다만 설계자가 설명할 때 알아들을 수 있는 능력은 생기지요. 이 부분은 factory와 worker를 적용해서 해결해라. 하는 것과 이 작업을 위한 interface를 따로 정의한 다음 이 작업은 이 interface를 구현한 놈들 중에 속성을 조사해서 런타임에 불러들여서 처리하도록 구현해라. 전자가 훨 간결하고 명확하다고 봅니다.

오늘 우리는 동지를 땅에 묻었습니다. 그러나 땅은 이제 우리들의 것입니다.
아직도 우리의 적은 강합니다. 그러나 우리는 그들보다 많습니다.
항상 많을 것입니다.

winner의 이미지

설령 현실세계가 객체지향, pattern 으로 simulation 이 가능하다고 하더라도 주어진 문제는 그렇지 않을 수 있으니까요.

상호참조는 여러가지면에서 다양하게 등장할 수 있는 문제라고 생각합니다.

상호참조를 좀더 일반화하자면 재귀참조라고 할 수 있는데 '열혈강의 Python' 에서 DOM tree 를 다룰 때 수없이 나오는 것이 재귀참조라 하더군요.

실제로 함수를 상호재귀호출하는 것도 필요하곤합니다.

'C++ 기초플러스 3판' 에서는 TV 와 remote controller 의 예를 들면서 상호참조를 보여주죠.

Java 야 모든 사용자 정의형이 참조로 다루어지므로 상호참조역시 문제될 것이 없는 것 아닐까요?

실제로 구조를 훼손해가면서 성능을 증가시키는 짓은 저도 해보았습니다만 역시 할게 못된다는 것이 제 생각입니다.

그 설계구조가 요구사항이나 domain problem 을 좀더 효과적으로 표현한다면 그대로 설계하는 것이 좋아보입니다.

http://www.zdnet.co.kr/hotissue/devcolumn/article.jsp?id=49399&page=6&forum=0

김창준씨가 Microsoftware 에 쓴 컬럼 '어떻게 공부할까?' 에서 pattern 에 대한 언급을 하네요. link 한 page 의 다음 page 도 계속 pattern 에 대한 이야기입니다.

fender의 이미지

여기에 대한 명언이 있습니다. "When the only tool you have is a hammer, every problem looks like a nail". :)

패턴에만 너무 집착하다보면 모든 문제를 다 알고 있는 패턴에 끼워맞추게 되고 쓸데없는 과도한 디자인을 불러올 수 있습니다.

하지만 저는 패턴은 객체지향 언어를 사용하는 프로그래머라면 반드시 '마스터' 해야 하는 단계라고 생각합니다. 저 역시 아직 패턴을 완벽히 이해하고 활용할 수 있는 수준은 못됩니다만, 디자인 패턴은 정의 그대로 객체지향 패러다임에서 많은 수의 프로그래머들이 경험했던 공통적인 문제들에 대한 가장 일반적이고도 검증된 해법을 정리한 것입니다.

물론 개 중에는 기존의 모든 상식을 뛰어 넘는 천재적인 프로그래머가 있을 지 모르겠습니다. 하지만 일반적인 프로그래머라면 먼저 기초를 제대로 알아야 응용을 할 수 있을 것입니다.

안타깝게도 실무에서는, 패턴의 과잉 보다는 패턴은 커녕 객체지향의 기초 조차도 제대로 안잡혀진 프로그래머가 훨씬 많은 것 같습니다. 그리고 패턴은 끊임없이 변화하고 발전하고 있습니다. GoF에 소개된 패턴들을 기본으로 삼아 웹이나 분산환경 등 시대의 변화에 따라 새로 생겨난 다양한 응용패턴들을 익히는 것도 상당한 도움이 됩니다.

예를들어 자바의 경우 웹개발 관련은 http://www.theserverside.com에 가보면 지금도 여러 개발자들이 참신한 패턴을 끊임없이 제시하고 이를 검증하는 작업이 진행되고 있습니다.

그런 의미에서 저는 패턴 공부에도 상당한 의미를 두고 싶습니다.

----------------------------
[서명] 그놈 한국 사용자 모임 - 그놈에 대한 모든 것! - 게시판, IRC, 위키, 갤러리 등등...

sDH8988L의 이미지

사실, 자동차와 바퀴의 관계는 제어 공학과 같은 부분에서 자주 등장하는

구조가 아닌가 합니다...

제어공학을 전공하지는 않았지만, 학부과정에서 수업을 들었고 Project를

수행해본 경험으로 미루어 봐서 바퀴도 자동차의 정보를 알아야 하고 자동차

역시 바퀴의 정보를 알아야 합니다... 물론, 어떤 Return 개념보다는 Observer

라는 관계가 휠씬 좋겠군요...

바퀴의 경우 자동차의 정보를 모를 경우에는 단순히 입력된 Input만 가지고는

정확한 행동 패턴을 결정할 수 없습니다...

제어 공학에서는 자신이 느끼는 질량과 다른 여러 가지 Factor를 종합해서

Dapping과 같은 것이 나오지요... 자신의 정보만 가지고는 가속과 같은 경우에

정확히 어떤 Cove를 그릴 지 알 수 없습니다...

Obserber는 실제 세계를 정확히 반영하기 위해서 필수 불가결한 요소라고

생각할 수 있습니다...

작용과 반작용에 대해서 생각하시면 쉬울 거라고 봅니다...

작용하는 객체는 상대 객체에 대한 정보를 알아야 적절한 동작이 가능하고

상대 객체 역시 작용 객체를 알아야 정확한 반작용을 만들어 낼 수 있습니다...

그럼 20000

mastercho의 이미지

그말씀은 결국 두 클래스간의 결합성을 매우 높이는게 아닌지요?

바퀴가 자동차를 알아야 한다?

어느 특별한 자동차에 특화된 바퀴를 만드는 셈이 되는데....

그건 객체지향과 오히려 동떨어집니다

만약 차의 인터페이스가 바뀌면

바퀴의 내부구조도 변경 되어야 한다는 말과 동일합니다

일반적인 상식으로 보자면 바퀴는 자동차에 관련됨이 없이

만들수 있는 컴포넌트로 봐야 할거 같다는 생각이 드네요

승자는 자기보다 우월한 사람을 보면 존경심을 갖고 그로부터 배울 점을 찾지만 패자는 자기보다 우월한 사람을 만나면 질투심을 갖고 어디 구멍난 곳이 없는지 찾는다.
- 하비스

fender의 이미지

예전에도 말씀 드렸지만, 제 생각엔 mastercho님께서 보다 구체적인 예제를 들어 주셨으면 좋겠습니다. 왜냐하면 자동차-바퀴의 경우 제가 생각해도 특별히 바퀴가 자동차의 참조를 가질 이유는 없다고 보기 때문입니다.

상호 참조가 일반적인 설계 방식인지 아닌지를 따지려면 우선 그런 설계가 필요한 구체적인 예를 놓고 따져보는게 좋을 것 같네요.

Quote:

그 말씀은 결국 두 클래스간의 결합성을 매우 높이는게 아닌지요?

바퀴가 자동차를 알아야 한다? 어느 특별한 자동차에 특화된 바퀴를 만드는 셈이 되는데....

그건 객체지향과 오히려 동떨어집니다

그리고 반복적으로 말씀드리지만, 바퀴가 자동차에 대한 참조를 가지고 있다고 해서 반드시 어느 '특별한' 자동차 클래스의 '구현'에 의존성을 가지는 건 아닙니다. 인터페이스나 추상 클래스를 쓰는 이유가 그런게 아니었던가요? :)

그럼~

----------------------------
[서명] 그놈 한국 사용자 모임 - 그놈에 대한 모든 것! - 게시판, IRC, 위키, 갤러리 등등...

mastercho의 이미지

Quote:
그리고 반복적으로 말씀드리지만, 바퀴가 자동차에 대한 참조를 가지고 있다고 해서 반드시 어느 '특별한' 자동차 클래스의 '구현'에 의존성을 가지는 건 아닙니다. 인터페이스나 추상 클래스를 쓰는 이유가 그런게 아니었던가요

제말은 구현 의존성이 아니라

클래스간의 의존성을 말하는 겁니다

말씀하신것은 무엇인지 알겠으나 ,

그것도 결국 인터페이스 클래스중

한쪽의 인터페이스를 변경하면 다른쪽의 인터페이스도 변경될수 밖에

없지 않냐를 묻고 있는겁니다

아주 간단히
예를 들어 A 는 B의 B.bbb()라는 인터페이스를 사용하고
B는 A.aaa()의 인터페이스를 사용한다고 봤을적에

B의 인터페이스 B.bbb()가 B.bbbbb()로 변경되면 A에서 사용되던 B.bbb()의
수정이 불가피 한게 아니냐는거죠

역도 마찬가지고요 , A,B둘다 서로에게 영향을 주고 있다고 봐야 하지 않은지요?

따라서 단방향일 경우보다 인터페이스를 수정하는데 어려움이 생길수 있는게 아닌가 싶습니다

그리고

상호 참조할만한 저절한 예는 찾지 못했습니다

물론 없지야 않겠지만 일반적인 어플리케이션에서 보자면
더욱 단방향이 설득력이 있다고 느껴지네요

승자는 자기보다 우월한 사람을 보면 존경심을 갖고 그로부터 배울 점을 찾지만 패자는 자기보다 우월한 사람을 만나면 질투심을 갖고 어디 구멍난 곳이 없는지 찾는다.
- 하비스

fender의 이미지

mastercho wrote:

한쪽의 인터페이스를 변경하면 다른쪽의 인터페이스도 변경될수 밖에 없지 않냐를 묻고 있는겁니다

당연히 그렇지요. 단, ICar나 ITire의 인터페이스가 그렇게 쉽게 바뀔 수 있다고 생각하지는 않습니다. T형 포드나 최신모델의 벤츠나 다 둥그런 바퀴로 굴러가는 건 똑같은거 아닌가요? :) (C/C++에서 인터페이스란 개념이 없어서 좀 혼동의 소지가 있지만, 여기서 이야기하는 인터페이스는 단순히 어떤 클래스에 어떤 메소드가 있다는 걸 나타내는게 아니라, instantiate 할 수 없는 추상적인 인터페이스를 뜻합니다. 즉, A.a();가 인터페이스가 아니라 A가 구현하는 IA가 인터페이스고, a()란 메소드는 IA에 들어가야 합니다).

모듈화된 프로그래밍을 하는 한 어느 정도의 커플링은 불가피한 측면이 있습니다. 옵저버를 쓰지 않고 자동차가 끊임없이 타이어의 상태를 점검하는 방식으로 구현해도 이는 의존성이 없어진 것이 아니라 타이어에 있던 의존성이 자동차 내부로 옮겨간 것뿐이라고 생각합니다.

설사 위에서 언급된 메시징과 같은 방법을 써도 어차피 프로그래머가 정한 메시지 값에 의존할 수밖에 없습니다.

클래스 간의 의존성을 무조건적으로 피하려면 클래스를 안쓰는 수밖에 없습니다. 극단적으로 생각하면, 클래스가 아니라 절차지향적인 함수로 바꿔도 함수의 내부 구현에 의존성을 가지게 되기 때문에 함수 대신 필요할 때마다 인라인으로 코딩할 수밖에 없습니다. 하지만 이런 습관이 올바른 설계인지는 생각해볼 문제가 아닐지요...

UML의 클래스 다이어그램을 봐도, 좋은 설계는 클래스간의 의존성과 상속관계를 나타내는 화살표만 봐도 전체의 구조와 프로그래머의 의도가 명확히 드러납니다. 이와 같이 문제는 의존성을 무조건 적으로 줄이는 것이 아니라 '잘 정의된' 의존성을 만드는 것이 아닌가 싶습니다.

그럼~

----------------------------
[서명] 그놈 한국 사용자 모임 - 그놈에 대한 모든 것! - 게시판, IRC, 위키, 갤러리 등등...

crimsoncream의 이미지

mastercho wrote:
Quote:
그리고 반복적으로 말씀드리지만, 바퀴가 자동차에 대한 참조를 가지고 있다고 해서 반드시 어느 '특별한' 자동차 클래스의 '구현'에 의존성을 가지는 건 아닙니다. 인터페이스나 추상 클래스를 쓰는 이유가 그런게 아니었던가요

제말은 구현 의존성이 아니라

클래스간의 의존성을 말하는 겁니다

말씀하신것은 무엇인지 알겠으나 ,

그것도 결국 인터페이스 클래스중

한쪽의 인터페이스를 변경하면 다른쪽의 인터페이스도 변경될수 밖에

없지 않냐를 묻고 있는겁니다

아주 간단히
예를 들어 A 는 B의 B.bbb()라는 인터페이스를 사용하고
B는 A.aaa()의 인터페이스를 사용한다고 봤을적에

B의 인터페이스 B.bbb()가 B.bbbbb()로 변경되면 A에서 사용되던 B.bbb()의
수정이 불가피 한게 아니냐는거죠

역도 마찬가지고요 , A,B둘다 서로에게 영향을 주고 있다고 봐야 하지 않은지요?

따라서 단방향일 경우보다 인터페이스를 수정하는데 어려움이 생길수 있는게 아닌가 싶습니다

그리고

상호 참조할만한 저절한 예는 찾지 못했습니다

물론 없지야 않겠지만 일반적인 어플리케이션에서 보자면
더욱 단방향이 설득력이 있다고 느껴지네요

써오신 글을 읽으면서 느낀 건데요.
인터페이스 혹은 컴포넌트를 객체와 등치되는 개념으로 생각하고 계신게 아닌지요? 그리고 C++가 C로 부터 물려받은 전처리개념의 산물로 남은 헤더파일의 꼬임과 oo의 객체간의 커플링을 같은 식으로 생각하고 계시고요. 아니시라면 죄송하지만 맞다면 이 부분을 잘 정리하고 넘어가실 필요가 있을 것 같습니다.

자동차-바퀴의 예가 계속 사용되니까 좀 도식적으로 정리를 해보면 만들고 싶은건 자동차이고 사용자는 운전자 입니다. 그리고 자동차가 자동차(사실은 바퀴를 제외한 자동차의 나머지 부분의 총합)와 바퀴로 구성되어 진다고 분석을 한거죠. 맞죠?

우선 어느 특별한 자동차에 특화된 바퀴를 만드는 것은 전혀 oo와 대치되는 일이 아닙니다. 바퀴가 고유한 속성과 그 속성을 다루는 method로 구성되고 이 method를 통해서만 사용된다면 충분히 oo스럽죠. 재사용성은 oo에서 optional한 장점이지 mandatory한 구성요소는 아닙니다.

상호참조에 의한 구현의 종속성은 oo의 여부를 결정하는 문제가 아니라 oo가 내재하고 있는 문제(커플링)로 봐야 할 것입니다. 이 커플링에 대한 해결책으로 인터페이스나 패턴이 생겨난 거죠. 무엇보다 바퀴가 자동차를 참조할 필요가 있냐 없냐가 중요한 거지 참조를 한다 안한다 혹은 그럼으로써 oo다 아니다는 중요한 문제가 아니겠죠.

하여간 바퀴가 자동차의 속성을 참조하기 위해서 특정 method를 invoke하면 이 바퀴의 구현은 자동차의 구현에 종속됩니다. 이 것을 막기 위해서 자동차의 interface를 만들고 바퀴는 이 interface를 통해서 필요한 속성을 얻어옴으로써 자동차의 구현으로 부터 독립되어집니다. 여기서 interface는 oo가 디커플링을 위해서 사용하는 메커니즘이지 oo 자체는 아닙니다. 인터페이스와 그 구현체인 컴퍼넌트가 oo의 오브젝트와 동일모듈일 경우가 많지만 그것이 컴퍼넌트가 오브젝트라는 뜻은 아니라는 거지요.

무슨 뜻이냐 하면 자동차의 인터페이스가 변경되면 바퀴의 구현이 바뀌므로 oo가 아니라는 생각을 가지고 계신거 같은데. 인터페이스는 자동차오브젝트와 바퀴오브젝트가 서로를 사용할때 커플링을 없애기 위해서 정의한 각 오브젝트와는 무관한 별도의 메커니즘이라는 겁니다. 자동차 오브젝트를 재구현 한다거나 subclassing 한다는 것과 자동차 인터페이스를 바꾼다는 것은 전혀 다른 얘기입니다. 자동차의 인터페이스는 자동차 오브젝트의 속성을 다루는 메소드가 아니라 특정 메소드 혹은 그 집합이나 속성이 외부로 노출되는 경로에 대한 명세일 뿐입니다. 이 명세를 전제로 한 바퀴오브젝트로서는 이 명세가 바뀌면 당연히 구현에 변경이 옵니다. 단 그 것은 바퀴오브젝트 또한 바뀐 명세를 사용할 필요가 있다는 뜻이죠. 바퀴로서는 전혀 바뀐 interface가 필요없다면 예전의 인터페이스를 가지고 있는 자동차오브젝트들과만 쓰여지면 되는 거죠. 이 자동차오브젝트들이 어떤 놈들이든 말이죠.

oo가 interface를 차용하는 또 하나의 이유는 잘못된 subclassing을 막고자 하는 의도도 있지요. 예를 들어 자동차에 스노우타이어를 붙인다. 이때는 타이어를 subclassing 하는 것이 자연스러우므로 문제가 안되지만 만약에 스키블레이드나 무한궤도를 붙인다고 했을때 코드 재사용성을 극대화하기 위해서 무리하게 subclassing을 하거나 매경우 class를 만들게 되면 부자연스러운 구현이거나 자동차의 코드내에 각 경우에 대한 커플링이 증가하게 되지요. 그래서 자동차 내부에서 바퀴를 바라 볼 때도 interface를 이용해서 자연스러운 구조로 디커플링을 해줄 수 있게 됩니다. 이 때 바퀴의 인터페이스는 역시 바퀴오브젝트의 속성이 아니고 바퀴오브젝트가 특정 속성을 외부로 노출하는 경로에 대한 명세일 뿐입니다. 바퀴와 스키블레이드는 전혀 다른 (클래스) 오브젝트이지만 같은 인터페이스를 구현하고 있는 컴퍼넌트가 되는 거지요.

이 때 이런 사용이 말그대로 패턴을 가지고 반복될 때 여기에 이름을 붙이고 정형화 한게 디자인 패턴이라고 생각합니다.

님께서 말씀하시는 c++ 클래스간의 상호참조의 어려움이리는 건 사실 컴파일 타임에 헤더파일이 꼬이는 문제로 c++의 전처리 메커니즘의 문제이지 패턴이 해결하고자하는 oo의 커플링과는 아무런 관계가 없는 걸로 보입니다.

오늘 우리는 동지를 땅에 묻었습니다. 그러나 땅은 이제 우리들의 것입니다.
아직도 우리의 적은 강합니다. 그러나 우리는 그들보다 많습니다.
항상 많을 것입니다.

mastercho의 이미지

Quote:
써오신 글을 읽으면서 느낀 건데요.
인터페이스 혹은 컴포넌트를 객체와 등치되는 개념으로 생각하고 계신게 아닌지요? 그리고 C++가 C로 부터 물려받은 전처리개념의 산물로 남은 헤더파일의 꼬임과 oo의 객체간의 커플링을 같은 식으로 생각하고 계시고요. 아니시라면 죄송하지만 맞다면 이 부분을 잘 정리하고 넘어가실 필요가 있을 것 같습니다.

어쩔수 없는 헤더파일의 꼬임 때문에 제가 쓰는 C++ 디자인이 일반적으로
단방향이다보니 , 약간 혼란이 온듯 싶습니다

Quote:
우선 어느 특별한 자동차에 특화된 바퀴를 만드는 것은 전혀 oo와 대치되는 일이 아닙니다. 바퀴가 고유한 속성과 그 속성을 다루는 method로 구성되고 이 method를 통해서만 사용된다면 충분히 oo스럽죠. 재사용성은 oo에서 optional한 장점이지 mandatory한 구성요소는 아닙니다.

맞습니다 -_-;; 재활용성을 중요시하는 개념에서 본건데, 너무 한쪽으로 치우쳐 생각하거 같네요

Quote:
자동차의 인터페이스가 변경되면 바퀴의 구현이 바뀌므로 oo가 아니라는 생각을 가지고 계신거 같은데. 인터페이스는 자동차오브젝트와 바퀴오브젝트가 서로를 사용할때 커플링을 없애기 위해서 정의한 각 오브젝트와는 무관한 별도의 메커니즘이라는 겁니다.

이부분은 오해가 있는듯 싶습니다. oo가 아니라고 주장한건 아니었습니다

전부터 이야기 해왔듯이 상호참조가 절대 악 이라고 말하기보단
설계적으로 문제가 많이 생길수 있지 않냐라는것이었습니다

지금은 1:1로 상호 참조하지만 복찹하게 여러클래스층으로 상호 참조하게
된다면 문제가 커질수 있다고 봐지는것이죠

단방향일 경우 계층적으로 수정작업이 쉽고 , 순차적으로 자기 자신의 내부만 신경쓰면 되는 식에 비교가 되었습니다.

Quote:

자동차의 인터페이스는 자동차 오브젝트의 속성을 다루는 메소드가 아니라 특정 메소드 혹은 그 집합이나 속성이 외부로 노출되는 경로에 대한 명세일 뿐입니다. 이 명세를 전제로 한 바퀴오브젝트로서는 이 명세가 바뀌면 당연히 구현에 변경이 옵니다. 단 그 것은 바퀴오브젝트 또한 바뀐 명세를 사용할 필요가 있다는 뜻이죠. 바퀴로서는 전혀 바뀐 interface가 필요없다면 예전의 인터페이스를 가지고 있는 자동차오브젝트들과만 쓰여지면 되는 거죠. 이 자동차오브젝트들이 어떤 놈들이든 말이죠.
oo가 interface를 차용하는 또 하나의 이유는 잘못된 subclassing을 막고자 하는 의도도 있지요. 예를 들어 자동차에 스노우타이어를 붙인다. 이때는 타이어를 subclassing 하는 것이 자연스러우므로 문제가 안되지만 만약에 스키블레이드나 무한궤도를 붙인다고 했을때 코드 재사용성을 극대화하기 위해서 무리하게 subclassing을 하거나 매경우 class를 만들게 되면 부자연스러운 구현이거나 자동차의 코드내에 각 경우에 대한 커플링이 증가하게 되지요. 그래서 자동차 내부에서 바퀴를 바라 볼 때도 interface를 이용해서 자연스러운 구조로 디커플링을 해줄 수 있게 됩니다. 이 때 바퀴의 인터페이스는 역시 바퀴오브젝트의 속성이 아니고 바퀴오브젝트가 특정 속성을 외부로 노출하는 경로에 대한 명세일 뿐입니다. 바퀴와 스키블레이드는 전혀 다른 (클래스) 오브젝트이지만 같은 인터페이스를 구현하고 있는 컴퍼넌트가 되는 거지요.

제가 C++만 해오다보니 인터페이스에 대한 개념이 약한거 같습니다
말씀 들어보니 그런거 같네요

제 C++ 수준에서 OOP를 이해할려다보니 한계가 좀 많이 드러난거 같습니다
순수 인터페이스에 대한 개념을 다시 공부해야 할듯 싶네요 :(

생각해보니
서로 얽힌다는건 추상클래스 이하수준의 이야기인거 같습니다 -_-;;

말씀 감사하고요

정리가 완전히 된것은 아니지만 뭔가
사고의 전환이 조금은 일어난듯 싶습니다

승자는 자기보다 우월한 사람을 보면 존경심을 갖고 그로부터 배울 점을 찾지만 패자는 자기보다 우월한 사람을 만나면 질투심을 갖고 어디 구멍난 곳이 없는지 찾는다.
- 하비스

지리즈의 이미지

죄송합니다. 요즘 새 업무 런치기간이라 자주 들리지 못하네요..

저도 사실은 디자인 패턴의 지지자는 아닙니다.
하지만, 업무에서 붙이치다 보니까 디자인 패턴 적인 사고가 아니면,
(KTF적인 사고가 아니라 --)
해결할 수 없는 경우가 많습니다.

전에 A--- T----B 이야기할 상대 인터페이스를 몰라도 되는 것에
대해 한 말씀드리겠습니다.
일단 업무 진행상 A와 B가 다른 사람에 의해서 작성될 경우,
그리고 서로 다른 작성자가 일정관계상 서로 협의할 수 없는 경우에
장점을 가지며, 실제로 업무를 진행하다 보면 이런 일이 다반사입니다.
A---T-----B 형태로 작성된 어떤 프로그램을
A----T----C 형태로 바꾸어야 하는 일이 비일비재하며,
실제로 A---T---B 프로그램을 작성한 사람은 다른 업무로 없거나(주로 출장)
퇴사했을 경우도 많습니다.

디자인 패턴을 사용하는 데 있어서 ROI측면에서 가장 중요한 점은
소프웨어의 성능적 최적화가 아니라 코드의 재사용입니다.
인터페이스만을 알면.. 내부에 어떠한 코드를 사용하고 돌아가는 지 굳이 알필요가 없는 Encaptulation의 장점이지만, 최소한의 인터페이스만을 알아도
된다는 것도 또한 업무를 크게 줄여주는 큰 장점입니다.
(잘 정리된 도큐먼트를 읽는 일조차 곤욕인 경우도 많습니다.)

이러한 재활용성의 장점외에도 소프트웨어의 특성상 어쩔 수 없이
디자인 패턴을 활용하지 않을 수 없는 경우도 있습니다.
전력계(파워미터)에서 값을 읽어 화면을 뿌려주는 범용 프로그램이 있다고 가정해 봅니다.
전력계의 종류는 수십가지이고 기능도 차이가 있으며
각각의 전력계마다 2~3가지 정도의
통신옵션을 가집니다.(GPIB,RS232,TCP..) 또한 GPIB의 경우
PC에 장착된 GPIB카드의 종류가 10여가지가 존재합니다.
화면 인터페이스도 장비의 종류나 사용자의 정의에 따라 차이가 발생할 수 있습니다.

이런 식의 요구 조건을 수용하기 위해서 개발자가 선택할 수 있는 길은 몇가가 안됍니다.
같은 기능이라도 제한된 몇개를 위한 경우와
제한되지 않는 N개를 위한 설계는 하늘과 땅차이지요.
이럴 때 I로 시작하는 클래스(인터페이스 클래스)가 존재한다는 것에 대한 감사가 절로 나옵니다.
이건 코드의 재활용성의 차원이 아니라, switch문으로 얼룩져야할
프로그램을 극적으로 구원해 줄 수 있는 구세주입니다.

재 생각에는 이렇습니다.
어쩌다 하나 만들고 말거면 디자인패턴 필요없습니다.
하지만, 업무상 유사한 프로그램을 계속적으로 만들어야 할 경우
디자인 패턴은 매우 큰 장점을 가집니다.
디자인 패턴 서적에 나오는 내용은 어디까지나 좋은 예일 뿐이고,
각각의 업무에 적합한 디지인패턴은 수년간 그 것을 개발한 사람들의
경험에 의해서 얻어지는 것이며 중요한 회사의 자산입니다.
디자인 패턴은 추상적이며 아주 주관적이고 각각의 요구에 따라
요구되는 기능도 다릅니다.
이것이만이 정답이라는 것도 없고.. 보편적인 것은 교과서를 쓰는 사람에게만
필요하다는 것이 제 생각입니다.

There is no spoon. Neo from the Matrix 1999.