Как отобразить объекты на вспомогательные классы, используя полиморфизм? - PullRequest
0 голосов
/ 11 сентября 2018

Я хочу заменить оператор switch полиморфизмом.Давайте возьмем пример PostOffice.Это почтовое отделение отправляет Letter и Package, которые являются подклассами Mail.Существуют конкретные способы отправки различных типов Mail, поэтому существуют LetterService и PackageService, оба из которых MailService *

public class PostOffice {

    @Inject
    private LetterSender letterSender;

    @Inject
    private PackageSender packageSender;

    public void send( Mail mail ) {
        if ( mail instanceof Letter ) {
            letterSender.send( (Letter) mail );
        } else if ( mail instanceof Package ) {
            packageSender.send( (Package) mail );
        }
    }

}

Как можно избежать условных иэкземпляр?Мне сказали, что вы можете удалить их, используя полиморфизм, но я все еще не понимаю, как «направить» правильный тип Mail к правильному MailSender.

Ответы [ 3 ]

0 голосов
/ 11 сентября 2018

Такую проблему можно решить несколькими различными способами. Самая простая версия - это цепь ответственности:

interface Sender {
    boolean canSend(Mail mail);
    void send(Mail mail);
}
...
List<Sender> senders;
...
senders.stream()
    .filter(s -> s.canSend(mail))
    .findAny()
    .ifPresentOrElseThrow(
        s -> s.send(mail),
        () -> new SomethingException()
    );
0 голосов
/ 11 сентября 2018

Вы можете использовать шаблон 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, в результате чего ваше приложение не могло бы отправлять любую почту, нуждающуюся в перевозке на самолете:)

0 голосов
/ 11 сентября 2018

Согласно действительной логике, LetterSender и PackageSender, вероятно, имеют два разных метода, каждый из которых имеет отдельный параметр. Для первого:

 public void send(Letter letter);

А для второго:

 public void send(Package letter);

Чтобы извлечь выгоду из полиморфизма, вы должны определить общий метод, определенный в интерфейсе, который реализуют эти два класса. Например:

public interface MailSender{
   void send(Mail mail);
}

Но в Java параметры не ковариантны для overriding. Поэтому вы не можете реализовать интерфейс, введя подтип Mail.
Таким образом, это означает, что вы должны реализовать void send(Mail mail) в двух классах Sender, таких как:

public class LetterSender implements MailSender{
   @Override
   public void send(Mail mail){
      // ...
   }
}  

public class PackageSender implements MailSender{
   @Override
   public void send(Mail mail){
      // ...
   }
}  

Чтобы достичь этого, вы должны определить Mail с точки зрения высокого уровня, где вы определяете поведение / методы, необходимые для любых Mail подклассов.
Каждый подкласс Mail будет определять реализацию для них.
И поэтому две реализации отправителя могут обрабатывать send(Mail mail) без необходимости понижать параметр.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...