54

我正在尝试在 Java 中实现工厂模式。我有一个名为 Shape 的类,其中 Circle 和 Triangle 扩展了。问题是 Shape 构造函数只获得 2 个参数,而 Circle 获得 3 个参数,Triangle 也是如此(我不会在代码部分显示,因为与 Circle 相同)。为了更好地展示它:

    private interface ShapeFactory{
        public Shape create(int x, int y);
    }

    private class CircleFactory implements ShapeFactory{
        public Shape create(float radius, int x, int y){ //error
            return new Circle(radius, x,y);
        }
    }

任何想法如何克服这个问题?我不能从工厂内部接收用户的输入(必须从外部接收)。

谢谢!

4

5 回答 5

34

你有两个选择:

1)抽象工厂

RectangularShape extends Shape

RoundShape extends Shape

并且RectangularShapeFactoryRoundShapeFactory

2) Builder(参见 Effective Java 中的 Item 2)

public Shape {
    private final int x;
    private final int y;
    private final double radius;

    private Shape(Builder builder) {
        x = builder.x;
        y = builder.y;
        radius = builder.radius;
    }

    public static class Builder {
        private final int x;
        private final int y;
        private double radius;

        public Builder(int x, int y) {
            this.x = x;
            this.y = y;
        }

        public Builder radius(double radius) {
            this.radius = radius;
            return this;
        }

        public Shape build() {
            return new Shape(this);
        }    
    }
}

//in client code 

    Shape rectangle = new Shape.Builder(x,y).build();
    Shape circle = new Shape.Builder(x,y).radius(radiusValue).build();
于 2012-12-14T20:59:20.040 回答
9

你试图做的事情是根本不可能的。如果构造函数参数不同,那么客户端代码将不得不Circle为 aSquare和 a 做不同的工作,而你不能用统一的代码解决这个问题。如果工厂除了处理您认为应该在工厂中发生的构造函数参数之外还有其他工作,那么您需要将其发布到您的问题中并说明您在分解此常见代码工作时遇到的困难。

于 2012-12-14T20:39:06.190 回答
7

您的所有实现都必须采用相同数量的参数,您有三个选项:

  • 让工厂存储附加参数,因此您只需要提供例如中心。
  • 让工厂接受所有论据,即使有些工厂可能会忽略其中一些。
  • 有一个可变长度的参数。例如'double ...' 问题是调用者需要知道工厂需要什么,这违背了工厂的目的。恕我直言。
于 2012-12-14T21:15:34.307 回答
1

拥有 Shape 接口通常是一个糟糕的设计,因为它非常有限。您需要不同的信息来描述不同的形状。调整大小就是一个很好的例子。对于圆形,您需要更改半径,对于矩形,您需要更改两侧,这意味着传递两个参数而不是一个参数。

您可以通过传递某种形状描述符来克服这个问题,例如实际形状必须适合的矩形。因此,假设您的所有形状都使用类预定义并且您想要缩放它们,则可以正确调整它的大小。如果您想要自定义形状,则必须以某种方式扩展形状描述符以包含自定义形状所需的所有信息,但要与现有形状保持兼容。这不一定很难,您可以添加可以为空的属性或参数。这里我只添加新参数。

private interface ShapeFactory{
    public Shape create(float x, float y, float width, float height);
}

private class CircleFactory implements ShapeFactory{
    public Shape create(float x, float y, float width, float height){
        float radius = Math.min(width, height);
        return new Circle(radius, x, y);
    }
}

另一个想法是您通常以这种方式使用工厂(根据您的需要,鞋面也可以很好):

private interface ShapeFactory{
    public Shape create(float x, float y, float width, float height, bool isCircle);
}

private class MyShapeFactory implements ShapeFactory{
    public Shape create(float x, float y, float width, float height, bool isCircle){
        if (isCircle)
            return new Circle(Math.min(width, height), x, y);
        else
            return new Rectangle(width, height, x, y);
    }
}

所以工厂不一定和构造函数有相同的参数。很多人都有这种印象,因为我猜他们试图自动化工厂并且只传递一个类列表而没有任何关于如何实例化它们的信息。自动化 DI 容器也会犯同样的错误。

这里真正重要的是,上层代码是否想知道它返回的是哪种 Shape 实现。但在某些情况下,您可能拥有或重构为某种通用描述符。例如,您不一定有Shape.scale(width, height)方法,如果有,您将无法调整圆形或矩形的大小,因为就像构造函数一样,那里的缩放比例是不同的。但是如果你想要的只是调用类似的东西Shape.draw(canvas),那么我想你很高兴。

同时我发现了一个类似的问题,有类似的答案,也许你也可以从中学习:https ://softwareengineering.stackexchange.com/a/389507/65755

于 2020-01-14T03:25:42.217 回答
0

您可以使用一个类来包装工厂参数,如下所示:

public interface ShapeArguments{
}

public class CircleArguments implements ShapeArguments{
 ...

 public CircleArguments(... radius,... x,... y){
    ...
 }
}

private interface ShapeFactory{
        public Shape create(ShapeArguments args);
    }

    private class CircleFactory implements ShapeFactory{
        public Shape create(ShapeArguments args){
            CircleArguments circleArgs = (CircleArguments)args;
            return new Circle(circleArgs.radius, circleArgs.x,circleArgs.y);
        }
    }

如果形状参数之间有任何共同的参数,您可以使用继承来更好地管理它

于 2021-02-20T21:59:57.487 回答