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

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何使用多态将对象映射到辅助类?相关的知识,希望对你有一定的参考价值。

我想用多态替换switch语句。我们以PostOffice为例。这个邮局发送LetterPackage,它们都是Mail的子类。有特定的方式发送不同类型的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 );
        }
    }

}

我怎样才能避免条件和instanceof?我被告知你可以使用多态性删除这些,但我仍然不明白如何将正确类型的Mail“路由”到正确的MailSender

答案

根据实际逻辑,LetterSenderPackageSender可能有两种不同的方法,每种方法都有一个不同的参数。对于第一个:

 public void send(Letter letter);

而对于第二个:

 public void send(Package letter);

要从多态性中受益,您应该定义在这两个类实现的接口中定义的公共方法。例如 :

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

但在Java中,overriding的参数不是协变的。因此,您无法通过对Mail参数进行子类型化来实现该接口。 所以这意味着你必须在两个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){
      // ...
   }
}  

要实现它,您应该从高级别的角度定义Mail,您可以在其中定义任何Mail子类所需的行为/方法。 每个Mail子类都将定义它们的实现。 因此,两个发送方实现可以处理send(Mail mail)而无需向下转换参数。

另一答案

这种问题可以通过几种不同的方式解决。最简单的版本是责任链:

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()
    );
另一答案

您可以使用访问者模式。

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

我花了一段时间才完全掌握了第一次遇到它时它是如何工作的。但它是一个非常强大的设计模式,它允许您消除每个实例+转换操作。

这样做的最大好处是,当你定义一个新的Mail子类时,让我们说AirMail。编译器将强制您实现访问(MailVisitor visitor)方法。这将自动使您在MailVisitor上定义新方法。而这反过来又要求您在MailSender类中实现该新方法。因此,在您定义了能够处理新创建的子类型的逻辑之前,您的代码将无法编译。如果您使用了if语句,那么您可能只是忘记为AirMail添加新分支,这将使您的应用程序无法发送任何需要飞机运输的邮件:)

以上是关于如何使用多态将对象映射到辅助类?的主要内容,如果未能解决你的问题,请参考以下文章

浅谈面向对象之封装继承多态!如何使用内部类模拟多继承

Java中,将ResultSet映射为对象和队列及其他辅助函数

如何将 XML 映射到 C# 对象

面向对象-通过代码分析多态

面向对象-通过代码分析多态

面向对象-通过代码分析多态