简体   繁体   English

如何使用多态将对象映射到助手类?

[英]How to map objects to helper classes using polymorphism?

I want to replace a switch statement with polymorphism. 我想用多态替换switch语句。 Let's take the example of a PostOffice . 让我们以PostOffice This post office sends Letter 's and Package 's, which are both subclasses of Mail . 该邮局发送LetterPackage ,这两个都是Mail子类。 There are specific ways to send different types of Mail , so there is a LetterService and PackageService , both of which are MailService 's 有几种发送不同类型Mail特定方法,因此有一个LetterServicePackageService ,它们都是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 );
        }
    }

}

How can I avoid the conditional and instanceof? 如何避免有条件和instanceof? I've been told that you can remove these using polymorphism, but I still don't understand how to "route" the correct type of Mail to the correct MailSender . 有人告诉我可以使用多态性删除它们,但是我仍然不知道如何将正确类型的Mail “路由”到正确的MailSender

According to the actual logic, LetterSender and PackageSender does probably have two distinct methods with each one a distinct parameter. 根据实际逻辑, LetterSenderPackageSender可能确实有两个不同的方法,每个方法都有一个不同的参数。 For the first one : 对于第一个:

 public void send(Letter letter);

And for the second one : 对于第二个:

 public void send(Package letter);

To benefit from the polymorphism you should define a common method defined in an interface that these two classes implement. 要从多态中受益,您应该定义在这两个类实现的接口中定义的通用方法。 For example : 例如 :

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

But in Java, parameters are not covariant for overriding . 但是在Java中,参数不是用于overriding协变。 So you could not implement the interface by subtyping the Mail parameter. 因此,您无法通过对Mail参数进行子类型化来实现该接口。
So it means that you have to implement void send(Mail mail) in the two Sender classes such as : 因此,这意味着您必须在两个Sender类中实现void send(Mail mail) ,例如:

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

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

To achieve it you should define Mail from a high level point of view where you define behaviors/methods required for any Mail subclasses. 为此,您应该从较高的角度定义Mail ,在其中定义任何Mail子类所需的行为/方法。
Each Mail subclass would define the implementation for them. 每个Mail子类将为其定义实现。
And so the two sender implementations could process send(Mail mail) without needing to downcast the parameter. 这样,这两个发件人实现就可以处理send(Mail mail)而无需向下转换参数。

This sort of problem can be solved in a few different ways. 这类问题可以通过几种不同的方式解决。 The simplest version is a Chain of Responsibility: 最简单的版本是责任链:

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()
    );

You could use the Visitor pattern for this. 您可以为此使用访客模式。

Define an interface MailVisitor as: 将接口MailVisitor定义为:

public interface MailVisitor {

    void visitLetter(Letter letter);
    void visitPackage(Package package);
}

Implement this interface in MailSender: 在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}
}

Now in the PostOffice class you let the MailSender take a visit to the mail package that just arrived: 现在,在PostOffice类中,让MailSender访问刚刚到达的邮件包:

public class PostOffice {

    @Inject
    private MailSender mailSender;

    public void send(Mail mail) {
        mail.visit(mailSender);
    }
}

The visit method is implemented as following: 访问方法的实现如下:

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);
    }
}

It took me a while to fully grasp how this worked the first time I encountered it. 我花了一段时间才完全了解我第一次遇到它是如何工作的。 But it's a very powerful design pattern, which allows you to eliminate every instanceof + cast operation. 但这是一个非常强大的设计模式,可让您消除+ + cast操作的每个实例。

The biggest advantage of this is that when you define a new Mail subclass, let's say AirMail. 这样的最大优点是,当您定义一个新的Mail子类时,假设是AirMail。 The compiler will force you to implement the visit(MailVisitor visitor) method. 编译器将强制您实现visit(MailVisitor visitor)方法。 Which will automatically make you define a new method on the MailVisitor. 这将自动使您在MailVisitor上定义新方法。 And this will in turn obligate you to implement that new method in the MailSender class. 这又将使您有义务在MailSender类中实现该新方法。 So your code will not compile until you have defined logic that is able to handle your newly created subtype. 因此,在定义了能够处理新创建的子类型的逻辑之前,您的代码将无法编译。 Whereas if you had used an if statement, you could have simply forgotten to add a new branch for AirMail, which would leave your application unable to send any Mail in need of airplane transportation :) 而如果您使用了if语句,则可能只是忘记为AirMail添加一个新分支,这将使您的应用程序无法发送任何需要飞机运输的邮件:)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM