본문 바로가기
Programming

객체지향 설계 원칙 SOLID 원칙

by whitele 2024. 8. 2.
반응형

SOLID 원칙

객체지향의 5가지 핵심 설계 원칙이다. 이외에도 여러 가지 마이너 한 원칙들이 존재하지만 SOLID만 원칙을 준수해도 좋은 코드가 나온다.

로버트 마틴이 정리한 좋은 객체지향 설계의 5가지 원칙이다.


1. SRP (Single Responsibility Principal) 단일 책임 원칙

하나의 책임을 가지는 클래스를 가져야 한다.

하나의 책임을 가진다는 것은 클래스 내에서 해야 할 일이 많지 않고 명확하게 하나의 일을 수행해야 한다는 것이다. 중요한 핵심은 변경되는 어떤 경우라도 하나의 이유로만 변경이 되어야 한다는 것이다.

너무 많은 것을 알고 여러 책임을 가지고 있다면 다양한 이유로 클래스 하나가 변경될 수 있다. 이러한 경우는 SRP 원칙이 지켜지지 않았다고 할 수 있다.

다음과 같은 예시는 SRP를 위배한다고 볼 수 있다.

public class Member{
    public void subscribe();
    public void pay();
    public String createJsonInfo();
}

구독, 결제, Json 정보 등 여러 가지 책임을 가지고 있다. Member의 역할이 제대로 보이지도 않고 subscirbe만 변경되거나 pay가 변경될 수 도 있다.

이런 경우 결합된 책임을 분리하여 별도의 클래스로 두어야 한다.


2. OCP (Open Closed Principal) 개방 폐쇄 원리

소프트웨어의 요소에는 확장에 열려있어야 하지만 변경에는 닫혀야 한다.

기능이 추가되거나 소프트웨어의 변경사항이 발생할 경우 기존의 코드가 변경되지 않고 수정하거나 추가할 수 있어야 한다.

특정 변경사항이 있어도 그 변경되는 책임에 대해서만 변경이 되어야 하며 다른 클래스에 대해서는 변경이 일어나게 돼서는 안 된다.

OCP는 재사용성과 관련된 중요한 원칙이다. 추상화(Abstract)와 상속(inheritance)을 이용해 OCP원칙을 준수할 수 있다.


3. LSP(Listov Substitution Principal) 리스코프 교체 원리

하위 파생클래스는 베이스 타입에 교체될 수 있어야 한다.

어떤 요구도 없이 슈퍼 클래스에서 받는 매개변수는 서브클래스가 무조건 받아야 하며 슈퍼클래스가 사용된 곳이 유효하다면 서브클래스가 사용되었을 때도 유효하여야만 한다.

일반적인 상속 방법을 이용한다면 충분히 LSP를 지킬 수 있다.

인터페이스 상속, 구현상속(상속보다 컴포지션을 사용)은 LSP를 만족하도록 설계해야 한다. 만약 지켜지지 않는 다면 instancof를 사용하게 된다.

예시로 List<Object>와 List<String>을 예시로 들 수 있다. Java는 기본적으로 불공변(Invariant)이다. String이 Object 하위타입이어서 List<String>이 List<Object>의 하위타입이 아니라는 점이다. 

만약 공변이었다면 (하위 타입이었다면) 다음 코드는 LSP를 위배하게 되는 예시이다.

import java.util.ArrayList;
import java.util.List;

public class LSPExample {
    public static void main(String[] args) {
        List<Object> objectList = new ArrayList<>();
        objectList.add("String");
        objectList.add(42);


        // LSP 위배: 만약 List<String>이 List<Object>로 간주된다면 다음 코드가 가능해야 함
        List<String> stringList = new ArrayList<>(); //불공변이기에 컴파일 오류, LSP위배
        addObject(stringList);
    }

    public static void addObject(List<Object> list) {
        list.add(42); // List<String>에 Integer를 추가하려고 하면 에러
    }
}




4. ISP (Interface Segregation Principal) 인터페이스 분리 원리

클라이언트는 자신이 사용하지 않는 메서드에 의존하여서는 안 된다.

자신이 사용하지 않는 인터페이스는 구현하지 말아야 한다.

다른 여러 클라이언트(모듈, 클래스)등을 위한 하나의 통합적인 인터페이스로 만들어 비대하게 만들면 불필요한 결합이 생기고 클라이언트의 변경이 생기면 다른 클라이언트도 다시 컴파일이 필요해지게 된다.

즉 통합 인터페이스보다 여러 개로 분리된 인터페이스로 설계하여야 한다.

위와 같은 경우 다른 Student들이 사용하지 않는 클래스들을 포함하고 있다. AcademicStudent는 sportClass, musicClass를 이용하지 않음에도 구현되어야 한다.

다음과 같은 분리된 인터페이스로 구현하여야 한다.


5. DIP (Dependency Inversion Principal) 의존관계 역전 원칙

추상적인 것이 구체적인 것에 의존하면 안 된다. 구체적인 것이 추상적인 것에 의존해야 한다.

추상적인 것이 구체적인 것이 아닌 반대로 구체적인 것이 추상적인 것에 의존한다 해서 의존 역전 법칙이다.

구현된 구체 클래스에 의존하지 말고 인터페이스 등을 이용한 추상화에 의존하라는 것이다.

보통 높은 수준의 클래스(인터페이스, 추상클래스) 등은 낮은 수준의 구현보다 변경 가능성이 낮기 때문이다.

728x90
반응형

댓글