상세 컨텐츠

본문 제목

[자바 CS지식] 좋은 객체 지향 설계 SOLID 원칙

JAVA

by 소란한소란 2024. 2. 11. 22:13

본문

728x90

제목: Java에서의 SOLID 원칙: 좋은 객체 지향 설계를 위한 다섯 가지 원칙


소개: 자바에서 객체 지향 프로그래밍을 할 때, SOLID 원칙을 따르는 것이 중요합니다. SOLID는 SRP, OCP, LSP, ISP, DIP 다섯 가지 원칙으로 이루어져 있으며, 이를 지키면 코드의 유지보수성과 확장성을 높일 수 있습니다. 이 블로그에서는 각 원칙의 특징과 중요성에 대해 알아보겠습니다.


 

1. 단일 책임 원칙 (Single Responsibility Principle - SRP):

  • 특징: 클래스는 하나의 책임만 가져야 합니다. 이는 클래스가 변경되어야 하는 이유는 오직 하나뿐이어야 한다는 원칙입니다.
  • 예시: 각 클래스는 하나의 역할에만 집중하여 코드의 복잡성을 낮춥니다.
// 단일 책임 원칙을 지키지 않은 예제
public class Employee {
    public void generateReport() {
        // 보고서 생성 로직
    }

    public void saveToDatabase() {
        // 데이터베이스에 저장하는 로직
    }
}

 

// 단일 책임 원칙을 지키는 예제
public class Employee {
    public void generateReport() {
        // 보고서 생성 로직
    }
}

public class DatabaseManager {
    public void saveToDatabase() {
        // 데이터베이스에 저장하는 로직
    }
}

 

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

  • 특징: 확장에는 열려 있어야 하고, 수정에는 닫혀 있어야 합니다. 새로운 기능을 추가할 때는 기존 코드를 수정하지 말고 새로운 코드를 추가하여 기능을 확장해야 합니다.
  • 예시: 인터페이스를 통한 확장을 장려하고, 새로운 기능이 필요한 경우 인터페이스를 구현하여 추가합니다.
// 개방/폐쇄 원칙을 지키지 않은 예제
public class Rectangle {
    public double width;
    public double height;
}

public class AreaCalculator {
    public double calculateRectangleArea(Rectangle rectangle) {
        return rectangle.width * rectangle.height;
    }
}

 

// 개방/폐쇄 원칙을 지키는 예제
public interface Shape {
    double calculateArea();
}

public class Rectangle implements Shape {
    private double width;
    private double height;

    @Override
    public double calculateArea() {
        return width * height;
    }
}

public class Circle implements Shape {
    private double radius;

    @Override
    public double calculateArea() {
        return Math.PI * radius * radius;
    }
}

 


 

3. 리스코프 치환 원칙 (Liskov Substitution Principle - LSP):

  • 특징: 하위 클래스는 상위 클래스로 대체될 수 있어야 합니다. 상위 클래스의 인스턴스는 하위 클래스의 인스턴스로 대체 가능해야 합니다.
  • 예시: 상위 클래스에서 정의된 메서드는 하위 클래스에서 오버라이딩되어도 동일한 동작을 보장해야 합니다.
// 리스코프 치환 원칙을 지키지 않은 예제
public class Bird {
    public void fly() {
        // 날기 로직
    }
}

public class Penguin extends Bird {
    // 펭귄은 날지 못하는데 부모 클래스의 메서드를 그대로 상속받음
}

 

// 리스코프 치환 원칙을 지키는 예제
public interface Flyable {
    void fly();
}

public class Bird implements Flyable {
    @Override
    public void fly() {
        // 날기 로직
    }
}

public class Penguin implements Flyable {
    @Override
    public void fly() {
        // 펭귄은 날지 못하지만 Flyable 인터페이스에 맞춰 구현
    }
}

 


 

4. 인터페이스 분리 원칙 (Interface Segregation Principle - ISP):

  • 특징: 클라이언트는 자신이 사용하지 않는 메서드에 의존하지 않아야 합니다. 작은 인터페이스 여러 개가 낫다는 원칙으로, 큰 인터페이스보다는 작은 단위로 나누어 사용합니다.
  • 예시: 클라이언트가 필요한 메서드만 있는 인터페이스를 사용하여 의존성을 최소화합니다.
// 인터페이스 분리 원칙을 지키지 않은 예제
public interface Worker {
    void work();
    void eat();
}

public class Programmer implements Worker {
    @Override
    public void work() {
        // 프로그래머의 작업 로직
    }

    @Override
    public void eat() {
        // 프로그래머의 식사 로직
    }
}

 

// 인터페이스 분리 원칙을 지키는 예제
public interface Workable {
    void work();
}

public interface Eatable {
    void eat();
}

public class Programmer implements Workable, Eatable {
    @Override
    public void work() {
        // 프로그래머의 작업 로직
    }

    @Override
    public void eat() {
        // 프로그래머의 식사 로직
    }
}

 


 

5. 의존 역전 원칙 (Dependency Inversion Principle - DIP):

  • 특징: 고수준 모듈은 저수준 모듈에 의존해서는 안 되며, 양쪽 모두 추상화에 의존해야 합니다. 추상화는 세부 구현에 의존해서는 안 됩니다.
  • 예시: 의존성 주입(Dependency Injection)과 같은 개념을 통해 모듈 간의 결합을 낮추고 유연한 구조를 만듭니다.
// 의존 역전 원칙을 지키지 않은 예제
public class LightBulb {
    public void turnOn() {
        // 전구를 켜는 로직
    }

    public void turnOff() {
        // 전구를 끄는 로직
    }
}

public class Switch {
    private LightBulb bulb;

    public Switch(LightBulb bulb) {
        this.bulb = bulb;
    }

    public void operate() {
        bulb.turnOn();
        // 스위치 조작에 따른 추가 로직
        bulb.turnOff();
    }
}

 

// 의존 역전 원칙을 지키는 예제
public interface Switchable {
    void turnOn();
    void turnOff();
}

public class LightBulb implements Switchable {
    @Override
    public void turnOn() {
        // 전구를 켜는 로직
    }

    @Override
    public void turnOff() {
        // 전구를 끄는 로직
    }
}

public class Switch {
    private Switchable device;

    public Switch(Switchable device) {
        this.device = device;
    }

    public void operate() {
        device.turnOn();
        // 스위치 조작에 따른 추가 로직
        device.turnOff();
    }
}

 


 

결론: SOLID 원칙은 자바뿐만 아니라 객체 지향 프로그래밍에서 좋은 설계를 위한 기본 원칙입니다. 이를 지키면 코드의 유지보수성과 확장성이 향상되어 품질 높은 소프트웨어를 개발할 수 있습니다. SOLID 원칙을 적용하면 객체 지향 프로그래밍의 강력한 기능을 최대한 활용할 수 있습니다.

 

728x90

관련글 더보기