Programming/Object-Oriented Programming

[OOP] 객체지향 설계원칙 - SOLID 원칙

빅콜팝 2023. 3. 12. 18:56
728x90
반응형

SRP(단일 책임 원칙)
OCP(개방-폐쇄 원칙)
LSP(리스코프 치환 원칙)
ISP(인터페이스 분리 원칙)
DIP(의존 역전 원칙)
 
SOLID는 객체 지향 프로그래밍에서 유용한 디자인 원칙의 묶음을 나타낸다. 이들 원칙은 소프트웨어의 설계, 개발 및 유지 보수를 용이하게 하기 위해 사용된다.


⛵️ 단일 책임 원칙 (SRP: Single Responsibility Principle)


클래스는 하나의 책임만 가져야한다. 즉, 클래스는 하나의 기능을 수행하고, 그 기능을 변경할 이유가 하나여야 한다.
 
예를 들어, 고객 정보를 데이터베이스에 저장하고, 이메일로 고객에게 안내 메일을 보내는 클래스가 있다고 가정해보면, 이 클래스는 두 가지 기능을 수행하므로 SRP 원칙을 위반한다. 이 경우, 이 두 가지 기능을 분리하여 각각의 클래스로 만드는 것이 좋은 설계이다.

올바른 SRP를 따른 코드 예시를 살펴보자.
온라인 쇼핑몰에서 상품을 판매하는 경우, 상품의 정보를 데이터베이스에 저장하고, 상품을 화면에 표시하는 두 가지 작업이 필요하다고 할 때, Product 클래스는 단순히 상품 정보를 저장하는 역할만을 담당하고, DisplayProduct 클래스는 상품 정보를 화면에 표시하는 역할만을 수행한다. 이렇게 두 개의 클래스로 분리함으로써, 각각의 클래스가 하나의 책임만을 지게 되어 코드 유지보수성과 확장성을 높일 수 있다.

@Getter
@Setter
public class Product {
    private String name;
    private double price;
    private int quantity;
}

public class DisplayProduct {
    private Product product;

    public DisplayProduct(Product product) {
        this.product = product;
    }

    public void display() {
        System.out.println("상품 이름: " + product.getName());
        System.out.println("상품 가격: " + product.getPrice());
        System.out.println("상품 수량: " + product.getQuantity());
    }
}

이와 같이 SRP 원칙을 준수하는 코드를 작성하면 코드 유지보수성이 높아지며, 기능 추가나 변경이 쉬워진다. 또한, 이렇게 클래스를 분리하면 한 클래스가 수정되어도 다른 클래스에 영향을 미치지 않아 코드의 안정성을 높일 수 있다.

 



🚧 개방-폐쇄 원칙 (OCP: Open/Closed Principle)


개방/폐쇄 원칙은 클래스, 모듈, 함수 등의 소프트웨어 요소가 확장에 대해 열려있고 수정에 대해서는 닫혀있어야 한다는 것을 의미한다. 즉, 새로운 동작을 추가하려면 기존 코드를 수정하지 않아도 되도록 설계되어야 한다.
 
자바에서 인터페이스를 활용하는 하는 것을 예를 들 수 있다. 어떤 객체의 기능을 확장하고 싶을 때 그 객체가 구현한 인터페이스를 상속받아 새로운 클래스를 만들고, 그 클래스에서 추가적인 기능을 구현할 수 있다. 이렇게 하면 기존의 객체를 수정하지 않아도 새로운 기능을 추가할 수 있으므로 OCP를 지키는 것이다.

 


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


상속 계층 구조에서 하위 클래스는 상위 클래스를 대체할 수 있어야한다. 이는 상위 클래스로 사용되는 객체를 하위 클래스로 대체해도 프로그램의 기능이 영향을 받지 않는 것을 의미한다.
 
LSP는 다형성(polymorphism)의 기반이 되는 원칙 중 하나이다. 상위 클래스는 하위 클래스들의 공통된 특성을 정의하고, 하위 클래스들은 상위 클래스를 확장하거나 구체화하여 구현한다. 이때 하위 클래스는 상위 클래스의 모든 속성과 메서드를 포함해야 하며, 상위 클래스의 메서드를 오버라이드하는 경우 하위 클래스의 메서드는 상위 클래스의 메서드와 같은 동작을 해야 한다.

 

 

✂️ 인터페이스 분리 원칙 (ISP: Interface Segregation Principle)


사용하지 않는 메서드에 의존하지 않도록 인터페이스를 작게 분리해야한다. 한 인터페이스가 많은 기능을 가지고 있다면, 클라이언트는 이 인터페이스의 모든 기능을 구현해야 한다.
 
즉, 파일과 관련된 메서드는 파일 인터페이스에, 디렉토리와 관련된 메서드는 디렉토리 인터페이스에 분리될 수 있다. 그러면 다음과 같이 인터페이스를 구성할 수 있다.

public interface File {
    void create();
    void delete();
}

public interface Directory {
    void create();
    void delete();
}

이렇게 인터페이스를 작게 분리하면, 파일과 디렉토리를 처리하는 클래스는 필요한 인터페이스만 구현하면 된다.



🔎 의존 역전 원칙 (DIP: Dependency Inversion Principle)


고수준 모듈은 저수준 모듈에 의존하면 안된다. 대신, 두 모듈 모두 추상화된 인터페이스에 의존해야한다.
 
예를 들어, 모바일 애플리케이션에서 데이터베이스와 연결하는 클래스가 있다고 가정해 보자. 이 때, DIP 원칙을 위반하지 않기 위해서는 데이터베이스와 연결하는 클래스를 직접 사용하는 것이 아니라, 인터페이스를 사용하여 데이터베이스 연결에 대한 추상화를 제공해야한다. 그러면 이 인터페이스를 구현한 클래스를 사용하여 데이터베이스와 연결하면 된다. 이를 통해 고수준 모듈(애플리케이션)은 저수준 모듈(데이터베이스)과 직접적인 의존성을 갖지 않게 되며, 확장성과 유지보수성이 개선된다.
 

이러한 SOLID 원칙은 소프트웨어의 유연성, 확장성 및 유지 보수성을 향상시키는 데 도움이 된다. 이를 적용하면 코드를 더 쉽게 변경하고 수정할 수 있으며, 소프트웨어 개발자는 더 나은 소프트웨어를 작성할 수 있다.



728x90
반응형