SOLID란 2000년대 초반 로버트 마틴이 객체 지향 프로그래밍 및 설계에 대한 5가지 원칙을 소개 한 것인데, 유지보수와 확장이 쉬운 소프트웨어를 만들고자 할 때 이 원칙을 적용할 수 있다. 코드의 가독성을 높이고 확장이 쉬운 구조를 만들기 위한 지침이다.

SOLID원칙은 5가지 원칙들에서 각 원칙의 두문자를 따와 만든 명칭이다. 각 원칙이 무엇인지 살펴보자.

단일 책임 원칙 (Single Responsibility Principle)

객체 지향 프로그래밍에서 단일 책임 원칙(single responsibility principle)이란 모든 클래스는 하나의 책임만 가지며, 클래스는 그 책임을 완전히 캡슐화해야 함을 일컫는다. 클래스가 제공하는 모든 기능은 이 책임과 주의 깊게 부합해야 한다.

로버트 마틴은 어떤 클래스나 모듈은 변경하려는 단 하나 이유만을 가져야 한다고 결론 지엇다. 예를 들어서 특정 데이터를 분석하고 서버에 전송하는 모듈을 생각해 보자. 이 모듈은 두 가지 이유로 변경될 수 있다. 첫 번째로 데이터를 분석 하는 알고리즘 때문에 변경될 수 있다. 두 번째로 서버에 전송하는 형식 때문에 변경될 수 있다. 단일 책임 원칙에 의하면 이 문제의 두 측면이 실제로 분리된 두 책임 때문이며, 따라서 분리된 클래스나 모듈로 나누어야 한다. 다른 시기에 다른 이유로 변경되어야 하는 두 가지를 묶는 것은 나쁜 설계일 수 있다.

한 클래스를 한 관심사에 집중하도록 유지하는 것이 중요한 이유는, 이것이 클래스를 더욱 튼튼하게 만들기 때문이다. 앞서 든 예를 계속 살펴보면, 편집 과정에 변경이 일어나면, 같은 클래스의 일부로 있는 출력 코드가 망가질 위험이 대단히 높다.

개방 폐쇄 원칙 (Open Closed Principle)

개방-폐쇄 원칙(OCP, Open-Closed Principle)은 ‘소프트웨어 개체(클래스, 모듈, 함수 등등)는 확장에 대해 열려 있어야 하고, 수정에 대해서는 닫혀 있어야 한다.’는 프로그래밍 원칙이다.

내가 작성한 코드 또는 모듈중에서 어떠한 내용을 수정하기 위해 다른 코드나 모듈까지 수정해야 한다면, 이와 같은 코드는 수정이 어렵다. 개방-폐쇄 원칙은 시스템의 구조를 올바르게 구성하여 나중에 이와 같은 유형의 변경이 더 이상의 수정을 유발하지 않도록 하는 것이다. 개방-폐쇄 원칙이 잘 적용되면, 기능을 추가하거나 변경해야 할 때 이미 제대로 동작하고 있던 원래 코드를 변경하지 않아도, 기존의 코드에 새로운 코드를 추가함으로써 기능의 추가나 변경이 가능하다.

개방-폐쇄 원칙은 객체 지향 프로그래밍의 핵심 원칙이라고 할 수 있다. 개방-폐쇄 원칙을 따르지 않는다고 해서 객체지향 언어를 구현이 불가능한 것은 아니지만 이 원칙을 무시하고 프로그래밍을 한다면, 객체 지향 프로그래밍의 가장 큰 장점인 유연성, 재사용성, 유지보수성 등을 결코 얻을 수 없다. 따라서 객체 지향 프로그래밍 언어에서 개방-폐쇄 원칙은 반드시 지켜야할 기본적인 원칙이다.

리스코프 치환 원칙 (Liskov Substitution Principle)

치환성은 객체 지향 프로그래밍 원칙이다. 클래스 S가 클래스 T의 부모 클래스라면 별다른 변경없이 부모 클래스 T를 자식클래스 S로 치환 할 수 있어야 한다는 원칙이다. 즉 업캐스팅된 인스턴스가 논리적으로 그 역할이 문제가 없어야 하는것이다. 이러한 원칙을 다른 용어로 행동적 하위형 이라고 한다.

리스코프의 원칙은 객체 지향 프로그래밍 특징에 관한 몇가지 표준적인 요구사항을 강제한다.

  • 하위클래스에서 메서드 파라미터의 반공변성
  • 하위클래스에서 반환형의 공변성
  • 하위클래스에서 메서드는 상위클래스 메서드에서 던져진 예외사항을 제외하고 새로운 예외사항을 던지면 안된다.
  • 하위클래스에서 선행조건은 강화될 수 없음
  • 하위클래스에서 후행 조건은 약화될 수 없음
  • 하위형에서 상위형의 불변조건은 반드시 유지되어야 한다.

Tip. 공변성, 반공변성, 불변성 이란?
다음과 같이 차례로 상속받는 타입이 있다고 가정한다. A ←B ← C

공변성의 예를들면 List<? extends B> 란 B를 상속 받는 타입으로 이루어진 리스트가 있다면 List<C>를 사용할 수 있다는 내용이다.

반공변성의 예를들면 List<? extends B> 란 리스트가 있을 때 List<A>를 사용할 수 있다는 것이다. 물론 A의 부모 타입으로도 치환이 가능하다.

불변성은 위의 공변성과 반공변성을 허용하지 않는 경우이다.

인터페이스 분리 원칙 (Interface Segregation Principle)

인터페이스 분리 원칙은 어떠한 클래스가 자신이 이용하지 않는 메서드에 의존하지 않아야 한다는 원칙이다. 인터페이스 분리 원칙은 큰 덩어리의 인터페이스들을 구체적이고 작은 단위들로 분리시킴으로써 클래스들이 꼭 필요한 메서드들만 이용할 수 있게 한다. 이와 같은 작은 단위들을 역할 인터페이스라고도 부른다. 인터페이스 분리 원칙을 통해 시스템의 내부 의존성을 약화시켜 리팩토링, 수정, 재배포를 쉽게 할 수 있다.

예를들어 독수리 객체를 만들어보자

public abstract class Bird {
    abstract void fly();
    abstract void cry();
}

public class Eagle extends Bird {
    @Override
    public void fly() {...}

    @Override
    public void cry() {...}
}

 

Bird라는 추상클래스를 만들어서 새의 울음소리를 내고 날 수 있는 기능을 가진 메소드를 만든 뒤 Bird를 상속받은 Eagle을 만들었다. 이때 Penguin 클래스를 만든다면 펭귄은 새지만 날지는 못하므로 fly() 메소드를 가지면 ISP 인터페이스 분리 원칙에 어긋날 수 있다. 다음과 같이 코드를 수정할 수 있다.

public abstract class Bird {
    abstract void cry();
}

public interface Flyable {
    void fly();
}

public abstract class FlyableBird extends Bird implements Flyable {...}

public class Eagle extends FlyableBird {
    @Override
    public void fly() {...}

    @Override
    public void cry() {...}
}

public class Penguin extends Bird {
    @Override
    void cry() {...}
}

fly() 메소드를 인터페이스로 분리하고 날 수 있는 새에게만 구현함으로써 펭귄은 사용하지 않는 fly() 메소드를 가지지 않을 수 있게되어 ISP원칙을 지킬 수 있게 되었다. 펭귄에게 swim() 메소드를 추가 하고 싶다면 Swimmable 인터페이스를 만들어 볼 수도 있다.

의존 역전 원칙 (Dependency Inversion Principle)

객체 지향 프로그래밍에서 의존 역전 원칙은 모듈들을 분리하는 특정 형식을 지칭한다. 이 원칙을 따르면, 상위 계층이 하위 계층에 의존하는 전통적인 의존 관계를 역전시킴으로써 상위 계층이 하위 계층의 구현으로부터 독립되게 할 수 있다. 이 원칙은 다음과 같은 내용을 담고 있다.

  • 첫째, 상위 모듈은 하위 모듈에 의존해서는 안된다. 상위 모듈과 하위 모듈 모두 추상화에 의존해야 한다.
  • 둘째, 추상화는 세부 사항에 의존해서는 안된다. 세부사항이 추상화에 의존해야 한다.
    이 원칙은 ‘상위와 하위 객체 모두가 동일한 추상화에 의존해야 한다’는 객체 지향적 설계의 대원칙을 제공한다.

카테고리: Dagger2Java

0개의 댓글

답글 남기기

Avatar placeholder

이메일은 공개되지 않습니다. 필수 입력창은 * 로 표시되어 있습니다.