본문 바로가기
Delphi/문법

델파이(Delphi) 디자인패턴(Design pattern) 4탄

by MonoSoft 2021. 6. 1.
728x90
반응형

델파이(Delphi) 디자인패턴(Design pattern) 4탄

Strategy 패턴

 

Strategy 패턴을 쉽게 이해하는 방법은,

상속의 단점 두가지(불필요한 기능상속, 다중상속 불허) 보완하기 

위한 Interface 의 사용과 Abstract 패턴의

사용을 통한 메서드의 다형성 보장이다.

 

Strategy 패턴에 대해 구글링을 해 보면 

Abstract 패턴만을 강조하여 도식화 해 놓고 Strategy 패턴이라고 

알려주는 곳이 많은데, 이는 잘못된 설명이다.

 

Strategy 패턴의 진정한 뜻은 자주 바뀌는 

개발 요구사항에 대비해 최소의 비용을 들여 전략적으로 코드가

유연성을 가질 수 있도록 설계하는 기법이다.


코드의 유연성이란 말 안에는 기능의 구현없이 

완성된 모듈을 재빠르게 가져다 쓸수 있도록 하는 전략도포함된다.


예를 들어 차를 생산하는데,

차의 프레임은 고정되어 있지만 차에 부착되는 옵션은 

차량 주문자의 요구에 의해 수시로 바뀐다.

이럴때 제조에 필요한 주문자의 요구사항에서 

벗어난 모든 옵션부품이 든 거대한 상자를 프레임 

옆에 두고 차를 조립하는 것은 공간적인 낭비와 

기타비용이 많이 들 뿐 아니라 수많은 부품중에 필요한 부품을 찾는데

(가독성 떨어짐) 시간을 소모해야 하기 때문에 생산성 또한 떨어진다.

 

이런 경우를 프로그램에서는 간단한 기능 하나만 필요한데 

불필요하게 Super Class 를 상속받아 쓴다라고 표현한다. 

옛말에 "닭 잡는데 소잡는 칼 쓴다." 라는 말이 이 경우이다.

 

경우에 OOP에서 상속이 이에 해당된다.

상속은 경우에 따라 비용이 많이 들수 있으며, 

단일 상속밖에 되지 않는 치명적인 단점 대문에 코드의 유연성이

급격하게 떨어진다. 

 

이를 해결하기 위해서는 다중상속이 가능한 Interface 얘기를 빼놓을 수 없는데,

대부분의 강좌들이 그부분을 간과하여 설명하고 있다.

 

또한,  Interface 부분을 설명할때 Interface 는 원래 정의만 하고 

Interface 를 상속받은 클래스에서

모든 것을 구현한다라고만 설명하고 있어 

Stragy 패턴을 이해하는데 혼란을 주기도 한다.

하지만, 이는 엄연히 잘못된 설명이다.

Strategy 패턴은 완성된 기능들을 가진 모듈들을 

어떤 구현도 없이 재빠르게 가져다 쓸수 있도록 설계하여야

Strategy(전략)패턴이 가진 이름값을 제대로 할 수 있는 것이다.

 

기능 모두를 직접구현하여야 한다면 

이 난해한 패턴을 굳이 가져다 쓰는 이점이 무엇이란 말인가?

 

이는 Strategy 패턴에 대한 잘못된 오해이다.

 

지금 부터는 비유를 바꾸어 상속의 단점에 대해 좀더 상세히 설명하겠다.

가장 큰 단점은 상위클래스를 상속받은 하위클래스는 불필요한 기능까지 

모조리 상속받아야 한다는 것이다.

 

클래스의 덩치가 클경우에 이것은 경우에 따라 상당한 자원낭비가 될 수도 있다.

 

TClassA = class

 필요한 기능1;

 필요한 기능2;

 불필요한 기능;

end;

 

TClassB = class(TClassA)

 필요한 기능1;

 필요한 기능2;

 불필요한 기능;

 ....

end;

 

이 경우 클래스상속에서 불필요한 기능을 없애려면 

불필요한 기능을 override 하는 수 밖에 없다.

하지만 이런 과정은 상속받아가는 

하위 클래스의 종류가 많을 경우 관리가 무척 힘들어지게 된다.

그럼, override 안하고 불필요한 기능을 제거 할 수 없을까?

 

역발상하여 필요한 기능만 가져다 쓸 수 있는 방법을 찾으면 될 것이다.

그렇게 할려면 아래 같은 형태의 다중상속 구조가 성립되어야 한다.

 

TClassB = class(필요한 기능1을 가진 클래스, 필요한 기능2를 가진 클래스)

  필요한 기능1;

  필요한 기능2;

end;

 

하지만, 애석하게도 우리의 델파이는 다중상속이 

지원되지 않기 때문에 위 형태로는 클래스를 설계할 수 없다.


이 문제를 해결하려면 Interface 를 사용해야 한다.

Interface 자체 설명만도 내용이 방대하기 때문에 따로 설명하지는 않겠다.

 

TClassB = class( 필요한 기능1을 가진 Interface, 필요한 기능2를 가진 Interface )

  필요한 기능1;

  필요한 기능2;

end;

 

필요한 기능1과 2는 Interface의 메소드이다.

 

위 같은 형태로 해서 인터페이스로 부터 

꼭 필요한 기능들만 상속받아 쓸 수 있다.

 

이 부분에서 아까전 비유를 든 

자동차 옵션부품 얘기를 이해할 수 있으리라 본다.

 

이렇게 되면 제조에 꼭 필요한 부품만 옆에 두고 조립한다고 할 수 있다.

 

실질적인 사용 예로 만들면 다음과 같다.

 

TClassB = class( TInterfacedObejct, ISpeak, IWork )

  Speak; //필요한 기능1 - ISpeak 의 메소드;

  Work;  //필요한 기능2 - IWork 의 메소드;

end;

 

​Interface 객체를 생성하기 위해 TInterfacedObject 클래스를 상속받고

ISpeak와 IWork Interface 를 다중 상속받았다.

 

Speak 기능은 경우에 따라 동적으로 기능이

변경 될 수 있도록 만들어야 Strategy 패턴을 제대로

구현했다고 말할 수 있다.

 

다시말해, 사용자가 Speak 를 호출하더라도 

어떤 경우는 한국말을 해야 할때도 있고 경우에 따라서는

영어를 말해야 할때도 있다는 말이다.

 

한국어로 말하기와 영어로 말하기 기능구현 코드가 짧다면 모르겠지만 

코드가 엄청나게 길다고 가정해 본다면

외부 인터페이스 메소드 규격 Speak 를 지키면서, 

내부기능은 사용자의 요구사항에 따라 동적으로 

다른 메서드에서 작동될 수 있도록 하는 방법을 찾아야 한다.

 

이에 대한 솔루션은 다음과 같이 추상화 패턴을 도입하는 것이다.

 

ISpeak = interface

 speak;

end;

 

TSpeakKorean = (TInterfacedObject, ISpeak)

 speak;

end;

 

TSpeakEnglish = (TInterfacedObject, ISpeak)

 speak;

end;

 

* TSpeakKorean과 TSpeakEnglish 는 ISpeak 를 

상속받았기 때문에 상호교환이 가능하다.

이런 추상화 기법은 클래스에서도 가능하지만, 

다중상속을 위해 인터페이스를 쓴 이상 위처럼 인터페이스로 해결해야 한다.

 

지금까지의 설명으로 우리는 두마리 토끼를 잡았다.

 

첫째, 

다중상속을 통한 모듈을 꽂아 쓰는 방식을 채택함으로 클래스 확장성 확보

 

둘재, 

인터페이스 메서드의 추상화를 통한 메서드의 동적기능 선택 유연성 확보


마지막으로 꼭 필요한게 있는데, 

그것은 바로 완성된 모듈을 어떻게 꽂아 쓰는가 하는 것이다.

 

우리가 Inteface 라는 말을 들을땐 정의만 하고 구현할 수 없다라고 배웠다.

그래서 위 같은 방법으로 프로그램을 짜더라도 기능구현은 

결국 최종 클래스 사용자의 몫이 아닌가 라고 생각 할 수 있다.


만일 이글을 보고 계신 당신도 같은 생각이라면 하나 물어보자!

DirectX 는 Interface 를 이용한 COM 기술로 만들어 져 있다.

Interface 를 썻으니 DirectX 를 당신이 모조리 구현하여 써야 하는가?

 

나는 답하고 싶다. 그걸 내가 다 구현할 줄 알면 MS 에 취직하지 이자리에 있겠냐고?

 

여기서 우리는 꼭 하나 알아야 할 사실이 있다.

Interface 의 정의야 어찌 되었건 Interface 의 활용은 

그렇게 생각하면 오산이란 것이다.

 

Interface 의 최종목적은 개발언어에 상관없이 

이미 완성된 기능을 쉽게 가져다 쓰기 위함에 있는 것이지,

정의만 하자고 만들어 둔 개념이 절대 아니다.

 

정의만 하자고 그걸 만들었으면 생산성이 없어도 너무 없게 된다.

해서, 첨부파일에  Inteface 타입의 객체를 Interface 를 가지고 

어떻게 가져다 쓰는지에 대한

 

간단한 예제를 만들어 올려 두었으니 참조하여 보기 바란다.

본인이 글 재주가 부족하여 Strategy 패턴에 대한 부족한 부분은 코드로 대신하였으면 한다.

 

긴 글 읽어 주셔서 감사합니다.

 

Strategy+Parttern.zip
0.21MB

 

728x90
반응형

댓글