본문 바로가기
728x90
반응형

solid

SOLID는 객체지향 프로그래밍에서 소프트웨어 설계를 더 견고하고 유지보수가 쉬운 방향으로 이끄는 다섯 가지 기본 원칙의 약어로, 소프트웨어가 변경될 때 코드를 쉽게 수정하고 확장할 수 있도록 돕는데 중점을 두고 있는 개념이다. 

 

 S - 단일 책임 원칙 (Single Responsibility Principle)

: 하나의 클래스는 오직 하나의 책임만을 가져야한다. 이를 통해 클래스가 변경되어야할 때 하나의 클래스만 바꾸면 된다는 것이다.

 

아래 코드와 같이 각 클래스는 하나의 책임만을 수행하게 되어 단일 책임 원칙을 지켜주어야 한다. 

 

class Order {
    private double totalPrice;

	// 주문 총 가격을 계산하는 책임
    public double calculateTotalPrice() {
        return totalPrice;
    }

    public void save() {
        // 주문을 저장하는 책임
        // 주문 저장 로직
    }
}

   // 주문 확인 이메일을 보내는 책임
class EmailSender {
    public void sendConfirmationEmail(Order order) {
     
     }
}

 

O - 개방 폐쇄 원칙 (Open/Closed Principle)

: 소프트웨어 엔티티(클래스, 모듈, 함수 등)는 확장 가능하게 설계되어야 하지만, 기존의 코드 변경 없이 행위를 변경할 수 있어야 한다는 개념이다. 즉 소프트웨어 엔티티(클래스, 모듈, 함수 등)가 확장에는 열려 있어야 하고, 변경에는 닫혀 있어야 한다는 원칙을 의미하는 것이다.

 OCP는 코드의 유연성을 높이고, 유지보수성을 향상시켜 시스템이 쉽게 변화하고 확장될 수 있도록 돕는다.

더보기

확장에는 열려 있어야 함 (Open for Extension)

  • 새로운 기능이나 동작을 추가하거나 새로운 요구사항이 발생했을 때, 기존의 코드 수정 없이 쉽게 확장할 수 있어야 한다.
  • 새로운 기능을 추가하거나 새로운 유형의 엔티티를 도입할 때, 코드를 확장해야 하며, 이는 기존 코드를 변경하지 않고 수행될 수 있어야 한다.

변경에는 닫혀 있어야 함 (Closed for Modification)

  • 이미 존재하는 코드를 수정하거나 변경하지 않아야 한다.
  • 새로운 기능 추가를 위해서는 기존의 코드를 수정하지 않고, 기존의 구조를 활용하여 새로운 기능을 확장할 수 있어야 한다.

 

아래 코드와 같이 삼각형이라는 도형이 추가되어도 Shape 인터페이스를 구현하는 새로운 클래스를 만들어 기존 코드를 수정하지 않고도 도형을 확장장할 수 있으며, 넓이 계산에도 유연하게 대처할 수 있다.

// 도형을 나타내는 인터페이스
interface Shape {
    double calculateArea(); // 넓이를 계산하는 메서드 선언
}

// 원을 구현하는 클래스
class Circle implements Shape {
    private double radius; // 반지름

    // 원의 넓이 계산
    public double calculateArea() {
        return Math.PI * radius * radius; // 원의 넓이 공식
    }
}

// 직사각형을 구현하는 클래스
class Rectangle implements Shape {
    private double width; // 가로 길이
    private double height; // 세로 길이

    // 직사각형의 넓이 계산
    public double calculateArea() {
        return width * height; // 직사각형의 넓이 공식
    }
}

 

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

: 상속 관계에서 자식 클래스는 부모 클래스의 기능을 모두 지원하고 기대되는 동작을 보장해야 한다. 즉 자식 클래스는 부모 클래스로 대체가 가능하다는 것을 의미한다. 

 

아래 코드와 같이 운송수단은 "이동할 수 있는 것"이라는 공통적인 특징을 가지고 있는데, 만약 어떤 함수나 메서드에서 운송수단을 기대하고 있다면, 그 자리에는 자동차, 오토바이, 트럭 등 운송수단의 하위 타입을 사용해도 문제가 없어야 한다는 것이다.

class Vehicle {
    void startEngine() {
        System.out.println("엔진을 가동합니다.");
    }
}

class Car extends Vehicle {
    void startEngine() {
        System.out.println("자동차의 엔진을 가동합니다.");
    }
}

class Truck extends Vehicle {
    void startEngine() {
        System.out.println("트럭의 엔진을 가동합니다.");
    }
}

public class Main {
    static void startVehicleEngine(Vehicle vehicle) {
        vehicle.startEngine();
    }

    public static void main(String[] args) {
        Vehicle car = new Car();
        Vehicle truck = new Truck();

        startVehicleEngine(car); // 자동차의 엔진을 가동합니다.
        startVehicleEngine(truck); // 트럭의 엔진을 가동합니다.
    }
}

 

 

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

: 클라이언트가 자신이 사용하지 않는 메서드에 의존하지 않아야 한다는 원칙으로, 인터페이스를 클라이언트가 필요로 하는 기능에 따라 작게 나누어 분리하여 클라이언트 의존성 최소화하고, 인터페이스는 하나의 특정한 클라이언트에 의존하는 메서드만을 포함해야는 것을 말한다.

 

 아래 코드와 같이 분리된 인터페이스들을 사용하면, 클라이언트는 필요로 하는 기능에만 의존하도록 할 수 있으며, 이는 코드의 유지보수성과 확장성을 향상시켜 줄 수 있다. 3개의 인터페이스를 분리하여 코드간의 의존성도 줄여줄 수 있다. 

interface Worker {
    void work();
    void eat();
    void sleep();
}


// 인터페이스 분리

interface Workable {
    void work();
}

interface Eatable {
    void eat();
}

interface Sleepable {
    void sleep();
}

// 필요한 기능을 각각의 클래스에 맞게 구현

class HumanWorker implements Workable, Eatable, Sleepable {
    // 기능 
}

class RobotWorker implements Workable {
    // 기능
}

 

D - 의존성 역전 원칙 (Dependency Inversion Principle)

: 높은 수준(기능을 제공하는 모듈)의 모듈은 저수준(세부 구현을 가진 모듈)의 모듈에 의존해서는 안 되며, 둘 모두 추상화에 의존해야 한다는 원칙을 말한다. 모듈을 인터페이스나 추상 클래스와 같은 추상화된 요소에 의존함으로써 모듈 간 결합도를 낮추고, 상위 수준 모듈은 추상화된 것에 의존하고 구체적인 구현은 하위 수준 모듈에 의존하는 일반적인 구조를 역전시켜 두 모듈 모두 추상화에 의존하도록 유도하는 것이다. 이를 통해 코드 변경 용이성을 높일 수 있다.

 

 아래 코드와 같이 Publisher 클래스가 ArticleProvider 인터페이스에 의존하고 있어, 실제 기사를 제공하는 구체적인 Article 클래스에 직접적으로 의존하지 않도록 하고, 이는 코드의 유연성과 확장성을 높여줄 수 있다. 만약 새로운 기사 제공 방식이 추가되거나 기존의 방식이 변경되더라도 Publisher 클래스를 수정하지 않고 쉽게 대응할 수 있게 된다.

interface ArticleProvider {
    String getContent();
}

class Article implements ArticleProvider {
    public String getContent() {
        // 기사 내용 반환
    }
}

class Publisher {
    private ArticleProvider articleProvider;

    public Publisher(ArticleProvider articleProvider) {
        this.articleProvider = articleProvider; // 추상화된 ArticleProvider에 의존
    }

    public void publish() {
        String content = articleProvider.getContent();
        // 기사를 발행하는 코드
    }
}

 

 

 

 

 

 

 

정규좋아

 

 

 

728x90
반응형