如何以编程方式检索方法的参数



作为一个家庭作业项目,我被要求在层次结构中创建一堆类,这些类在构造函数中采用不同的参数,并允许用户交互创建此类对象。为了简单起见,我们假设类是BaseChildChild扩展了Base。项目中任何未来的类都将扩展Base或其子类之一。

虽然项目的重点不是编写优雅或可维护的代码,但我正在尝试编写这样的代码。

理想情况下,在弄清楚用户想要实例化什么对象之后,我将有一种方法来弄清楚创建这样一个对象所需的参数,并将该列表传递给负责从用户那里检索这些参数的方法。我想到了一种静态方法,getNecessaryArguments(),比如:

public class Base {
public static List<String> getNecessaryArguments() {
return Arrays.asList("name", "age");
}
public Base(String name, String age) {
...
}
}
public class Child extends Base {
public static List<String> getNecessaryArguments() {
final List<String> baseArgs = Base.getNecessaryArguments();
baseArgs.addAll(Arrays.asList("position"));
}
public Child(final String name, final String age, final String position) {
super(name, age);
...
}

然后我会做一些类似UserInterface.requestParameters(XXX.getNecessaryArguments())的事情。

不过,这个代码有几个明显的问题:

  1. 每个类需要并行维护两个参数列表:构造函数的实际参数和getNecessaryArguments()返回的参数
  2. Child扩展的类必须在其getNecessaryArguments()中明确提及,因为super()不能在静态上下文中使用

如果未来设计发生变化,这一切都可能导致不同步问题。

为了使代码能够适应一些设计更改,处理这些问题的最佳方式是什么?我想到了反射,但我认为您无法从中检索参数名称,只能检索方法签名。

您可以用Map替换List。然后,它可以同时保存参数名称和实际值。它可以用于复制对象的当前属性,编辑其中的一个或多个属性,并将其传递给新对象的构造函数。

当对象具有查询其当前状态的方法时,可以用静态最终模板对象替换静态方法,该对象保存可以通过所述方法查询的属性的默认值。

public class Base {
static final Base TEMPLATE = new Base();
final String name, age;
protected Base() {
name = "no name";
age = null;
}
public Base(String name, String age) {
this.name = name;
this.age = age;
}
public Base(Map<String,String> map) {
name = map.get("name");
age = map.get("age");
}
public Map<String,String> arguments() {
HashMap<String,String> map = new HashMap<>();
map.put("name", name);
map.put("age", age);
return map;
}
}
public class Child extends Base {
static final Child TEMPLATE = new Child();
final String position;
protected Child() {
position = "unspecified";
}
public Child(final String name, final String age, final String position) {
super(name, age);
this.position = position;
}
public Child(Map<String, String> map) {
super(map);
this.position = map.get("position");
}
@Override
public Map<String, String> arguments() {
final Map<String, String> map = super.arguments();
map.put("position", position);
return map;
}
}

你可以像一样使用它

Map<String, String> values = Child.TEMPLATE.arguments();
UI.editValues(values);
Child ch = new Child(values);

这导致了Builder模式,因为有理由用专用类替换通用Map。这些类可以定义具有不同类型的特性,还可以强制执行约束。如果类可以通过接口报告约束,则可获得额外的积分。

但它需要为每一个要构建的实际类型专门化构建器类型。图案有很多不同的变化。直接从Map的使用中得出的一个是:

public class Base {
public static class Builder {
String name = "no name";
int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = Objects.requireNonNull(name);
}
public int getAge() {
return age;
}
public void setAge(int age) {
if(age < 0) throw new IllegalArgumentException();
this.age = age;
}
}
final String name;
final int age;
public Base(String name, int age) {
if(age < 0) throw new IllegalArgumentException();
this.name = Objects.requireNonNull(name);
this.age = age;
}
public Base(Builder b) {
name = b.name;
age = b.age;
}
public void fill(Builder b) {
b.setName(name);
b.setAge(age);
}
}
public class Child extends Base {
public static class Builder extends Base.Builder {
String position = "unspecified";
public String getPosition() {
return position;
}
public void setPosition(String position) {
this.position = Objects.requireNonNull(position);
}
}
final String position;
public Child(final String name, final int age, final String position) {
super(name, age);
this.position = Objects.requireNonNull(position);
}
public Child(Builder b) {
super(b);
this.position = b.position;
}
public void fill(Builder b) {
super.fill(b);
b.setPosition(position);
}
}

这可以像一样使用

Child.Builder b = new Child.Builder();
UI.editValues(b);
Child c = new Child(b);

请注意,这些构建器遵循关于属性的Bean模式,这对于通用用户界面非常有用。当然,这意味着UI处理可变对象,但这也适用于ListMap变体。关键是构建器的状态将用于在正确的时间构建不可变对象,例如在将它们传递给后续处理代码之前。

JavaBeans,如果我选择ConstructorProperties,可能会很感兴趣地看到一些想法。例如,要与bean类Foo并行,就要有一个描述类FooBean。

然而,家庭作业是关于你自己在代码中提出的想法。不要做复杂的事情,试着找到没有绒毛的东西。

可以编译时保留参数名称,如注释所示。这将是一种特殊情况:开发人员必须更改他们的编译器选项。

或者,On可以为每个参数生成运行时范围的注释,这样就可以通过反射接收该注释,该注释的值就是参数名称。这将是一个没有问题的解决方案。

您对一段基于类的信息使用静态getter。这有点难以处理,例如没有继承。

或者,您可以为描述信息BeanClassInfo的构造函数创建一个类,并在一个类BeanRegistry:中

public class BeanRegistry {
private Map<Class<? extends Base>, BeanClassInfo> classInfos = new HashMap<>();
public void registerClass(Class<? extends Base> type, BeanClassInfo classInfo) {
classInfos.put(type, BeanClassInfo);
}
...
}
public class BeanClassInfo {
List<String> argNames = ...
}
public class Base {
protected static BeanRegistry registry = new BeanRegistry();
}
public class Child extends Base {
static {
BeanClassInfo classInfo = new BeanClassInfo();
...
registry.registerClass(Child.class, classInfo);
}

static { ... }是一个静态初始化器

正如您所看到的,您只有一个Base.beanRegistry对象,很容易找到。孩子们必须做一些事情来"注册"自己。

在您写的评论中:

我宁愿避免可变

在这种情况下,您可以为每个不可变类创建一个生成器。例如,String是不可变的,但创建String的StringBuilder是可变的。

此外,变量信息可以保存在生成器中,因为生成器必须知道类构造所需的所有字段。

class Child extends Base {
private final String position;
public Child(final String name, final String age, final String position) {
super(name, age);
this.position = position;
}
/* ... */
}
class ChildBuilder {
private String name;
private String age;
private String position;
public List<String> parameters() {
return Arrays.asList("name", "age", "position");
}
public ChildBuilder withName(String name) {
this.name = name;
return this;
}
public ChildBuilder withAge(String age) {
this.age = age;
return this;
}
public ChildBuilder withPosition(String position) {
this.position = position;
return this;
}
public Child build() {
return new Child(name, age, position);
}
}

最新更新