简体   繁体   English

用可选参数构造一个类

[英]Structuring a class with optional parameters

While reading this page about the builder pattern, I noticed the class contained optional parameters. 在阅读有关构建器模式的本页时 ,我注意到该类包含可选参数。

Example: 例:

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

If we remove the builder pattern, so our class looks like this: 如果删除构建器模式,则我们的类如下所示:

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

Would there be any problem or downside to taking those optional parameters and placing them inside an AbstractMap and passing it to the constructor? 将这些可选参数并将其放置在AbstractMap并将其传递给构造函数会不会有问题或不利之处?

The only disadvantage I can see is the work to validate it. 我看到的唯一缺点是要对其进行验证。

  1. You would have to create a defensive copy 您必须创建一个防御性副本
  2. Create a private List<String> validOptionalFacts; 创建一个private List<String> validOptionalFacts; and loop through the keys of the AbstractMap and ensure the String values are valid and if not, throw an exception. 并遍历AbstractMapkeys ,并确保String值有效,如果无效,则引发异常。
  3. Check and throw an exception for duplicated parameters. 检查并为重复的参数引发异常。

For this small example, it might be okay to have the optional parameters outside of a map , but suppose you had 10+ optional parameters, this would mean creating new setters/getters for those parameters, where as if it were a map, I can have a method like this: 对于这个小例子,可以在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;
}

The map approach is bad for a few reasons: 地图方法很糟糕,原因如下:

  1. Your approach is against regular OO programming paradigms. 您的方法与常规的OO编程范例相反。

    • When you define an object you also define all of the possible attributes that can be assigned to it, the code that uses the object does not define characteristics of the object, it simply uses the object for its intended purpose. 定义对象时,还定义了可以分配给它的所有可能的属性,使用该对象的代码未定义该对象的特征,它只是将对象用于其预期目的。
  2. The code requires knowledge of the object by any code that uses the object. 该代码要求使用该对象的任何代码了解该对象。

  3. The code is less readable, more prone to errors, and difficult to be used in more than exactly one place. 该代码可读性较低,更容易出错,并且很难在多个地方使用。

  4. The code creates the possibility of throwing a Runtime Exception where none existed before (this alone is a reason to avoid your approach). 该代码可以在以前不存在的情况下抛出运行时异常(仅这是避免使用该方法的原因)。

If I would like to use an object made by another developer I simply create an instance of that object (or perhaps I am altering code that has already created an instance of the object) and look at all the valid attributes I can set and get. 如果我想使用由另一个开发人员制作的对象,则只需创建该对象的一个​​实例(或者也许我正在更改已经创建该对象的实例的代码),然后查看我可以设置和获取的所有有效属性。
Under this map scenario am I supposed to look back since the instantiation of the object and find every alteration of the map? 在此地图方案下,我是否应该回顾自对象实例化以来发现地图的所有变更? What if I want to add a new parameter to the map, how do I know if it is a valid parameter? 如果我想向地图添加新参数,怎么知道它是否是有效参数? How will code down the line know what mystery parameters are contained in this map? 向下的代码如何知道此映射中包含哪些神秘参数?
If your solution to this problem is to define the valid map parameters inside the object, what is the point of doing this as opposed to the standard way of defining objects? 如果您解决此问题的方法是在对象内部定义有效的地图参数,那么与定义对象的标准方法相反,这样做的目的是什么?

There are many more reasons why this approach would be less than desirable, but what exactly is the solution this solving? 还有很多原因导致这种方法不受欢迎,但是这种解决方案到底是什么呢? Creating fewer getters and setters? 创建更少的getter和setter? Any IDE will generate them automatically, but even if that were not the case a very slight improvement in coding time would never be worth the laundry list of problems this solution would create. 任何IDE都会自动生成它们,但是即使不是这种情况,对编码时间的微小改进也永远都不值得解决方案产生的问题清单。

The way I see it there are some disadvantages in addition to the fact that validation might become much more cumbersome, as you already pointed out. 正如您已经指出的那样,我认为它的方式除了验证可能变得更加麻烦之外,还有一些缺点。

As noted in the comments to your question, your approach will only work if your optional parameters all have the same value. 正如您对问题的评论所指出的那样,只有当所有可选参数都具有相同的值时,您的方法才有效。 If they do not, you will have to resort to using Map<String,Object> . 如果没有,则必须使用Map<String,Object> That in turn will make it hard to deal with these parameters, as you lost all type information. 这样一来,由于丢失了所有类型信息,将使这些参数难以处理。

Secondly, and I think this is the main issue with your approach: When utilizing a method such as updateParameter(String key, int value) the knowledge about which parameters the object being built requires is transferred from the builder to the client using this builder. 其次,我认为这是您的方法的主要问题:当使用诸如updateParameter(String key, int value)之类的方法时,有关使用该构建器将要构建的对象所需的参数的知识从该构建器传输到客户端。 Compare: 相比:

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

new Builder.calories(0)
          .fat(1)
          .carbohydrates(0)
          .build();

With the second approach the client will know that it is possible to set carbohydrates, because the builder provides a public method for it. 使用第二种方法,客户将知道可以设置碳水化合物,因为建造者为此提供了一种公共方法。 It will not be possible to set the parameter mumbojumbo , because no such method exists. 不可能设置参数mumbojumbo ,因为不存在这样的方法。

In the first approach, will the client know if there is an additional parameter proteins ? 在第一种方法中,客户是否会知道是否还有其他参数proteins What happens if you mistype? 如果您输错了怎么办? What happens if a provided parameter is not actually used by the object being built? 如果所提供的参数实际上未被构建的对象使用,会发生什么? All these questions don't even pose themselves when sticking to the approach without the map. 坚持使用没有地图的方法时,所有这些问题甚至都不会摆出姿势。

In summary: Your approach provides seemingly more convenience (at first glance) at the cost of safety, readability and maintainability. 总结:您的方法(乍看之下)提供了更多的便利(以安全性,可读性和可维护性为代价)。

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

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