我正在尝试找到“正确”的方法来创建一个消耗超类型A的通用对象的工厂,并根据它们的类型,它生成超类型B的通用对象,但我想我已被咬了Java的非神化的泛型。实现这样的东西的“真正的Java方式”是什么:
工厂要使用的虚构对象的层次结构:
abstract class Shape<T extends Shape> {
private String userFriendlyShapeName = "not set";
public abstract String getArea();
public final String getUserFriendlyShapeName() { return userFriendlyShapeName; }
}
class Circle extends Shape<Circle> {
public String getArea() { return ""; //return area }
public String getCircumference() { return ""; //return circumference }
public String getRadius() { return ""; //get radius }
}
class Square extends Shape<Square> {
public String getArea() { return ""; //return area }
public String getSideLength() { return ""; //return side length }
}
虚构的工厂:
class DrawableShapeFactory {
public <T extends Shape, S extends DrawableShape> DrawableShape<S> createDrawableShapeFrom(String userFriendlyDrawableShapeName, Shape<T> shape) {
//these won't work:
//T drawableShape = (T) shape;
//return createDrawableShape(userFriendlyDrawableShapeName, drawableShape);
//or
// return createDrawableShape(userFriendlyDrawableShapeName, (T) shape);
return null;
}
private DrawableShape<DrawableCircle> createDrawableShape(String userFriendlyName, Circle circle) {
DrawableShape<DrawableCircle> drawableShape = new DrawableCircle(userFriendlyDrawableShapeName, circle);
return drawableShape;
}
private DrawableShape<DrawableSquare> createDrawableShape(String userFriendlyName, Square square) {
DrawableShape<DrawableSquare> drawableShape = new DrawableSquare(userFriendlyDrawableShapeName, square);
}
}
在这种情况下,有一个类可以生成Shapes,然后将它们作为形状传递给虚构的Drawer类,然后实例化一个合适的可绘制形状,例如:
class ShapeCreator {
...
ShapeCreator() {
shapeDrawer.draw(someShapeCreatedEarlier);
}
...
}
class ShapeDrawer() {
...
public draw(Shape shape) {
draw(drawableShapeFactory.createDrawableShapeFrom(shape));
}
private draw(DrawableShape<Circle> drawableShape) {
//an implementation that draws a circle on the wall
}
private draw(DrawableShape<Square> drawableShape) {
//an implementation that draws a square on the wall
}
}
对此帖子的长度以及任何潜在的语法问题表示歉意。欢迎任何反馈(包括设计或任何真实的)
由于
答案 0 :(得分:0)
您正在使用的一般问题类型称为双重调度问题。通常,使用多态性,以便根据调用方法的对象,方法调用的行为不同。例如:
public interface Shape {
public String getSimpleName();
}
public class Circle implements Shape {
@Override
public String getSimpleName() {
return "Circle";
}
}
public class Square implements Shape {
@Override
public String getSimpleName() {
return "Square";
}
}
这称为单调度,因为getSimpleName
方法的行为仅取决于一个因素 - 即被调用对象的特定实现类型(即Circle
或Square
) 。在double-dispatch中,调用的行为会根据调用它的对象和传递给方法的参数(因此在double-dispatch中的 double )而改变。
要解决您的问题,您可以完全放弃工厂,如果您创建一个名为Drawer
的新界面和一个名为Shape
draw
的新方法,该方法接受Drawer
1}}对象。这个draw
方法除了遵循提供的Drawer
对象之外什么都不做,并且根据调用Shape
方法的draw
,绘制形状的逻辑会有所不同。这称为Visitor Pattern,其问题实现的示例如下所示:
public interface Shape {
public void draw(Drawer drawer);
}
public interface Drawer {
public void draw(Circle circle);
public void draw(Square square);
}
public class Circle implements Shape {
@Override
public void draw(Drawer drawer) {
drawer.draw(this);
}
}
public class Square implements Shape {
@Override
public void draw(Drawer drawer) {
drawer.draw(this);
}
}
public class SimpleDrawer implements Drawer {
@Override
public void draw(Circle circle) {
System.out.println("Drew a simple circle");
}
@Override
public void draw(Square square) {
System.out.println("Drew a simple square");
}
}
public class ComplexDrawer implements Drawer {
@Override
public void draw(Circle circle) {
System.out.println("Drew a complex circle");
}
@Override
public void draw(Square square) {
System.out.println("Drew a complex square");
}
}
请注意,您可以将任何其他方法添加到Drawer
和Shape
接口,但这些是实现该模式所需的最小方法。然后,您可以通过执行以下操作来绘制形状:
Circle circle = new Circle();
SimpleDrawer drawer = new SimpleDrawer();
circle.draw(drawer);
这将输出以下内容:
Drew a simple circle
使用访问者模式应该不需要将Shape
个对象包装到Drawable
个对象中,并删除非实现类型的问题。问题在于,当您添加新的Shape
实现时,您必须向draw
接口添加一个新的Drawer
方法,该接口接受新实现作为参数。这可能会妨碍一点灵活性,但好处是您不必担心用于包装Shape
对象的古怪工厂方法。
draw(Drawer)
的重复实施非常重要,因为每个this
实施的Shape
类型都会发生变化。实质上,传递给this
的{{1}}参数指示调用draw
实现的draw
方法(例如Drawer
或draw(Circle)
)。作为额外的好处,在draw(Square)
实现中的每个draw
方法中,传入的Drawer
是其实现类型(例如Shape
或Circle
)所以不需要不安全的转换:您已经拥有该对象的实际类型。
以上是主要解决方案,但如果您只有一个Square
实施 - 请说Drawer
- 必须提供DefaultDrawer
draw
方法每次Shape
都可能是一种痛苦。要缓解此问题,可以向Drawer
接口添加draw()
方法,并使用抽象类提供默认实现。例如:
Shape
public interface Shape {
public void draw(Drawer drawer);
public void draw();
}
public abstract class DefaultShape implements Shape {
@Override
public void draw() {
draw(new DefaultDrawer());
}
}
实现将扩展Shape
类并为DefaultShape
提供实现。然后,您只需调用draw(Drawer drawer)
即可绘制形状,如draw()
中所示。缺点是现在您已将circle.draw()
实施与特定Shape
绑定(即Drawer
取决于特定 DefaultShape
而非一般Drawer
接口)和Drawer
实现现在扩展抽象类而不是实现接口。