[Refactoring] 냄새 21. 서로 다른 인터페이스의 대안 클래스들
13 Jan 2023해당 포스트는 inflearn의 백기선님의 강의인 리팩토링 을 듣고 정리한 글입니다.
냄새 21. 서로 다른 인터페이스의 대안 클래스들
- 비슷한 일을 여러 곳에서 서로 다른 규약을 사용해 지원하고 있는 코드 냄새.
- 대안 클래스로 사용하려면 동일한 인터페이스를 구현하고 있어야 한다.
- “함수 선언 변경하기 (Change Function Declaration)”와 “함수 옮기기 (Move Function)”을 사용해서 서로 동일한 인터페이스를 구현하게끔 코드를 수정할 수 있다.
- 두 클래스에서 일부 코드가 중복되는 경우에는 “슈퍼클래스 추출하기 (Extract Superclass)”를 사용해 중복된 코드를 슈퍼클래스로 옮기고 두 클래스를 새로운 슈퍼클래스의 서브클래스로 만들 수 있다.
예제 코드
충분한 협의가 되지않아, 비슷한 기능이 인터페이스로 구현되었을 경우 합치는 방법이다.
예제코드를 살펴보면, 둘의 기능이 각각 이메일 발송과, 알림 발송이지만 비슷한 기능으로 돌아가는 것을 볼 수있다.
두 기능을 슈퍼클래스로 묶어 정의해보고자 한다.
- EmailService, OrderProcessor
public interface EmailService {
void sendEmail(EmailMessage emailMessage);
}
public class OrderProcessor {
private EmailService emailService;
public void notifyShipping(Shipping shipping) {
EmailMessage emailMessage = new EmailMessage();
emailMessage.setTitle(shipping.getOrder() + " is shipped");
emailMessage.setTo(shipping.getEmail());
emailMessage.setFrom("no-reply@whiteship.com");
emailService.sendEmail(emailMessage);
}
}
- AlertService, OrderAlerts
public interface AlertService {
void add(AlertMessage alertMessage);
}
public class OrderAlerts {
private AlertService alertService;
public void alertShipped(Order order) {
AlertMessage alertMessage = new AlertMessage();
alertMessage.setMessage(order.toString() + " is shipped");
alertMessage.setFor(order.getEmail());
alertService.add(alertMessage);
}
}
두 로직의 공통점을 보면 제목, 발신자, 수신자의 정보를 추출 하여 공통적으로 사용할 수 있다.
먼저 세 정보를 담는 객체를 만들어준다.
public class Notification {
private String title;
private String receiver;
private String sender;
...
}
이후 해당 정보를 발송할 슈퍼클래스를 만들어준다.
public interface NotificationService {
void sendNotification(Notification notification);
}
먼저 이메일의 기능을 변환해보자.
sendNotification
에서 부여하던 제목 수신자 발신자의 정보를 Notification
클래스에 담아준다.
public class OrderProcessor {
private EmailService emailService;
public void notifyShipping(Shipping shipping) {
Notification notification = Notification.newNotification(shipping.getOrder() + " is shipped")
.receiver(shipping.getEmail())
.sender("no-reply@whiteship.com");
sendNotification(shipping);
}
...
}
이후 방금 생성한 NotificationService
를 상속하는 구현체를 만들어 sendNotification
의 메소드를 옮겨주도록 하자.
public class EmailNotificationService implements NotificationService{
private EmailService emailService;
@Override
public void sendNotification(Notification notification) {
EmailMessage emailMessage = new EmailMessage();
emailMessage.setTitle(notification.getTitle());
emailMessage.setTo(notification.getReceiver());
emailMessage.setFrom(notification.getSender());
emailService.sendEmail(emailMessage);
}
}
그러면 기존의 이메일 발송 로직은 필요없는 부분을 지워 간단하게 만들어 줄 수 있다.
public class OrderProcessor {
private NotificationService notificationService;
public void notifyShipping(Shipping shipping) {
Notification notification = Notification.newNotification(shipping.getOrder() + " is shipped")
.receiver(shipping.getEmail())
.sender("no-reply@whiteship.com");
notificationService.sendNotification(notification);
}
}
알림 관련 부분도 동일하게 적용 해 줄 수있다.
public class OrderAlerts {
private NotificationService notificationService;
public void alertShipped(Order order) {
Notification notification = Notification.newNotification(order.toString() + " is shipped")
.receiver(order.getEmail());
notificationService.sendNotification(notification);
}
}
public class AlertNotificationService implements NotificationService{
private AlertService alertService;
@Override
public void sendNotification(Notification notification) {
AlertMessage alertMessage = new AlertMessage();
alertMessage.setMessage(notification.getTitle());
alertMessage.setFor(notification.getReceiver());
alertService.add(alertMessage);
}
}