DIP (Dependency Inversion Principle) 는 의존성 역전 준말로 아래와 같은 원칙을 가지고 있습니다.
A. 상위 모듈은 하위 모듈에서 아무것도 가져오지 않아야 한다.
B. 추상화는 세부 사항에 의존해서는 안 됩니다. 세부 사항(구체적 구현)은 추상화에 의존해야 합니다.
DIP 원칙을 처음 봤을 때 구현에 의존하지 말고 추상화 계층에 의존을 하라는 이전의 원칙과 비슷하게 느꼈습니다. 하지만 DIP는 단순히 인터페이스에 의존하는 것을 넘어, 고 수준 모듈과 저 수준 모듈의 관계까지 아우르는 원칙입니다. 즉 전체 시스템의 모듈들이 구체적인 구현에 의존하지 않는다면 유연하게 소프트웨어를 확장을 할 수 있습니다.
DIP 관련 예시를 살펴 보겠습니다.
알림이라는 클래스가 이메일 서비스와 SMS 서비스 라는 구체적인 하위 모듈이 직접적으로 의존을 하고 있습니다. 코드로 구현을 한다면 아래와 같습니다.
class EmailService {
public void sendEmail(String message) {
System.out.println("이메일 전송: " + message);
}
}
class SMSService {
public void sendSMS(String message) {
System.out.println("SMS 전송: " + message);
}
}
class Notification {
private EmailService emailService;
private SMSService smsService;
public Notification() {
this.emailService = new EmailService();
this.smsService = new SMSService();
}
public void sendEmailNotification(String message) {
emailService.sendEmail(message);
}
public void sendSMSNotification(String message) {
smsService.sendSMS(message);
}
}
해당 코드는 아래와 같은 문제점이 있습니다.
- sendEmail 과 sendSMS 메서드를 호출할 때 알림 모듈이 각 서비스의 구체적인 구현에 종속되어 있어, 서비스가 변경되거나 새로운 알림 방식이 추가될 경우 알림 모듈을 수정해야 합니다.
- 이로 인해 확장성이 떨어지고, 유연하게 새로운 기능을 추가하거나 유지 보수하는 데 어려움이 있습니다.
DIP 적용된 예시를 위해 위 이미지처럼 설계를 하였고 아래와 같이 코드를 작성했습니다.
// 알림 서비스 인터페이스
interface NotificationService {
void send(String message);
}
// 이메일 서비스 클래스
class EmailService implements NotificationService {
public void send(String message) {
System.out.println("이메일 전송: " + message);
}
}
// SMS 서비스 클래스
class SMSService implements NotificationService {
public void send(String message) {
System.out.println("SMS 전송: " + message);
}
}
// 알림 클래스
class Notification {
private NotificationService service;
public Notification(NotificationService service) {
this.service = service;
}
public void sendNotification(String message) {
service.send(message);
}
}
위 내용은 DIP(Dependency Inversion Principle)가 적용된 예시로, 상위 모듈인 Notification 인터페이스 또는 추상클래스가 구체적인 EmailService와 SMSService 구현체에 의존하지 않고, NotificationService라는 추상화된 인터페이스를 통해 상호작용하도록 설계되었습니다. 이를 통해 시스템이 유연하게 확장 가능하며, 각 서비스 구현체의 변경이 상위 모듈에 영향을 미치지 않게 됩니다.
DIP 를 공부했을 때는 추상화 계층을 참조하기 떄문에 구현클래스 확장이 가능한건 이해가 되었는데 추상 레이어가 깨질경우에는 답이 없는건가? 라는 생각도 해보았다.
참조 사이트
'개발이야기 > Design Pattern' 카테고리의 다른 글
객체 지향 SOLID 원칙 - ISP (1) | 2024.10.31 |
---|---|
객체 지향 SOLID 원칙 - OCP (4) | 2024.10.29 |
객체 지향 SOLID 원칙 - SRP 편 (3) | 2024.10.28 |
디자인 패턴에 대한 필요성과 이해 (0) | 2024.10.14 |