在Java中,我想根据用户输入创建一些几何形状。诀窍是我无法更改现有的API,所以我不能使用像
这样的varargs语法public Shape(Object... attrs) {}
用户输入:
shape.1.triangle.arg1 = 3
shape.1.triangle.arg2 = 4
shape.1.triangle.arg3 = 5
shape.1.triangle.arg4 = "My first triangle"
shape.2.rectangle.arg1 = 4
shape.2.rectangle.arg2 = 7
shape.2.rectangle.arg3 = "Another string label"
应该导致方法调用,如:
Shape s1 = new Triangle(arg1, arg2, arg3, arg4);
或者一般地说:
String shapeType = "triangle";
Object[] args = {arg1, arg2, arg3, arg4};
// This won't work, because newInstance() doesn't take args
Shape s1 = Class.forName(shapeType).newInstance(args);
String shapeType = "rectangle";
Object[] args = {arg1, arg2, arg3};
// This won't work, because newInstance() doesn't take args
Shape s2 = Class.forName(shapeType).newInstance(args);
问题是Triangle的构造函数不允许使用varargs(...),我无法更改它。具体来说,构造函数是
public Triangle (int a, int b, int c, String label) {}
public Rectangle (int a, int b, String label) {}
那么如何根据用户输入创建正确的形状?
答案 0 :(得分:4)
用工厂对象包裹三角形和方形类。然后,工厂可以根据您传入的内容确定要创建的内容,并且不必更改底层三角形和矩形对象的签名。
答案 1 :(得分:1)
这是一种有趣的方法。问题不在于Triangle
缺少vararg构造函数,而是newInstance()
只能调用默认构造函数。
mschaef提出了一个合理的方法。它仍然需要静态意识到每个形状的构造函数形式。然后,这些知识将隐藏在各自的工厂中。这可能是最好的解决方案。问题是你必须为每个形状写一个工厂。
但是,您可以编写使用反射API动态调用正确构造函数的代码。如果你有很多形状,那么它将在一个地方解决所有这些问题。您将获得Class
对象,然后调用getDeclaredConstructors()
以获取构造函数数组。 getParameterTypes()
可以查询构造函数参数类型。您的代码必须根据您拥有的参数找到理想的构造函数。这基本上是编译器对静态类型所做的。
这个解决方案不是特别优雅,但确实有加分: 如果您正确地编写一次,它将始终适用于您可能引入的任何新形状。您可以将代码放在一个整洁的实用程序类中,而不要再查看它。
一般来说,我仍然认为使用反射是一种不受欢迎的选择。
答案 2 :(得分:0)
我认为您需要使用Bridge Pattern,或者至少使用它的变体。您的网桥可以遵循您希望拥有的签名,并决定如何调用基础类。
请注意,如果您的基础库无法执行您需要执行的操作,则无法解决此问题。如果它只需要4个参数来制作一个三角形。你的桥可能会抛出某种不支持的异常。
答案 3 :(得分:0)
如果您想实施原始解决方案(myClass.newInstance(args)
) - 您可以这样做(一个建议,确定您可以提出自己的解决方案):
shape.1.triangle.arg1.int = 3
shape.1.triangle.arg2.int = 4
shape.1.triangle.arg3.int = 5
shape.1.triangle.arg4.String = "My first triangle"
shape.2.rectangle.arg1.int = 4
shape.2.rectangle.arg2.int = 7
shape.2.rectangle.arg3.String = "Another string label"
然后,您的代码可以使用class.getConstructor(Class<?> ... parameterType)
来获取正确的构造函数,并在newInstance(Object ... args)
对象上调用Constructor
。
示例:
Class<?> shape = Class.forName("triangle");
Constructor<?> constructor = shape.getConstructor(Integer.TYPE, Integer.TYPE, Integer.TYPE, String.class);
constructor.newInstance(Integer.TYPE, Integer.TYPE, Integer.TYPE, String.class);
希望这有用。
另一种方法是使用FactoryBuilder模式。
shape.triangle.factory=TriangleFactory
shape.rectangle.factory=RectangleFactory
shape.1.triangle.arg1 = 3
shape.1.triangle.arg2 = 4
shape.1.triangle.arg3 = 5
shape.1.triangle.arg4 = "My first triangle"
shape.2.rectangle.arg1 = 4
shape.2.rectangle.arg2 = 7
shape.2.rectangle.arg3 = "Another string label"
你需要:
public interface IShapeFactory
{
public IShape buildShape(Object ... args);
}
public class TriangleFactory implements IShapeFactory
{
public IShape buildShape(Object ... args)
{
return new Triangle(args[0], args[1], args[2], args[3]); // You will need some casting here :)
}
}
您需要做的所有代码都是实例化工厂,使用参数调用buildShape
方法,工厂将执行它需要做的事情。
第二个解决方案对我来说似乎更好,我觉得你更容易,但如果从外部提供形状,你的用户可能更难以实现新的形状。
你的电话。