주요글: 도커 시작하기
반응형

거의 모든 객체 지향 관련 서적에서 설명하는 기본 객체 지향 설계 원칙이 있는데, 객체 지향 기초의 마지막 이야기로 이들 원칙들을 살펴보도록 하자.


이전글


단일 책임 원칙(SRP)


첫 번째 원칙은 '단일 책임 원칙(SRP; Single Responsibility Principle)'이다. 이는 객체는 단 한 개의 책임(역할)만을 가져야 한다는 내용으로, 객체를 변경해야 하는 이유는 단 하나여야 한다는 원칙이다. 예를 들어, Book 클래스가 JSON으로의 변환을 처리한다고 해 보자. 이 경우 클래스는 다음과 같이 정의될 것이다.


public class Book {

    public int calculatePrice() { ... }

    public String toJson() { ... }

}


만약 Book를 XML이나 HTML로도 표현해야 한다면? 이런 식으로 변환해야 할 대상이 늘어날수록 Book 클래스의 함께 변하게 된다. 이 외에도 JSON이나 XML의 구조가 변경되더라도 Book 클래스의 코드가 변경된다. Book 클래스의 코드가 표현 방식의 추가나 변경에 따라서 함께 바뀌는 증상이 발생하는데, 그 이유는 Book 클래스가 많은 책임을 지고 있기 때문이다.


책임을 많이 질수록 클래스 내부에서 서로 다른 역할을 위한 코드들 간에 강하게 결합될 가능성이 높아진다. 예를 들어, JSON으로 변환하는 코드와 HTML로 변환하는 코드와 계산을 위한 코드 중 어딘가가 연결되어 있을 수 있고, 이런 경우 계산 공식의 변화가 JSON 변환 코드에 변화를 불러 일으킬 수도 있다.


따라서, 한 객체는 하나의 책임만 갖도록 설계해야 한다. 예를 들어, 앞서 Book 클래스의 경우 책 자체의 정보를 처리하는 Book 클래스와 JSON이나 XML로 표현을 변경해주는 JsonConverter, XMLConverter 클래스로 분리해주어야 한다.


이렇게 한 객체가 하나의 책임만을 갖도록 함으로써 각 객체를 변경시키는 이유는 한 가지로 좁혀진다. 책과 관련된 계산 변경이 필요하면 Book만 변경되며, JSON이나 XML 형식이 변경되면 JsonConverter 클래스와 XMLConverter 클래스에만 변화가 생긴다. (물론, Book의 정보 자체에 변화가 생기면 그 변화는 두 Converter에 영향을 준다.)


의존 역전 원칙(DIP)


의존 역전 원칙(Dependency Inversion Principle)은 구현 클래스가 아닌 인터페이스 타입을 사용하라는 규칙이다. 이는 이미 앞서 '객체 지향 기초 3, 유연함 http://javacan.tistory.com/entry/OO-Basic-3-Flexibility'에서 말했던 내용과 동일하다. 구현 클래스는 자주 변경될 가능성이 높기 때문에, 변화 가능성이 상대적으로 적은 추상 타입이나 메서드에 의존하면 변화에 따른 영향을 최소화할 수 있다.



예를 들어, 위 그림에서 보듯이 SomeClient 클래스는 구현 클래스인 FtpLogCollector를 사용(의존)하는 것 보다, 추상 타입인 LogCollector를 사용하는 것이 변화에 영향을 덜 받게 된다. 이 경우 FtpLogCollector 자체의 구현이 변경되거나 심지어 DBLogCollector로 구현 클래스가 바뀌더라도 SomeClient는 거의 영향을 받지 않게 된다.


개방 폐쇄 원칙(OCP)


개방 폐쇄 원칙(Open-Closed Principle)은 특정 클래스(또는 모듈 등)는 그 클래스를 수정하지 않으면서 확장(extension) 가능해야 한다라는 원칙이다. 객체 지향에서 OCP는 크게 두 가지 방법으로 구현할 수 있다.

  • 상속을 통한 구현
  • 조립을 통한 구현

상속을 통한 구현은 스프링 2 버전의 MVC 컨트롤러 구성에서 확인할 수 있다. 아래 그림은 스프링 MVC의 Controller 타입의 계층 구조이다. 이 구조를 보면 AbstractController 클래스를 수정하지 않고, 상속을 통해 기능을 확장한 것을 알 수 있다. 예를 들어, 파라미터를 커맨드 객체로 받기 위해 AbstractController 클래스를 수정하지 않고 이 클래스를 상속받은 BaseCommandController 클래스를 만들었다. 즉, 변경을 하지 않으면서(Closed for modification) 확장에는 열려(open for extension)있는 것이다. 추가로 아래 클래스들은 각각 한 가지 역할만을 갖도록 설계되어 있다. 상속을 통해 기능을 확장하는 경우에도 SRP를 잘 지키고 있다.



OCP를 실현하는 두 번째 방법은 조립(composition)이다. 조립에 대한 내용은 '객체 지향 기초 4, 상속 보단 조립 http://javacan.tistory.com/entry/OO-Basic-4-Composition-Over-Inheritance)에서 살펴봤었는데, 조립과 DIP 그리고 상속이 함께 맞물리는 방법으로 OCP를 구현하게 된다. 아래 그림은 의존 역전 원칙을 설명할 때 사용한 클래스 다이어그램인데, 이 구조를 OCP에도 그대로 사용할 수 있다.



위 그림에서 SomeClient는 로그를 수집하는 기능을 조립하는 방식으로 구현하고 있다. SomeClient를 사용하는 또 다른 코드는 SomeClient가 로그 수집을 직접 수행하는지 조립을 통해 수행하는지는 모를 것이다. SomeClient는 조립을 통해 로그 수집 기능을 구현하고 있기 때문에, 로그 수집 방법을 개선할 필요가 있을 때 SomeClient는 수정하지 않으면 기능을 확장할 수 있다.


참고자료


제가 쓴 객체 지향 입문서입니다.


http://www.aladin.co.kr/shop/wproduct.aspx?ISBN=8969090010  에서 확인하실 수 있습니다.

+ Recent posts