Java 如何使用多态性将对象映射到帮助器类?
我想用多态性替换switch语句。让我们以Java 如何使用多态性将对象映射到帮助器类?,java,polymorphism,Java,Polymorphism,我想用多态性替换switch语句。让我们以邮局为例。这家邮局发送信件和包裹,它们都是邮件的子类。有特定的方式发送不同类型的邮件,因此有信函服务和打包服务,这两种服务都是邮件服务 public class PostOffice { @Inject private LetterSender letterSender; @Inject private PackageSender packageSender; public void send( Mail m
邮局
为例。这家邮局发送信件
和包裹
,它们都是邮件
的子类。有特定的方式发送不同类型的邮件
,因此有信函服务
和打包服务
,这两种服务都是邮件服务
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?我被告知可以使用多态性删除这些,但我仍然不知道如何根据实际逻辑将正确的
邮件类型“路由”到正确的邮件发件人,LetterSender
和PackageSender
可能有两个不同的方法,每个方法都有一个不同的参数。
对于第一个:
public void send(Letter letter);
对于第二个问题:
public void send(Package letter);
为了从多态性中获益,您应该在这两个类实现的接口中定义一个公共方法。例如:
public interface MailSender{
void send(Mail mail);
}
但是在Java中,重写的参数不是协变的。因此,您无法通过子键入Mail
参数来实现该接口。
因此,这意味着您必须在两个发件人类中实现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)
,而无需向下转换参数 这类问题可以用几种不同的方法来解决。最简单的版本是责任链:
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()
);
接口发送器{
可以发送(邮件);
无效发送(邮件);
}
...
名单发送者;
...
senders.stream()
.filter(s->s.canSend(邮件))
.findAny()
.IfPresentoreSethrow(
s->s.send(邮件),
()->新事物例外()
);
您可以使用访问者模式进行此操作
将接口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}
}
现在,在邮局课程中,您可以让邮件发送者访问刚刚到达的邮件包:
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操作
这样做的最大优点是,当您定义一个新的邮件子类时,比如说AirMail。编译器将强制您实现visit(MailVisitor)方法。这将自动使您在MailVisitor上定义一个新方法。这反过来又要求您在MailSender类中实现该新方法。因此,在定义了能够处理新创建的子类型的逻辑之前,代码不会编译。然而,如果您使用了if语句,您可能会忘记为AirMail添加一个新的分支,这将使您的应用程序无法发送任何需要飞机运输的邮件:)该模式实现得很好。附带说明:对于已访问的类,accept()
优于visit()
。但我不认为这是一个很好的模式用例。实际代码将两个发送方的逻辑划分为不同的类。访问者为同一访问者类中的已访问类收集相同类型的操作。它改变了班级的总体责任。在需要实现双重分派或在同一类中收集相同操作时,可以使用访问者模式。这实际上不是OP的要求。OP问他如何删除条件语句和instanceof语句。我相信这是实现这一目标的最干净的解决方案,它也消除了铸造的任何需要。如果确实希望在单独的类中实现,MailSender类还可以委托给PackageSender和LetterSender。“如果确实希望在单独的类中实现,MailSender类还可以委托给PackageSender和LetterSender。”当然,它将创建一个没有任何值的间接寻址。对于已经添加了间接寻址的访问者,它将进行大量间接寻址:3个委托调用来执行任务。