Вы можете использовать шаблон Visitor для этого.
Определить интерфейс MailVisitor как:
public interface MailVisitor {
void visitLetter(Letter letter);
void visitPackage(Package package);
}
Реализовать этот интерфейс в MailSender:
public class MailSender implement MailVisitor {
@Override
public void visitLetter(Letter letter) {//letter sending goes here}
@Override
public void visitPackage(Package package) {//package sending goes here}
}
Теперь в классе PostOffice вы позволяете MailSender посетить только что прибывший почтовый пакет:
public class PostOffice {
@Inject
private MailSender mailSender;
public void send(Mail mail) {
mail.visit(mailSender);
}
}
Метод посещения реализован следующим образом:
public abstract class Mail {
public abstract void visit(MailVisitor visitor);
}
public class Letter extends Mail {
public void visit(MailVisitor visitor) {
visitor.visitLetter(this);
}
}
public class Package extends Mail {
public void visit(MailVisitor visitor) {
visitor.visitPackage(this);
}
}
Мне потребовалось некоторое время, чтобы полностью понять, как это работало, когда я впервые столкнулся с этим. Но это очень мощный шаблон проектирования, который позволяет исключить каждую операцию instanceof + cast.
Самым большим преимуществом этого является то, что когда вы определяете новый подкласс Mail, скажем, AirMail. Компилятор заставит вас реализовать метод посещения (посетитель MailVisitor). Который автоматически заставит вас определить новый метод в MailVisitor. А это, в свою очередь, обязывает вас реализовать этот новый метод в классе MailSender. Таким образом, ваш код не будет компилироваться до тех пор, пока вы не определите логику, способную обрабатывать ваш вновь созданный подтип. Принимая во внимание, что если бы вы использовали оператор if, вы могли бы просто забыть добавить новую ветку для AirMail, в результате чего ваше приложение не могло бы отправлять любую почту, нуждающуюся в перевозке на самолете:)