zoukankan      html  css  js  c++  java
  • 浅谈设计模式--建造器模式(Builder Pattern)

    建造器模式,是于创建带有大量参数的对象,并避免因参数数量多而产生的一些问题(如状态不一致-JavaBean的setter模式)。

    如果参数多且有些是必须初始化的,有些是不一定需要初始化的时候,创建对象是非常麻烦的,因为不得不为每种情况都添加一个构造方法。建造器模式,就是为了解决这个问题的。

    使用Builder模式并不难:

    1.创造一个静态内部建造类(Builder Class. e.g. UserBuilder)

    2.类的构造方法必须设置为private,防止类被正常构造

    3.建造类提供public方法,来设置可选的参数,并返回Builder对象

    4.最后建造类提供build()方法,真正创建原来的类的对象

    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
    
        private User(UserBuilder builder) {
        this.firstName = builder.firstName;
        this.lastName = builder.lastName;
        this.age = builder.age;
        this.phone = builder.phone;
        this.address = builder.address;
        }
    
        // a list of getter method
    
        @Override
        public String toString() {
        return firstName + " " + lastName + "-" + age + " , " + phone + "/"
            + address;
        }
    
        // Builder Class
        public static class UserBuilder {
        private final String firstName;
        private final String lastName;
        private int age;
        private String phone;
        private String address;
    
        public UserBuilder(String firstName, String lastName) {
            this.firstName = firstName;
            this.lastName = lastName;
        }
    
        public UserBuilder age(int age) {
            this.age = age;
            return this;
        }
    
        public UserBuilder phone(String phone) {
            this.phone = phone;
            return this;
        }
    
        public UserBuilder address(String address) {
            this.address = address;
            return this;
        }
    
        public User build() {
            return new User(this);
        }
        }
    
        
        public static void main(String[] args) {
        // TODO Auto-generated method stub
        System.out.println(new User.UserBuilder("Jhon", "Doe").age(30)
            .phone("1234567").address("Fake address 1234").build()
            .toString());
        }
    }

    另外,在build()方法里,也可以检验参数的正确性,例如:

    public User build() {
        User user = new user(this);
        if (user.getAge()<120) {
            throw new IllegalStateException(“Age out of range”); // thread-safe
        }
        return user;
    }

    这是线程安全的做法,因为user已经是不可变对象。下面的非线程安全做法,应该避免:

    public User build() {
        if (age 120) {
            throw new IllegalStateException(“Age out of range”); // bad, not thread-safe
        }
        // This is the window of opportunity for a second thread to modify the value of age
        return new User(this);
    }

    但是,这种建造器的使用方法,其实是有隐患的:

    1. 它没有指引用户,一步步的进行构建对象;用户并不知道何时何地用何方法

    2. 状态不一致的风险仍然存在

    如果需要构建顺序的话,可以做如下修改,来使建造器模式更加人性化:

    public class NewUser {
    
        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
    
        private NewUser(UserBuilder builder) {
        this.firstName = builder.firstName;
        this.lastName = builder.lastName;
        this.age = builder.age;
        this.phone = builder.phone;
        this.address = builder.address;
        }
    
        // a list of getter method
    
        @Override
        public String toString() {
        return firstName + " " + lastName + "-" + age + " , " + phone + "/"
            + address;
        }
    
        public static interface FirstNameStep {
        LastNameStep firstName(String name);
        }
    
        public static interface LastNameStep {
        AgeStep lastName(String lastName);
        }
    
        public static interface AgeStep {
        PhoneStep age(int age);
        }
    
        public static interface PhoneStep {
        AddressStep phone(String phone);
        }
    
        public static interface AddressStep {
        BuildStep address(String address);
        }
    
        public static interface BuildStep {
        NewUser build();
        }
    
        // Builder Class
        public static class UserBuilder implements FirstNameStep, LastNameStep,
            AgeStep, PhoneStep, AddressStep, BuildStep {
        private String firstName;
        private String lastName;
        private int age;
        private String phone;
        private String address;
    
        private UserBuilder() {
        }
    
        public static FirstNameStep newBuilder() {
            return new UserBuilder();
        }
    
        public LastNameStep firstName(String firstName) {
            this.firstName = firstName;
            return this;
        }
    
        public AgeStep lastName(String lastName) {
            this.lastName = lastName;
            return this;
        }
    
        public PhoneStep age(int age) {
            this.age = age;
            return this;
        }
    
        public AddressStep phone(String phone) {
            this.phone = phone;
            return this;
        }
    
        public UserBuilder address(String address) {
            this.address = address;
            return this;
        }
    
        public NewUser build() {
            return new NewUser(this);
        }
        }
    
        public static void main(String[] args) {
        NewUser user = NewUser.UserBuilder.newBuilder().firstName("ABC")
            .lastName("haha").age(10).phone("123").address("wa").build();
        System.out.println(user.toString());
    
        }
    
    }

    这个实现更加复杂,利用了接口的设计,使得建造器创建对象时,可以一步接着一步(firstName->lastName->age->phone->address),相当友好。缺点是,实现有点复杂,代码量比较大。

    参考:

    http://www.javacodegeeks.com/2013/01/the-builder-pattern-in-practice.html

    http://rdafbn.blogspot.ie/2012/07/step-builder-pattern_28.html

  • 相关阅读:
    用sed删除空行
    烂泥:php5.6源码安装及php-fpm配置
    linux系统vsftpd登陆慢卡怎么办
    Linux Vsftpd 连接超时解决方法
    linux中shell截取字符串方法总结
    运算符
    数据类型
    is null 和=null的区别
    DML
    DDL
  • 原文地址:https://www.cnblogs.com/techyc/p/3538359.html
Copyright © 2011-2022 走看看