使用可选参数构建类

时间:2018-05-24 14:37:09

标签: java oop dictionary constructor

在阅读有关构建器模式的this page时,我注意到该类包含可选参数。

示例:

public static class Builder {
        // Required parameters
        private final int servingSize;
        private final int servings;

        // Optional parameters - initialized to default values
        private int calories      = 0;
        private int fat           = 0;
        private int carbohydrate  = 0;
        private int sodium        = 0;

        public Builder(int servingSize, int servings) {
            this.servingSize = servingSize;
            this.servings    = servings;
        }

如果我们删除构建器模式,那么我们的类看起来像这样:

 public final class NutritionFacts
 {
        private final int servingSize;
        private final int servings;
        private final AbstractMap<String,int> optionalFacts;

        public NutritionFacts (int servingSize, int servings, optionalFacts) 
        {
            this.servingSize = servingSize;
            this.servings    = servings;
            // code to make defensive copy of optionalFacts
        }
 }

获取这些可选参数并将它们放在AbstractMap中并将其传递给构造函数会不会有任何问题或不足?

我能看到的唯一缺点是验证它的工作。

  1. 您必须制作防御性副本
  2. 创建private List<String> validOptionalFacts;并循环浏览keys的{​​{1}}并确保AbstractMap值有效,如果没有,则抛出异常。
  3. 检查并抛出重复参数的异常。
  4. 对于这个小例子,可以选择String以外的可选参数,但假设你有10多个可选参数,这意味着为这些参数创建新的map,其中好像它是一张地图,我可以有这样的方法:

    setters/getters

2 个答案:

答案 0 :(得分:0)

由于以下几个原因,地图方法很糟糕:

  1. 您的方法违反了常规的OO编程范例。

    • 定义对象时,还定义了可以分配给它的所有可能属性,使用该对象的代码不定义对象的特征,它只是将对象用于其预期目的。
  2. 代码需要使用该对象的任何代码了解对象。

  3. 代码可读性较差,更容易出错,并且难以在一个地方使用。

  4. 代码创建了抛出之前不存在的运行时异常的可能性(仅此一点是避免您接近的原因)。

  5. 如果我想使用另一个开发人员创建的对象,我只需创建该对象的一个​​实例(或者我可能正在改变已经创建了该对象实例的代码)并查看我可以设置的所有有效属性得到。
    在这个地图场景下,我应该回顾一下自对象的实例化并找到地图的每个变化?如果我想在地图中添加新参数怎么办?如何知道它是否是有效参数?如何在线下编码知道这张地图中包含哪些神秘参数? 如果您对此问题的解决方案是在对象内部定义有效的map参数,那么与标准对象的标准方法相反,这样做的重点是什么?

    为什么这种方法不太理想,还有很多原因,但这解决的解决方案究竟是什么?创建更少的getter和setter?任何IDE都会自动生成它们,但即使不是这种情况,编码时间的轻微改进也不值得这个解决方案会产生的问题清单。

答案 1 :(得分:0)

我所看到的方式除了验证可能变得更加麻烦之外还有一些缺点,正如您已经指出的那样。

如您对问题的评论中所述,只有在您的可选参数都具有相同值时,您的方法才有效。如果他们不这样做,您将不得不使用Map<String,Object>。这反过来会使你很难处理这些参数,因为你丢失了所有类型的信息。

其次,我认为这是您的方法的主要问题:当使用诸如updateParameter(String key, int value)之类的方法时,使用此构建器将关于构建对象所需参数的知识从构建器传输到客户端。比较:

new Builder.updateParameter("calories", 0)
          .updateParameters("fat", 1)
          .updateParameters("carbohydrates",0)
          .build();
new Builder.calories(0)
          .fat(1)
          .carbohydrates(0)
          .build();

使用第二种方法,客户端将知道可以设置碳水化合物,因为构建器为其提供了公共方法。无法设置参数mumbojumbo,因为不存在此类方法。

在第一种方法中,客户是否会知道是否还有其他参数proteins?如果你错误输入怎么办?如果正在构建的对象实际上没有使用提供的参数会发生什么?所有这些问题在坚持没有地图的方法时甚至不会出现。

总结:您的方法以安全性,可读性和可维护性为代价提供了看似更方便(乍一看)。

相关问题