새발블로그
[JAVA] SOLID 원칙 본문
1. SRP (Single Responsibility Principle) - 단일 책임 원칙
하나의 클래스는 하나의 책임만 가져야 한다.
- 설명: 클래스는 하나의 작업만 수행해야 하며, 이로 인해 클래스가 변경되는 이유도 하나여야 한다.
- 장점: 유지보수성이 높아지고, 각 클래스가 독립적으로 변경될 수 있다.
// SRP 준수 (좋은 예시)
public class UserService {
public void registerUser(String username, String password) { /* 회원가입 로직 */ }
}
public class EmailService {
public void sendWelcomeEmail(String email) { /* 이메일 전송 로직 */ }
}
UserService와 EmailService가 각각 독립적인 책임을 가지고 있어, 하나의 클래스가 여러 책임을 갖지 않도록 분리되었다.
2. OCP (Open/Closed Principle) - 개방-폐쇄 원칙
확장에는 열려 있고, 수정에는 닫혀 있어야 한다.
- 설명: 기존 코드를 수정하지 않고, 기능을 확장할 수 있어야 한다. 새로운 기능은 추가할 수 있지만, 기존의 코드를 변경해서는 안 된다.
- 장점: 시스템의 변경이 최소화되고, 기존 코드의 안정성을 보장할 수 있다.
// OCP 준수 (좋은 예시)
public interface Shape {
void draw();
}
public class Circle implements Shape {
public void draw() {
System.out.println("Drawing Circle");
}
}
public class Square implements Shape {
public void draw() {
System.out.println("Drawing Square");
}
}
public class ShapePrinter {
public void printShape(Shape shape) {
shape.draw();
}
}
도형을 추가할 때 기존 코드를 수정하지 않고, Shape 인터페이스를 확장한 새로운 도형 클래스를 추가하는 방식으로 기능을 확장하였다.
3. LSP (Liskov Substitution Principle) - 리스코프 치환 원칙
부모 클래스를 사용하는 곳에 자식 클래스를 넣어도 문제가 없어야 한다.
- 설명: 자식 클래스는 부모 클래스의 기능을 그대로 사용할 수 있어야 하며, 부모 클래스의 인스턴스가 자식 클래스의 인스턴스로 교체되어도 오류가 발생하지 않아야 한다.
- 장점: 코드의 유연성과 확장성을 보장한다.
public class Bird {
public void fly() {
System.out.println("Flying...");
}
}
public class Sparrow extends Bird {
@Override
public void fly() {
System.out.println("Sparrow flying...");
}
}
날 수 없는 새인 Penguin을 Bird로 구현한 경우, 문제가 된다. Penguin이 Bird를 상속받으면 fly() 메서드를 호출할 때 예외가 발생합니다. 이는 LSP 위반입니다.
4. ISP (Interface Segregation Principle) - 인터페이스 분리 원칙
클라이언트는 자신이 사용하지 않는 인터페이스에 의존하면 안 된다.
- 설명: 인터페이스는 클라이언트가 필요로 하는 기능만 제공해야 하며, 불필요한 메서드를 포함해서는 안 된다. 즉, 하나의 커다란 인터페이스를 여러 개의 작은 인터페이스로 나누어야 한다.
- 장점: 인터페이스가 클라이언트의 요구에 맞게 구체화되므로, 불필요한 의존성을 피할 수 있다.
// ISP 준수 (좋은 예시)
public interface Printer {
void print();
}
public interface Scanner {
void scan();
}
public class SimplePrinter implements Printer {
public void print() {
System.out.println("Printing...");
}
}
여기서는 프린터 기능과 스캔 기능을 각각 별도의 인터페이스로 분리하여 필요한 기능만을 구현하게 한다.
5. DIP (Dependency Inversion Principle) - 의존 역전 원칙
고수준 모듈이 저수준 모듈에 의존하면 안 된다.
- 설명: 고수준 모듈은 저수준 모듈에 의존하는 것이 아니라, 추상화된 인터페이스에 의존해야 한다. 이렇게 하면 코드의 의존성을 낮출 수 있다.
- 장점: 유연성과 확장성이 향상된다.
// DIP 준수 (좋은 예시)
public interface Switchable {
void turnOn();
void turnOff();
}
public class LightBulb implements Switchable {
public void turnOn() { System.out.println("Bulb on"); }
public void turnOff() { System.out.println("Bulb off"); }
}
public class Fan implements Switchable {
public void turnOn() { System.out.println("Fan on"); }
public void turnOff() { System.out.println("Fan off"); }
}
public class Switch {
private Switchable device;
public Switch(Switchable device) {
this.device = device;
}
public void operate() {
device.turnOn();
}
}
Switch 클래스는 구체적인 장치 클래스(LightBulb 또는 Fan)가 아니라 Switchable 인터페이스에 의존함으로써, 장치가 추가되거나 변경되더라도 Switch 클래스의 수정 없이 새로운 장치를 쉽게 추가할 수 있다.
'Language > Java' 카테고리의 다른 글
| [JAVA] 멀티스레드 (1) | 2025.05.04 |
|---|---|
| [JAVA] 상속 (0) | 2025.05.04 |
| [JAVA] 클래스 (0) | 2025.05.04 |
| [JAVA] 변수 위치 및 메모리 구조 (0) | 2025.05.04 |
| [JAVA] JVM (0) | 2025.05.04 |