java builder模式用法

时间:2016-02-09 09:06:50

标签: java design-patterns pojo

最近我看到一些开发人员使用嵌套的构建器类(如

)编写他们的VO
<form method="post">
    <select name="XStocLa" id="StocLa">
        <option>A</option>
        <option>B</option>
    </select>
    <input type="submit" value="Select">
</form>

<form method="post">
    <input type="date" name="ordersmonth">
    <input type="submit" value="Select">
</form>
<?php

if(isset($_POST['ordersmonth'])) {
   $OrdersMonth = $_POST['ordersmonth'];    
   $sql ="SELECT * FROM " .$OrderTable. " WHERE YEAR(Date) = YEAR('" .$OrdersMonth. "') AND MONTH(DATE) = MONTH('" .$OrdersMonth. "') AND StocLa = '$XStocLa' ORDER BY Date DESC, ID ASC";
   $result = $conn->query($sql);
} else {
   $sql ="SELECT * FROM " .$OrderTable. " WHERE StocLa = '$XStocLa' ORDER BY Date DESC, ID ASC";
   $result = $conn->query($sql);
}
?>

现在,他们声称这使代码更具可读性。我的观点是,这有以下缺点:

  1. 我不能简单地添加字段并期望我的IDE为我完成代码,因为现在我也需要更新这个内部类。

  2. 简单的POJO携带的代码与VO无关。

  3. 如果我在这里遗漏了什么,我正在寻找任何建议。随意添加您的想法。

    此修改后的示例代码如下所示:

    public class User {
    
        private String firstName;
        private String lastName;
    
        public String getFirstName() {
            return firstName;
        }
    
        public void setFirstName(String firstName) {
            this.firstName = firstName;
        }
    
        public String getLastName() {
            return lastName;
        }
    
        public void setLastName(String lastName) {
            this.lastName = lastName;
        }
    
        public static class UserBuilder {
    
            private String firstName;
            private String lastName;
    
            public User build() {
                User user = new User();
                user.firstName = firstName;
                user.lastName = lastName;
                return user;
            }
    
            public UserBuilder withFirstName(String firstName) {
                this.firstName = firstName;
                return this;
            }
    
            public UserBuilder withLastName(String lastName) {
                this.firstName = firstName;
                return this;
            }       
        }
    
    }
    

3 个答案:

答案 0 :(得分:1)

以下是约书亚布洛赫的文章。他非常清楚地解释了为什么,何时以及如何使用构建器:http://www.informit.com/articles/article.aspx?p=1216151&seqNum=2

这是他的书“Effective Java”中的一个项目。如果您对Java有一点经验,我强烈建议您阅读本书。

要点:

当你得到一个具有很多属性的类时,有几种方法可以创建一个对象并初始化它。

如果你逐个设置每个属性,它可以是罗嗦的,你的对象可以在创建后改变。使用此方法,不可能使您的类不可变,并且您无法确定您的对象是否处于一致状态。

例子来自文章:

public class NutritionFacts {
    // Parameters initialized to default values (if any)
    private int servingSize  = -1; // Required; no default value
    private int servings     = -1;  //     "     "      "      "
    private int calories     = 0;
    private int fat          = 0;
    private int sodium       = 0;
    private int carbohydrate = 0;

    public NutritionFacts() { }
    // Setters
    public void setServingSize(int val)  { servingSize = val; }
    public void setServings(int val)     { servings = val; }
    public void setCalories(int val)     { calories = val; }
    public void setFat(int val)          { fat = val; }
    public void setSodium(int val)       { sodium = val; }
    public void setCarbohydrate(int val) { carbohydrate = val; }
}

您可以使用伸缩构造函数。它可以使您的对象不可变。但是,如果您获得许多属性,则可能很难编写和读取代码。当你只想要使用一个setted属性创建,而不幸的是这个是构造函数的最后一个参数时,你必须设置所有参数。

例子来自文章:

public class NutritionFacts {
    private final int servingSize;  // (mL)            required
    private final int servings;     // (per container) required
    private final int calories;     //                 optional
    private final int fat;          // (g)             optional
    private final int sodium;       // (mg)            optional
    private final int carbohydrate; // (g)             optional

    public NutritionFacts(int servingSize, int servings) {
        this(servingSize, servings, 0);
    }

    public NutritionFacts(int servingSize, int servings,
            int calories) {
        this(servingSize, servings, calories, 0);
    }

    public NutritionFacts(int servingSize, int servings,
            int calories, int fat) {
        this(servingSize, servings, calories, fat, 0);
    }

    public NutritionFacts(int servingSize, int servings,
            int calories, int fat, int sodium) {
        this(servingSize, servings, calories, fat, sodium, 0);
    }

    public NutritionFacts(int servingSize, int servings,
           int calories, int fat, int sodium, int carbohydrate) {
        this.servingSize  = servingSize;
        this.servings     = servings;
        this.calories     = calories;
        this.fat          = fat;
        this.sodium       = sodium;
        this.carbohydrate = carbohydrate;
    }
}

构建器允许使代码更易读,更容易编写。它还允许您使您的类不可变。

例子来自文章:

public class NutritionFacts {
    private final int servingSize;
    private final int servings;
    private final int calories;
    private final int fat;
    private final int sodium;
    private final int carbohydrate;

    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 Builder calories(int val)
            { calories = val;      return this; }
        public Builder fat(int val)
            { fat = val;           return this; }
        public Builder carbohydrate(int val)
            { carbohydrate = val;  return this; }
        public Builder sodium(int val)
            { sodium = val;        return this; }

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

    private NutritionFacts(Builder builder) {
        servingSize  = builder.servingSize;
        servings     = builder.servings;
        calories     = builder.calories;
        fat          = builder.fat;
        sodium       = builder.sodium;
        carbohydrate = builder.carbohydrate;
    }
}

在您的示例中,我不确定为仅具有两个属性的类执行构建器非常有用。

我希望这会对你有所帮助。

答案 1 :(得分:0)

在给定示例中,恕我直言通过使用Builder模式没有获得任何价值。您可以在没有Builder的情况下创建User对象(因为所有的setter)。在这种特殊情况下,Builder为您提供的唯一内容是Fluent Interface

当存在创建有效对象的各种组合时,您应该使用Builder Pattern。多亏了这一点,您将不必实现许多构造函数或工厂方法。

创建有效对象时,构建器也很有用,需要很多参数。

Builder应该负责只构建一个有效的对象。在您的情况下,如果用户需要姓名,则Builder不应该允许创建没有设置这些属性的User实例。

答案 2 :(得分:0)

从小的不可变对象开始

如果您需要所有属性,那么您应该只使用构造函数。通过这样做,您可以创建漂亮的小型不可变对象。

当您有多个可选字段

时,构建器会很有用

如果有多个可选字段和不同的方法来创建对象,则需要多个构造函数。

public User (int requiredParameter) { ... }
public User (int reqiredParameter, int optionalParameter) { ... }
public User (int reqiredParameter, int optionalParameter, String optionalParameter2) { ... }
public User (int reqiredParameter, String optionalParameter2) { ... }

它会产生混乱的代码。很难确定应该使用哪个构造函数。 在这种情况下,您可以使用嵌套构建器以直观的方式创建对象。