냄새 21. 서로 다른 인터페이스의 대안 클래스들 (Alternative Classes with Different Interfaces)
비슷한 일을 여러 곳에서 서로 다른 규약을 사용해 지원하고 있는 코드 냄새.
대안 클래스로 사용하려면 동일한 인터페이스를 구현하고 있어야 한다.
“함수 선언 변경하기 (Change Function Declaration)”와 “함수 옮기기 (Move Function)을 사용해서 서로 동일한 인터페이스를 구현하게끔 코드를 수정할 수 있다.
두 클래스에서 일부 코드가 중복되는 경우에는“슈퍼클래스 추출하기 (Extract Superclass)”를 사용해 중복된 코드를 슈퍼클래스로 옮기고 두 클래스를 새로운 슈퍼클 래스의 서브클래스로 만들 수 있다.
예시 코드
Before
OrderProcessor와 OrderAlerts는 하는 일이 유사하다.
API를 제공받아 사용하기 때문에 임의로 고칠 수 없는 경우 (EmailService, AlertService)
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);
}
}
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 interface EmailService {
void sendEmail(EmailMessage emailMessage);
}
public interface AlertService {
void add(AlertMessage alertMessage);
}
After
어떤 notificationService를 넘겨주는가에 따라 행동이 다르게끔 notificationService 인터페이스를 생성한다.
(동일한 인터페이스로 감싸는 방법을 사용해 해결할 수 있다.)
(책에는 인터페이스 코드 자체를 수정하는 예시 코드가 있으나 위 예시 코드는 그렇지 못한 경우를 상정)
public interface NotificationService {
public void senNotification(Notification notification);
}
public class OrderProcessor {
private NotificationService notificationService;
public OrderProcessor(NotificationService notificationService) {
this.notificationService = notificationService;
}
public void notifyShipping(Shipping shipping) {
Notification notification = Notification.newNotification(shipping.getOrder() + " is shipped")
.receiver(shipping.getEmail())
.sender("no-reply@gmail.com");
notificationService.senNotification(notification);
}
}
public class EmailNotificationService implements NotificationService {
private EmailService emailService;
@Override
public void senNotification(Notification notification) {
EmailMessage emailMessage = new EmailMessage();
emailMessage.setTitle(notification.getTitle() + " is shipped");
emailMessage.setTo(notification.getReceiver());
emailMessage.setFrom(notification.getSender());
emailService.sendEmail(emailMessage);
}
}
public class OrderAlerts {
private NotificationService notificationService;
public OrderAlerts(NotificationService notificationService) {
this.notificationService = notificationService;
}
public void alertShipped(Order order) {
Notification notification = Notification.newNotification(order.toString() + " is shipped")
.receiver(order.getEmail());
notificationService.senNotification(notification);
}
}
public class AlertNotificationService implements NotificationService {
private AlertService alertService;
@Override
public void senNotification(Notification notification) {
AlertMessage alertMessage = new AlertMessage();
alertMessage.setMessage(notification.getTitle());
alertMessage.setFor(notification.getReceiver());
alertService.add(alertMessage);
}
}
냄새 21. 서로 다른 인터페이스의 대안 클래스들 (Alternative Classes with Different Interfaces)
예시 코드
Before
After
어떤 notificationService를 넘겨주는가에 따라 행동이 다르게끔 notificationService 인터페이스를 생성한다. (동일한 인터페이스로 감싸는 방법을 사용해 해결할 수 있다.) (책에는 인터페이스 코드 자체를 수정하는 예시 코드가 있으나 위 예시 코드는 그렇지 못한 경우를 상정)