티스토리 뷰

donaricano-btn
반응형

몇년 전, 어떤 나라의 교통국에서 자국의 교통망 시뮬레이션을 위한 프로그램을 만들었습니다. 물론 그들이 직접 만든 것은 아니고 IT강국인 한국의 한 SI업체에 외주를 맡긴 것이죠. 결과적으로 그들의 선택은 아주 훌륭한 것이었습니다. 수퍼 개발자가 넘쳐나는 대한민국의 SI업체인 만큼 기간내에 완벽한 시뮬레이션 프로그램을 만들어서 납품했기 때문이죠.

 

올해 봄, 이 회사의 신입 개발자로 채용되게 된 브래드는 얼마 전 부터 이 교통 시뮬레이션 프로젝트의 유지보수 업무를 맡고 있습니다. 뛰어난 선배들이 워낙 완벽하게 만들어 둔 프로젝트이기 때문에 브래드의 업무는 그저 기존의 코드를 보고 프로젝트 구조를 이해하는 정도에 불과합니다. 

 

브래드는 이 시뮬레이션에서 핵심이 되는 Vehicle 객체의 구조를 다음 그림과 같이 파악했습니다.

 

 

Vehicle 객체는 실제 도로위에 돌아다니는 각종 탈 것 들을 의미합니다. 그리고 이 클래스를 상속받은 많은 종류의 차량들이 있죠. 가령 예를들면 스포츠카나 세단, SUV같은 것들입니다. 브래드는 객체지향 개발자였기 때문에 그에게 이런 상속 구조를 파악하는 것은 아주 쉬운 일이었죠.

 

그러던 어느날 기술의 발전이 드디어 하늘늘 날아다니는 자동차를 만들어 냈습니다. 이 프로그램을 운영하고 있던 교통국은 깜짝 놀랄 수 밖에 없었죠. 기존의 프로그램으로는 하늘을 날아다니는 자동차를 시뮬레이션 할 수 없었기 때문입니다. 교통국은 이 프로그램을 만든 회사에 이 문제를 해결해 달라고 요청했고, 이 업무는 유지보수를 담당하고 있던 브래드가 맡게 되었습니다.

 

브래드는 생각했죠. 'Vehicle 클래스에 fly() 메소드만 구현하면 되겠군! 역시 객체지향 개발자가 되길 잘했어!' 라구요. 그는 수퍼 객체지향 개발자였기 때문에 아주 쉽게 클래스의 구조를 다음 그림과 같이 수정했습니다.

 

 

정말 간단하죠? 이 맛에 객체지향 개발자를 하는것 아니겠어요? Vehicle 클래스에 fly() 메소드를 추가하는 것 만으로 날아다니는 자동차를 간단하게 구현한 브래드는 배포를마치고 기분좋게 퇴근 했습니다.

 

브래드가 기분좋게 퇴근해 워라밸을 즐기고 있는 동안 프로그램을 사용하던 교통국에서는 난리가 났습니다. 바로 어제까지만 해도 잘 작동되던 시뮬레이션의 자동차들이 갑자기 모두 하늘을 날아다니고 있는 모습을 봤기 때문입니다. 교통국은 당장 이 문제로 브래드 회사의 사장님에게 항의를 했고 사장님은 꿀잠을 자려고 누워있던 브래드를 당장 호출하게 됐죠.

 

아닌 밤중에 날벼락을 맞은 브래드는 한밤중에 다시 출근해서 뭐가 문제인지 고민하기 시작했습니다. 혹시 여러분은 어떤 문제가 발생했는지 눈치 채셨나요? 네 맞습니다. 상위 클래스에 fly() 메소드를 구현한 덕분에 모든 하위 클래스가 fly() 메소드를 가지게 되었고 결국 시뮬레이션내의 모든 자동차들이 날아다니기 시작했던 것입니다.

 

브래드는 문제를 깨닫고 새로운 구현 방법을 고민하기 시작했습니다. 그는 클래스간의 상속관계를 포기하고싶지 않았지만 모든 클래스에 fly() 메소드가 있어야 하는 것이 아니기 때문에 지금처럼 클래스를 통한 상속 방법을 사용할 수는 없었습니다. 그래서 브래드는 인터페이스를 사용하기로 했습니다.

 

 

브래드는 클래스의 구조를 위 그림과 같이 바꾸었습니다. Flyable 이라는 인터페이스를 만들고 날아다닐 수 있는 자동차 클래스는 이 Flyable 인터페이스를 구현하도록 한 것이죠. 이제 날아다닐 수 있는 종류의 탈것들은 이 Flyable 인터페이스를 구현하기만 하면 됩니다. 또 기술이 발전해서 다른 기능을 가진 탈것들이 등장한다면 새로운 인터페이스를 추가하기만 하면 됩니다.

 

브래드는 서둘러 수정한 코드를 배포하고 교통국의 연락을 기다렸습니다. 아니나 다를까 새로 바꾼 코드는 아주 잘 작동한다고 합니다.

 

그러나 브래드는 여전히 한가지 걱정거리를 가지고 있었습니다. 이제 새로운 탈것이 생길 때 마다 기능과 관련된 인터페이스를 구현하고 기능과 관련된 메소드를 매번 재정의 해야 합니다. 그건 여간 귀찮은 일이 아닐 수 없죠. 조금 더 쉬운 방법은 없을까요?

 

일단 상속을 사용해서는 더이상 이 문제를 해결할 수 없다는 것을 인정해야만 합니다. 인터페이스를 통한 방법도 문제점이 있기는 마찬가지였죠. 이럴때 기억할만한 디자인 원칙 한가지가 있습니다.

 

애플리케이션에서 달라지는 부분을 찾아내고, 달라지지 않는 부분으로부터 분리시킨다.

 

다시말해 바뀌는 부분을 따로 뽑아내서 캡슐화 시키라는 것입니다. 그렇게 되면 나중에 바뀌지 않는 부분에는 영향을 미치지 않은채로 그 부분만 고치거나 확장할 수 있게 됩니다. 

 

그렇다면 브래드가 고민하고 있는 코드에서 바뀌는 부분과 그렇지 않은 부분은 어디일까요? 말할 것도 없이 fly() 메소드 입니다. 이 부분을 어떻게 분리할 수 있을까요?

 

일단 이 부분은 Vehicle 클래스에서 더이상 구현하지 않습니다. 대신 이 메소드만을 목적으로 하는 클래스를 따로 만들어 보겠습니다. 예를들면 Fly나 FlyNoWay같은 식으로 말이죠. 이 클래스들은 결국 fly() 메소드를 구현한 구현체들입니다. 그렇다면 다시말해 fly() 메소드를 정의한 인터페이스의 구현체라고 볼 수도 있겠죠. 이 인터페이스의 이름을 FlyBehavior라고 해 봅시다. 결국 다음 그림과 같이 나타낼 수 있을 것입니다.

 

 

그렇다면 이제 이 날아다니는 방법에 관한 부분을 기존의 Vehicle 클래스에 포함시켜야 합니다. 이것은 클래스의 인스턴스 변수로 FlyBehavior를 추가하는 것으로 완성됩니다.

 

 

이제 새롭게 바뀐 구조가 이전에 비해 얼마나 더 유연하고 재사용이 간편한지 느낄수 있으신가요? 이 방법에서 우리는 상속을 사용하지 않았습니다. 대신 한클래스의 멤버 변수로 다른 클래스를 포함시켰죠. 이런식으로 두 클래스를 합치는 방법을 구성(composition) 이라고 합니다. 

 

상속보다는 구성을 활용한다.

 

이제 여러분께 솔직하게 말씀드려야 할 때가 온 것 같군요. 브래드의 고민을 돕기 위해 고안해 낸 방법은 사실 제가 만들어낸 것이 아닙니다. 이미 너무 많은 개발자들이 사용하고 있는 디자인 패턴 중 하나인 전략패턴(strategy pattern)입니다. 

 

이 포스트를 읽고 계실 대다수 여러분들은 아마 이 패턴의 이름은 몰랐더라도 이미 책이나 실무에서 이런 패턴을 가진 클래스를 많이 보셨을 것입니다. 그만큼 대중적이고 강력한 디자인 패턴이기 때문입니다. 이 패턴에 대한 조금 더 정확한 정의를 소개하고 마치도록 하겠습니다.

 

전략패턴(Strategy Pattern)에서는 알고리즘군을 정의하고 각각을 캡슐화하여 교환해서 사용할 수 있도록 만든다. 전략패턴을 활용하면 알고리즘을 사용하는 클라이언트와는 독립적으로 알고리즘을 변경할 수 있다.

 

참고


위 내용은 한빛미디어에서 출판된 Head First Design Patterns 중 1장의 내용을 임의대로 수정하고 요약한 것입니다.

반응형

'language > JAVA' 카테고리의 다른 글

데코레이터 패턴(Decorator Pattern)  (0) 2020.02.24
옵저버 패턴(Observer Pattern)  (0) 2020.02.12
String 문자열 객체간의 비교  (0) 2019.12.17
추상 클래스와 인터페이스의 차이  (3) 2019.10.14
final 키워드  (0) 2019.10.12
donaricano-btn
댓글
반응형
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함