如何使用构建器模式为抽象超类的子类遵循最佳Java实践?

时间:2017-09-29 08:12:57

标签: java oop inheritance builder discord

// CommandBuilder.java
public class CommandBuilder {

 public String name; // required
 public String description = "Default Value"; //optional

 public CommandBuilder(String name) {
  this.name = name;
 }

 public CommandBuilder setDescription(String description) {
  this.description = description;
  return this;
 }

 public CommandBuilder build() {
  return this;
 }
}

// Command.java
public abstract class Command extends ListenerAdapter {

 private String name;
 private String description;

 protected abstract void execCommand(MessageReceivedEvent event);

 public Command(CommandBuilder builder) {
  this.name = builder.name;
  this.description = builder.description;
 }

@Override
public void onMessageReceived(MessageReceivedEvent event) {
   execCommand(event);
 }
}

// ExampleCommand.java
public class ExampleCommand extends Command {

 public ExampleCommand(CommandBuilder builder) {
  super(builder);
 }

 @Override
 protected void execCommand(MessageReceivedEvent event) {
  // ...
 }
}

// Bot.java
public class Bot() {

 public static void main(String[] args) {

  // ...
  jdaBuilder.addEventListener(
   new ExampleCommand(
    new CommandBuilder("Example Command").setDescription("You know it.").build();
   )
  );
  // ...
 }
}

所以,我需要一些关于代码风格的建议。以上大致是我在JDA中的Discord bot的代码设置。 <{1}}做什么或jdaBuilder.addEventListener(Object)是什么,并不重要。

在构造具有继承类型MessageReceivedEvent的对象时,我使用构建器模式来避免过多的构造函数重载,因为在我的实际代码中,Command类可以接受的不仅仅是两个参数。 Command的问题是CommandBuilder没有并且不能返回类型build()的对象(因为它是抽象的)而是类型Command本身, Command的子类作为参数(然后传递给CommandBuilder)。 THAT的问题在于:

  1. 不需要调用Command,因为每个其他方法都返回build()以及
  2. 使用4-6个参数实例化CommandBuilder的子类可能会在主要内容中非常混乱。
  3. 那么,解决这个问题的最佳方法是什么?我想过使用一个接口,但在我的抽象Command类中,有一些带有“默认”代码的方法,如果需要它们,子类可以选择覆盖它(但它不是必需的!)。 这些默认方法使用Command类的其他方法,因此我不能将它们重构为接口作为真正的默认方法。

    我的代码运行得很好,我只是认为通过我必须实例化对象的方式,我在某处做了错误的转向。关于如何重构或重写我的代码以遵循最佳Java实践的任何建议?

1 个答案:

答案 0 :(得分:0)

也许你会像这样重构你的build()

public <T extends Command> T build(Function<CommandBuilder, T> constructor) {
    return constructor.apply(this);
}

这种方式允许构建器接受任何构造函数引用,例如

FooCommand cmd = builder.build(FooCommand::new);

也许另外你可以添加适当的代码,或者允许反射,以便

FooCommand cmd = builder.build(FooCommand.class);

也可以使用,但如果你没有匹配的构造函数,那么前者的优势在于编译时错误。