티스토리 뷰

SOLID 5원칙

 

SOLID 5원칙이란 로버트 마틴이 소개한 객체지향 프로그래밍 설계의 5가지 기본 원칙을 말하며, 5가지의 앞글자를 따서 "SOLID" 라고 부르고 있습니다.

 

• SRP : 단일 책임 원칙(single responsibility principle)

OCP: 개방-폐쇄 원칙 (Open/closed principle)

LSP : 리스코프 치환 원칙 (Liskov substitution principle)

ISP : 인터페이스 분리 원칙 (Interface segregation principle)

DIP : 의존관계 역전 원칙 (Dependency inversion principle)

 

 

단일 책임 원칙 : SRP(single responsibility principle)

 

단일 책임 원칙(SRP)하나의 객체는 단 하나의 책임만 가져야 한다는 원칙을 말합니다.

다시 말해, 하나의 클래스는 하나의 기능만 가져야 한다는 것입니다.

 

쉽게 설명 하기 위해, 어느 새우 식당으로 예를 들어보겠습니다.

여기 새우 식당에서는 크게 3가지의 역할이 필요합니다.

첫 번째는 새우를 식당에 배달하는 배달기사.

두 번째는 새우를 요리하시는 요리사.

세 번째는 요리를 서빙하는 홀 직원.

이렇게 크게 세 역할이 있다고 가정해보겠습니다.

 

코드로 짜본다면, 세 개의 클래스가 있을 것이고 하나의 클래스당 하나의 역할을 가져야 합니다. (SRP 원칙)

하나의 클래스에서 세 개의 역할(책임)을 하게 된다면, SRP 원칙 위반이라고 할 수 있습니다.

즉, 한명이 배달도 하고 요리도 하고 서빙을 해서는 안된다는 이야기죠.

class Person {         // SRP 원칙 위반
    void rider();    // 배달
    void cook();     // 요리
    void serving();  // 서빙
}

 

위와 같이 하나의 클래스(객체)에 책임(기능)이 많아지게 되면, 클래스 내부에서 서로 다른 역할을 하는 코드끼리 강하게 결합이 될 경우가 크기 때문에 시스템이 복잡해질 수 있습니다.

 

예를 들어, 배달 역할에 대한 코드만 수정을 하였는데, 배달과 요리 그리고 서빙의 코드가 결합이 되어 있어 배달 역할에 대한 코드만 수정했지만, 요리와 서빙에 대한 코드도 수정을 하게되는 경우가 생기게 됩니다.

이럴 때, 유지보수가 어렵고 한 책임(기능)의 변경이 다른 책임(기능)의 변경으로 연쇄 작용이 일어날 수 있다는 것이죠.

 

아래와 같이 하나의 클래스가 하나의 책임만 가질 수 있도록 코드를 작성해야 합니다.

class Chef {
    void cook();
}

class Rider {
    void ride();
 }
 
class Staff {
    void serving();
 }

 

이렇게 단일 책임 원칙에 맞게 코드를 작성 한다면, 추후에 요리에 관한 코드를 수정할 때 Rider 와 Staff 클래스를 변경하지 않아도 됩니다.

 

즉, 클래스에 변경으로 인한 다른 클래스에 영향을 줄일 수 있기 때문에 응집도가 높아지고 결합도는 낮아지는 장점을 가질 수 있습니다.

 

 

개방-폐쇄 원칙 : OCP (Open/closed principle)

 

개방-폐쇄 원칙(OCP)기존의 코드는 수정하지 않으면서, 기능은 추가할 수 있도록 설계되어야 한다는 원칙입니다.

보통 확장에 대해서는 열려(open)있고, 수정에 대해서는 닫혀(close)있여야 한다는 의미로 정의되어 사용되고 있습니다.

객체 지향의 특징 중 '추상화''다형성'을 생각하시면 쉽게 이해하실 수 있습니다.

 

interface Animal {
    void speak();
}

class Cat implements Animal {
    void speak() {
        System.out.println("야옹");
     }
 }
 
 class Dog implements Animal {
     void speak() {
         System.out.printlb("멍멍");
     }
}

public class Main {
    public static void main(String[] args) {
        //Animal animal = new Cat();
        Animal animal = new Dog();
        
        //animal.speak() // 야옹
        animal.speak() // 멍멍

 

위 코드에서 볼수 있듯이, 클라이언트는 Animal 객체에 구현체로 Cat 과 Dog 을 골라서 사용할 수 있습니다.

다른 동물들을 사용하고 싶다면, Animal 인터페이스를 이용하여 확장할 수 있습니다.

 

확장할 때와 변경할 때 이미 구현되어 있는 구현체 혹은 Animal 인터페이스의 기존 Cat과 Dog 코드 수정은 하지 않아도 되며, 구현체만 새로 만들어 클라이언트 측에서 변경하여 확장/변경이 가능합니다. 

 

이것을 확장에 열려있으며 변경에는 닫혀있다고 이해할 수 있습니다.

 

 

리스코프 치환 원칙 : LSP (Liskov substitution principle)

 

리스코프 치환 원칙(LSP)로그램의 객체는 프로그램의 정확성을 깨뜨리지 않으면서 하위 타입의 인스턴스로 바꿀 수 있어야 한다는 원칙입니다.

 

하위 타입들이 상위 타입이 지정한 제약 조건들을 지키고, 상위 타입에서 하위 타입으로 변동이 일어나도 상위 타입의 역할을 문제없이 제공해야 한다는 것을 의미합니다.

 

쉽게 예를 들자면, 자동차 인터페이스가 있다고 가정해 보겠습니다. 

자동차 인터페이스에는 excel() 메서드가 정의되어 있는데, 이 자동차 인터페이스(상위 타입)를 구현하는 하위 타입들은 excel() 을 구현할 때 느리더라도 앞으로 가도록 코드를 작성해야 합니다.

왜냐하면 excel() 의 기능은 앞으로 가라는 기능이기 때문이죠.

 

여기서 자동차 인터페이스를 구현받은 하위 타입들(자식 객체)이 excel() 을 뒤로 가게 구현한다면 그것이 LSP 위반이라고 할 수 있습니다.

상위 타입이 지정한 제약 조건을 지키지 않았기 때문입니다.

 

 

인터페이스 분리 원칙 : ISP (Interface segregation principle)

 

인터페이스 분리 원칙(ISP)은 위에서 살펴본 단일 책임 원칙(SRP)와 비슷하게 생각하면 이해하기 쉽다.

SRP는 클래스의 단일 책임을 강조한다면, ISP는 인터페이스의 단일 책임을 강조한다고 볼 수 있기 때문입니다.

interface Person {   // ISP 원칙 위반
    void rider();    // 배달
    void cook();     // 요리
    void serving();  // 서빙
}
interface Chef {
    void cook();
}

interface Rider {
    void ride();
 }
 
interface Staff {
    void serving();
 }

 

인터페이스 분리 원칙(ISP)을 준수한다면, 인터페이스가 명확해지고 대체 가능성이 높아진다는 장점이 있습니다.

 

 

의존관계 역전 원칙 : DIP (Dependency inversion principle)

 

의존관계 역전 원칙(DIP)구현 클래스에 의존하지 않고, 추상 클래스 혹은 인터페이스에 의존 해야한다는 원칙입니다.

 

의존 관계를 맺을 때 변화하기 쉬운 것 또는 자주 변화하는 것보다는 변화하기 어려운 것, 거의 변화가 없는 것에 의존하라는 것입니다.