繁体   English   中英

用可选参数构造一个类

[英]Structuring a class with optional parameters

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

例:

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; 并遍历AbstractMapkeys ,并确保String值有效,如果无效,则引发异常。
  3. 检查并为重复的参数引发异常。

对于这个小例子,可以在map外部放置可选参数,但是假设您有10个以上可选参数,这意味着为这些参数创建新的setters/getters ,就像地图一样,我可以有这样的方法:

public NutritionFacts updateParameter(String key, int value){ 
     //call method to validate the key/value
     //update fact
     //return a new object that reflects our change
     return this; 
}

public int retrieveValuefor(String key){
     //call method to validate the key
     //Get value associated with the key
     //return value associated with the key
     return factValue;
}

地图方法很糟糕,原因如下:

  1. 您的方法与常规的OO编程范例相反。

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

  3. 该代码可读性较低,更容易出错,并且很难在多个地方使用。

  4. 该代码可以在以前不存在的情况下抛出运行时异常(仅这是避免使用该方法的原因)。

如果我想使用由另一个开发人员制作的对象,则只需创建该对象的一个​​实例(或者也许我正在更改已经创建该对象的实例的代码),然后查看我可以设置和获取的所有有效属性。
在此地图方案下,我是否应该回顾自对象实例化以来发现地图的所有变更? 如果我想向地图添加新参数,怎么知道它是否是有效参数? 向下的代码如何知道此映射中包含哪些神秘参数?
如果您解决此问题的方法是在对象内部定义有效的地图参数,那么与定义对象的标准方法相反,这样做的目的是什么?

还有很多原因导致这种方法不受欢迎,但是这种解决方案到底是什么呢? 创建更少的getter和setter? 任何IDE都会自动生成它们,但是即使不是这种情况,对编码时间的微小改进也永远都不值得解决方案产生的问题清单。

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

正如您对问题的评论所指出的那样,只有当所有可选参数都具有相同的值时,您的方法才有效。 如果没有,则必须使用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 如果您输错了怎么办? 如果所提供的参数实际上未被构建的对象使用,会发生什么? 坚持使用没有地图的方法时,所有这些问题甚至都不会摆出姿势。

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM