read

리팩토링을 하거나, 클래스등을 디자인할 때 가이드라인이 되어 줄 수 있는 몇 가지 원칙이 있다. Uncle Bob 이라고 알려져 있는 Robert C Martin 이 정리한 것인데, 다음과 같다.

  • Single responsibility principle
  • Open/Closed principle
  • Liskov substitution principle
  • Interface segragation principle
  • Dependency inversion principle

앞 글자를 따서 SOLID 원칙이라고 한다. 각각에 대해 간단하게 살펴보자.

Single responsibility Principle

하나의 객체는 한가지 일에만 집중하도록 한다는 이야기다. Pitcher 라는 클래스가 있으면 공 던지는 일에 대한 일을 하도록 디자인 되어야지, 방망이를 휘둘러 공을 치는 일까지 담당하지 않도록 해야 한다.

Open/Closed Principle

Open for extension, Closed for modification. 새로운 기능을 확장하기는 좋아야 하나, 기존의 것을 수정하는 것은 막아야 한다라는 뜻인데, Object 개념이 이미 자리 잡은 상태에서는 이 문장이 쉽게 수긍이 갈 수 있으나, 그 외의 경우라면 모순처럼 보일 수도 있다. 어쨌든 이 원칙이 이야기하고자 하는 바는 Abstraction 을 통해 객체의 인터페이스를 상속을 거쳐 확장 가능하게 함으로써 기존의 소스코드는 가능하면 변경을 막겠다는 것이다.

Liskov substitution Principle

if S is a subtype of T, then objects of type T in a program may be replaced with objects of type S without altering any of the desirable properties of that program

리스코프 치환 원칙. 자식 클래스는 부모 클래스의 역할도 할 수 있어야 한다는 것이다. 확장된 인터페이스를 사용하다가, 기반 인터페이스를 쓰더라도 문제 없이 작동한다는 것을 보증한다. 일종의 하위 버전 호환성, backward compatibility 와도 유사하다.

Interface Segragation Principle

인터페이스를 명확히 나누어서 정의해두라는 뜻이다. 하나의 통짜 인터페이스에 필요한 메소드들을 모두 선언해두지 말고, 각각의 용도에 따라 나눠서 인터페이스를 선언하도록 한다. 그렇게 나눈 뒤 각 인터페이스들이 공유하는 부분이 발생하면, 이것을 따로 빼어 재사용할 수 있게 수정한다.

Dependency Inversion Principle

A. High-level modules should not depend on low-level modules. Both should depend on abstractions.

B. Abstractions should not depend upon details. Details should depend upon abstractions.

B 항은 위의 LSP(Liskov Substitution Principle) 와도 일맥상통하는 부분이 있다. A. 항은 용어 그 자체로 선뜻 이해되지 않는데, 여기서 말하는 dependency 는 사용관계, UML 에서 말하는 aggregation 에 해당한다. 즉 인터페이스 위부의 어떤 코드가 (high-level module) 가 인터페이스 내부의 concrete class 를 직접 쓰지 말고 interface 를 통해야 한다는 것.

예를 들어 어떤 라이브러리에 Apple 이라는 클래스가 있다고 하자. 이 클래스는 IFruit 이라는 인터페이스를 상속받았다고 한다면, 이 라이브러리를 쓰는 코드는 다음과 같은 식으로 코딩한다.

IFruit firstObj = new Apple();

human.Eat(firstObj);

이렇게 함으로써 현재 코드 -> 라이브러리내의 Apple 클래스에 대한 dependency 를 IFruit 을 통하게 되었다. 인터페이스를 통하지 않았다면,

Apple firstObj = new Apple();

human.Eat(firstObj)

이렇게 코딩하게 되어 현재 코드가 라이브러리 내의 Apple 이라는 클래스에 dependency 가 생겼지만, 인터페이스를 통하게 함으로써 Apple 을 포함한 라이브러리가 IFruit 라는 인터페이스를 구현하도록 강제했다. 이 강제성으로 인해 dependency 가 low-level 에서 high-level 쪽으로 뒤집혔다고 보게 된다는 뜻.

References

Blog Logo

Ki Sung Bae


Published

Image

Gsong's Blog

Developer + Entrepreneur = Entreveloper

Back to Overview