<其它设计模式介绍及案例源码下载 >
简介:构造器模式(Builder Pattern)使用多个简单的对象一步一步构建成一个复杂的对象。一个 Builder 类会一步一步构造最终的对象。该 Builder 类无需通过其他对象完成构造,独立于其他对象的。
主要解决:主要解决在软件系统中,有时候面临着"一个复杂对象"的创建工作,其通常由各个部分的子对象用一定的算法构成;由于需求的变化,这个复杂对象的子对象的属性经常面临着剧烈的变化,但是将它们组合在一起的算法却相对稳定,而且组合体对象个属性是稳定不变的
优点: 1、建造者独立,易扩展。 2、便于控制细节风险。
缺点: 1、产品必须有共同点,范围有限制。 2、如内部变化复杂,会有很多的建造类。
使用场景: 1、需要生成的对象具有复杂的内部结构。 2、需要生成的对象内部属性本身相互依赖。
实例解析:
类中有些属性是必须的,有些则是可选的。你将要如何创建你的对象?所有的属性都声明为final,所以你必须在构造器中给它们全部赋值,但是你也想给这个类的客户端忽略可选属性的机会。
public class User { private final String firstName; //required private final String lastName; //required private final int age; //optional private final String phone; //optional private final String address; //optional ... }
方案一(通过使用不同个数参数的构造函数,如下):
public User(String firstName, String lastName) { this(firstName, lastName, 0,,"",""); } public User(String firstName, String lastName, int age) { this(firstName, lastName, age, "",""); } public User(String firstName, String lastName, int age, String phone) { this(firstName, lastName, age, phone, ""); } public User(String firstName, String lastName, int age, String phone, String address) { this.firstName = firstName; this.lastName = lastName; this.age = age; this.phone = phone; this.address = address; }
评价: 当你只有几个属性时还好,但是当属性个数太多时,代码就变的难以理解和维护。并且代码对客户端来说变的很难,如:应该调用哪个构造器?有两个参数的?有三个参数的?那些不用传确切值的参数的默认值是多少?几个类型相同的参数是很令人费解的。第一个String是电话还是地址?等等一系列问题
方案二(通过对可选参数设置set、get方法,如下):
public class User { private String firstName; // required private String lastName; // required private int age; // optional private String phone; // optional public User() { this.firstName=firstName; this.lastName=lastName; } public String getFirstName() { return firstName; } public String getLastName() { return lastName; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getPhone() { return phone; } public void setPhone(String phone) { this.phone = phone; } }
评价:这种方法有两个主要弊端。第一个是类 实例的不一致状态。如果你要用User的多个属性来创建一个User对象,那么在所有的setX方法调用前,对象处于不完全状态。这就意味着客户端的其他 部分可能看到对象,并且假设它已经完成构造了,实际它并没有。方法的第二个缺点是对象的各个属性可以通过set方法一直处于可变状态,丧失了对象稳定不变的优势。
方案三(构造器模式);
public class Users { private final String name; private final String gender; private final int age; private final String email; private Users(UsersBuilder ub) { super(); this.name = ub.name; this.gender = ub.gender; this.age = ub.age; this.email = ub.email; } public String getName() { return name; } public String getGender() { return gender; } public int getAge() { return age; } public String getEmail() { return email; } @Override public String toString() { return "Users [name=" + name + ", gender=" + gender + ", age=" + age + ", email=" + email + "]"; } public static class UsersBuilder { final String name; final String gender; String email; int age; public UsersBuilder(String name, String gender) { super(); this.name = name; this.gender = gender; } public UsersBuilder setAge(int age) { this.age = age; return this; } public UsersBuilder setEmail(String email) { this.email = email; return this; } public String getEmail() { return email; } public int getAge() { return age; } public String getName() { return name; } public String getGender() { return gender; } public Users build() throws Exception { Users user=new Users(this); if(user.getAge()>200) { throw new Exception("参数格式不正确"); } return user; } } } public class TestClass { public static void main(String[] args) { UsersBuilder ub=new UsersBuilder("张三","男"); Users user=null; try { user=ub.setAge(13).build(); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println(user.getEmail()); } }
评价:这种方法能确保,无论什么时候你拿到这个类的一个对象,它的状态都是完整的。重点是,类似于构造器,建造者可以强制其参数的不变性。建造方法可以检查这些不变性, 如果它们无效就抛出Exception异常。关键是可以在从建造者中拷贝参数到对象时检查,并且是在对象字段上检查而不是在构造 器字段。这样做的理由是,既然建造者不是线程安全的,如果我们在实际创建对象前检查参数,参数值可能会在检查和拷贝之间被另一个线程改变。这个阶段的时间 被认为是“易损窗口”。
注意:
- User的构造器是私有的,这就意味着客户端不能直接创建实例。
- 这个类是不可变的。所有属性都是final类型并且他们由构造器设置值。此外,我们只提供getter操作。
- 建造者的构造器只接受两个必须的参数,并且这两个属性是仅有的被设置为final类型的,这样就能保证这些属性在构造器中是被赋值的。