【问题标题】:Factory in Java when concrete objects take different constructor parameters当具体对象采用不同的构造函数参数时,Java 中的工厂
【发布时间】:2012-12-02 21:02:51
【问题描述】:

我正在尝试在 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);
        }
    }

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

谢谢!

【问题讨论】:

  • 为什么要使用工厂?你想达到什么目的?
  • 认为这不是一个甜蜜的解决方案,您可以传递具体Shape特定参数的添加图。此外:是否需要在创建时提供半径?您可以在创建时设置一些默认半径,然后在创建形状后设置所需的半径。
  • 嗯,我想过这个选项,但它与构造函数的想法相矛盾......
  • 错误不能出现在您显示的位置。您可能得到的是一个错误,因为您的循环工厂不是抽象的,但是您没有实现 ShapeFactory 接口中的任何方法。为什么你认为这个用例需要工厂?
  • 同意。在这里这样做可能是这样一种情况,即一切都是货物集装箱,由相同的钉子和相同的锤子制成。

标签: java design-patterns factory


【解决方案1】:

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

【讨论】:

    【解决方案2】:

    你有两个选择:

    1)Abstract Factory:

    RectangularShape extends Shape

    RoundShape extends Shape

    RectangularShapeFactoryRoundShapeFactory

    2) Builder(另请参阅 Effective Java 中的第 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();
    

    【讨论】:

    • 抽象工厂不能解决这个问题。所有ShapeFactory 实现仍然需要一个通用接口。
    • (2) 这里的构建器看起来只是隐式的服务定位器反模式。
    【解决方案3】:

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

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

    【讨论】:

    • 您建议今天选择选项 2 吗?
    • 只有第一个选项在适当的抽象工厂模式中可用。另一种制作具有 CreateTriangle 和 CreateCircule 方法的工厂的方法,但它似乎与 OP 目标正交。
    【解决方案4】:

    拥有 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);
        }
    }
    

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

    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

    【讨论】:

      【解决方案5】:

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

      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);
              }
          }
      

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

      【讨论】:

        猜你喜欢
        • 2017-03-19
        • 1970-01-01
        • 2018-10-01
        • 2011-09-30
        • 1970-01-01
        • 1970-01-01
        • 2019-09-07
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多