实现基于提供的通用对象实例化对象的工厂方法的正确方法是什么

时间:2018-04-07 16:45:16

标签: java generics factory

我正在尝试找到“正确”的方法来创建一个消耗超类型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
    }
}

对此帖子的长度以及任何潜在的语法问题表示歉意。欢迎任何反馈(包括设计或任何真实的)

由于

1 个答案:

答案 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方法的行为仅取决于一个因素 - 即被调用对象的特定实现类型(即CircleSquare) 。在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");
    }
}

请注意,您可以将任何其他方法添加到DrawerShape接口,但这些是实现该模式所需的最小方法。然后,您可以通过执行以下操作来绘制形状:

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方法(例如Drawerdraw(Circle))。作为额外的好处,在draw(Square)实现中的每个draw方法中,传入的Drawer是其实现类型(例如ShapeCircle)所以不需要不安全的转换:您已经拥有该对象的实际类型。

以上是主要解决方案,但如果您只有一个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实现现在扩展抽象类而不是实现接口。