SOLID 원칙은 객체 지향 프로그래밍(OOP)에서 유지보수성과 확장성을 높이기 위한 다섯 가지 설계 원칙을 의미합니다. 각각의 원칙을 자세히 설명하겠습니다.
1. SRP (Single Responsibility Principle) - 단일 책임 원칙
"클래스는 단 하나의 책임만 가져야 한다."
- 하나의 클래스는 오직 하나의 기능(책임)만 수행해야 하며, 변경해야 하는 이유도 하나만 존재해야 한다.
- 즉, 한 클래스가 여러 가지 기능을 가지면 수정할 때 예상치 못한 부작용이 발생할 가능성이 커진다.
예제
// ❌ 단일 책임 원칙 위반: 한 클래스가 여러 가지 역할을 수행함.
class Report {
public void generateReport() {
System.out.println("Report Generated");
}
public void printReport() {
System.out.println("Printing Report");
}
}
// ✅ 단일 책임 원칙 준수: 클래스를 분리하여 각각의 역할을 수행하도록 개선.
class ReportGenerator {
public void generateReport() {
System.out.println("Report Generated");
}
}
class ReportPrinter {
public void printReport() {
System.out.println("Printing Report");
}
}
ReportGenerator는 보고서를 생성하는 역할, ReportPrinter는 보고서를 출력하는 역할을 수행하도록 분리.
2. OCP (Open/Closed Principle) - 개방-폐쇄 원칙
"확장에는 열려 있고, 수정에는 닫혀 있어야 한다."
- 새로운 기능이 추가될 때 기존 코드를 수정하지 않고 확장 가능하도록 설계해야 한다.
- 주로 인터페이스, 추상 클래스, 다형성(Polymorphism)을 이용하여 설계를 유연하게 만든다.
예제
// ❌ 개방-폐쇄 원칙 위반: 새로운 결제 방식이 추가될 때마다 기존 코드를 수정해야 함.
class Payment {
public void processPayment(String type) {
if (type.equals("CreditCard")) {
System.out.println("Processing Credit Card Payment");
} else if (type.equals("PayPal")) {
System.out.println("Processing PayPal Payment");
}
}
}
// ✅ 개방-폐쇄 원칙 준수: 새로운 결제 방식을 추가할 때 기존 코드를 수정하지 않아도 됨.
interface PaymentMethod {
void processPayment();
}
class CreditCardPayment implements PaymentMethod {
public void processPayment() {
System.out.println("Processing Credit Card Payment");
}
}
class PayPalPayment implements PaymentMethod {
public void processPayment() {
System.out.println("Processing PayPal Payment");
}
}
class PaymentProcessor {
public void process(PaymentMethod method) {
method.processPayment();
}
}
새로운 결제 방식이 필요하면 PaymentMethod를 구현하는 클래스를 추가하기만 하면 된다.
3. LSP (Liskov Substitution Principle) - 리스코프 치환 원칙
"하위 클래스는 상위 클래스의 기능을 대체할 수 있어야 한다."
- 즉, 상위 클래스 객체를 하위 클래스 객체로 교체해도 프로그램이 정상적으로 동작해야 한다.
- 상속받은 하위 클래스는 상위 클래스의 계약(기능과 동작)을 반드시 유지해야 한다.
예제
// ❌ 리스코프 치환 원칙 위반: Rectangle(직사각형)을 상속한 Square(정사각형)가 예상치 못한 동작을 함.
class Rectangle {
protected int width, height;
public void setWidth(int width) {
this.width = width;
}
public void setHeight(int height) {
this.height = height;
}
public int getArea() {
return width * height;
}
}
class Square extends Rectangle {
@Override
public void setWidth(int width) {
this.width = width;
this.height = width; // 정사각형이므로 높이도 동일하게 설정
}
@Override
public void setHeight(int height) {
this.width = height;
this.height = height;
}
}
Square(정사각형)는 Rectangle을 상속했지만, 높이와 너비가 같아야 하는 특성 때문에 Rectangle(직사각형)의 기능을 제대로 대체하지 못함.
개선 방법:
abstract class Shape {
abstract int getArea();
}
class Rectangle extends Shape {
protected int width, height;
public Rectangle(int width, int height) {
this.width = width;
this.height = height;
}
@Override
public int getArea() {
return width * height;
}
}
class Square extends Shape {
private int side;
public Square(int side) {
this.side = side;
}
@Override
public int getArea() {
return side * side;
}
}
Rectangle(직사각형)과 Square(정사각형)를 Shape(모양) 인터페이스로 분리하여 상속의 문제를 해결.
4. ISP (Interface Segregation Principle) - 인터페이스 분리 원칙
"클라이언트가 자신이 사용하지 않는 인터페이스에 의존하지 않아야 한다."
- 하나의 거대한 인터페이스를 여러 개의 작은 인터페이스로 나누어야 한다.
- 필요 없는 기능을 구현하지 않도록 강제하는 구조를 방지한다.
예제
// ❌ 인터페이스 분리 원칙 위반: 한 인터페이스가 너무 많은 기능을 포함하고 있음.
interface Worker {
void work();
void eat();
}
class Developer implements Worker {
public void work() {
System.out.println("Coding...");
}
public void eat() {
System.out.println("Eating...");
}
}
Developer는 work()만 필요하지만 eat()까지 구현해야 함.
개선 방법:
interface Workable {
void work();
}
interface Eatable {
void eat();
}
class Developer implements Workable {
public void work() {
System.out.println("Coding...");
}
}
class OfficeWorker implements Workable, Eatable {
public void work() {
System.out.println("Doing office work...");
}
public void eat() {
System.out.println("Eating lunch...");
}
}
Workable, Eatable 인터페이스를 분리하여 필요한 기능만 구현하도록 개선.
5. DIP (Dependency Inversion Principle) - 의존 역전 원칙
"고수준 모듈이 저수준 모듈에 의존하지 않고, 추상화에 의존해야 한다."
- 구체적인 구현이 아니라 인터페이스나 추상 클래스에 의존하도록 설계해야 한다.
- 이를 통해 결합도를 낮추고 유지보수를 쉽게 만들 수 있다.
예제
// X 의존 역전 원칙 위반: Switch(고수준 모듈)가 LightBulb(저수준 모듈)에 직접 의존
class LightBulb { //저수준 모듈
public void turnOn() {
System.out.println("Light Bulb is On");
}
public void turnOff() {
System.out.println("Light Bulb is Off");
}
}
class Switch { //고수준 모듈
private LightBulb bulb;
public Switch(LightBulb bulb) { //Switch가 LightBulb 클래스를 직접 의존
this.bulb = bulb;
}
public void operate() {
bulb.turnOn();
}
}
Switch 클래스는 LightBulb에 직접 의존하고 있어 확장성이 떨어짐.
개선 방법:
// 추상화된 인터페이스 (고수준 모듈과 저수준 모듈 간의 연결 역할)
interface Switchable {
void turnOn();
void turnOff();
}
// 저수준 모듈 1: 전구 (LightBulb)
class LightBulb implements Switchable {
public void turnOn() {
System.out.println("Light Bulb is On");
}
public void turnOff() {
System.out.println("Light Bulb is Off");
}
}
// 저수준 모듈 2: 선풍기 (Fan)
class Fan implements Switchable {
public void turnOn() {
System.out.println("Fan is Spinning");
}
public void turnOff() {
System.out.println("Fan Stopped");
}
}
// 고수준 모듈: Switch
class Switch {
private final Switchable device; // 인터페이스에 의존 (추상화)
public Switch(Switchable device) { // DIP 준수 - 인터페이스를 통해 의존성 주입
this.device = device;
}
public void turnOn() {
device.turnOn(); // 구현체가 아닌 인터페이스의 메서드를 호출
}
public void turnOff() {
device.turnOff();
}
}
// 실행 예제
public class Main {
public static void main(String[] args) {
Switchable bulb = new LightBulb();
Switchable fan = new Fan();
Switch switch1 = new Switch(bulb);
Switch switch2 = new Switch(fan);
switch1.turnOn(); // "Light Bulb is On"
switch2.turnOn(); // "Fan is Spinning"
}
}
Switchable 인터페이스를 도입하여 Switch가 LightBulb 구현체에 직접 의존하지 않도록 개선.
정리
- SRP: 단일 책임을 유지하라.
- OCP: 기존 코드를 변경하지 않고 확장 가능하게 하라.
- LSP: 하위 클래스는 상위 클래스를 완전히 대체할 수 있어야 한다.
- ISP: 인터페이스를 작은 단위로 분리하라.
- DIP: 구체적인 구현이 아닌 추상화에 의존하라.
다음엔 SRP, OCP, LSP, ISP, DIP를 세부적으로 좀 더 정리해봐야겠다.
'프로그래밍 > JAVA' 카테고리의 다른 글
Java 싱글톤 패턴 완벽 정리! 개념부터 구현까지 총정리 (Singleton Pattern) (0) | 2025.03.01 |
---|---|
Java 디자인 패턴 완벽 정리 | 개념, 종류, 사용 이유 (0) | 2025.03.01 |
[JAVA] final이란? final 사용하는 방법은 무엇일까? (0) | 2022.12.18 |
[JAVA] static 이란? + static 변수, static 메서드 (0) | 2022.12.17 |
[이클립스 단축키] 자주 사용하는 단축키 정리 (0) | 2022.12.15 |
댓글