객체지향 설계 원칙이란?
객체지향 프로그래밍(Object-Oriented Programming, OOP)은 소프트웨어를 보다 효율적으로 설계하고 유지보수할 수 있도록 돕는 패러다임이다. 하지만 개념을 단순 적용한다고 좋은 코드가 되는 것은 아니다. 이를 위해 몇 가지 중요한 설계 원칙들이 존재하며, 이를 따르면 확장 가능하고 유지보수가 쉬운 소프트웨어를 만들 수 있다.
SOLID 원칙
1) 단일 책임 원칙 (Single Responsibility Principle, SRP)
잘못된 코드 예제:
class ReportManager {
public void generateReport() {
// 보고서 생성 로직
}
public void printReport() {
// 보고서 출력 로직
}
public void saveToDatabase() {
// 데이터베이스 저장 로직
}
}
개선된 코드:
class ReportGenerator {
public void generateReport() {
// 보고서 생성 로직
}
}
class ReportPrinter {
public void printReport() {
// 보고서 출력 로직
}
}
class ReportSaver {
public void saveToDatabase() {
// 데이터베이스 저장 로직
}
}
실무 적용:
- 서비스 클래스에서 비즈니스 로직과 데이터베이스 처리 로직을 분리하여 유지보수성을 높인다.
- 하나의 변경이 다른 기능에 영향을 주지 않도록 한다.
2) 개방-폐쇄 원칙 (Open-Closed Principle, OCP)
잘못된 코드 예제:
class PaymentProcessor {
public void processPayment(String type) {
if (type.equals("credit")) {
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 paymentMethod) {
paymentMethod.processPayment();
}
}
실무 적용:
- 새로운 결제 방식 추가 시 기존 코드를 수정하지 않고 새로운 클래스를 추가할 수 있다.
- 유지보수가 쉬워지고 버그 발생 가능성이 줄어든다.
3) 리스코프 치환 원칙 (Liskov Substitution Principle, LSP)
잘못된 코드 예제:
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) {
super.setWidth(width);
super.setHeight(width);
}
}
개선된 코드:
interface Shape {
int getArea();
}
class Rectangle implements Shape {
protected int width, height;
public Rectangle(int width, int height) {
this.width = width;
this.height = height;
}
public int getArea() { return width * height; }
}
class Square implements Shape {
private int side;
public Square(int side) {
this.side = side;
}
public int getArea() { return side * side; }
}
실무 적용:
- 상속을 사용할 때, 하위 클래스가 부모 클래스의 기대 동작을 변경하지 않도록 주의해야 한다.
- LSP를 위반하면 예상치 못한 버그가 발생할 가능성이 높아진다.
4) 인터페이스 분리 원칙 (Interface Segregation Principle, ISP)
실무 적용:
- 하나의 인터페이스가 너무 많은 기능을 포함하면, 클라이언트가 필요하지 않은 기능까지 구현해야 하는 문제가 발생한다.
5) 의존 역전 원칙 (Dependency Inversion Principle, DIP)
실무 적용:
- OCP와 DIP는 함께 사용될 때 효과적이다. DIP를 적용하면 인터페이스를 통해 확장이 가능해지고, 기존 코드 수정 없이 새로운 기능을 추가할 수 있다.
interface NotificationService {
void sendNotification(String message);
}
class EmailNotification implements NotificationService {
public void sendNotification(String message) {
System.out.println("Email: " + message);
}
}
class NotificationManager {
private NotificationService notificationService;
public NotificationManager(NotificationService notificationService) {
this.notificationService = notificationService;
}
public void notifyUser(String message) {
notificationService.sendNotification(message);
}
}
이렇게 하면 NotificationManager는 NotificationService 인터페이스에 의존하므로, 새로운 알림 방식(SMS, Push 등)이 추가되더라도 기존 코드를 수정할 필요가 없다.마무리객체지향 설계를 할 때 이 원칙들을 잘 적용하면 더 나은 소프트웨어를 만들 수 있다!
마무리
SOLID 원칙을 따르면 유지보수성이 뛰어나고 확장성이 높은 소프트웨어를 만들 수 있다. 원칙 간의 관계를 고려하여 적절히 적용하는 것이 중요하다. OCP와 DIP를 함께 사용하면 새로운 기능 추가가 용이해지고, LSP를 지키면 상속 구조에서 예기치 않은 오류를 방지할 수 있다.
객체지향 설계를 할 때 이 원칙들을 잘 적용하면 더 나은 소프트웨어를 만들 수 있다
'CS' 카테고리의 다른 글
접근 제한자 (Access Modifier) (0) | 2025.03.04 |
---|---|
객체지향 프로그래밍의 특징 (0) | 2025.03.04 |
직렬화와 역직렬화 (2) | 2025.03.04 |
오버로딩(Overloading)과 오버라이딩(Overriding) (0) | 2025.02.25 |
싱글톤 패턴(Singleton Pattern) (1) | 2025.02.25 |