Java建造者模式

来源:互联网 发布:淘宝网进入 编辑:程序博客网 时间:2024/06/07 21:04
Builder模式定义:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。

Builder模式是一步一步创建一个复杂的对象,它允许用户可以只通过指定复杂对象的类型和内容就可以构建它们。用户不知道内部的具体构建细节。

实用范围
1、当创建复杂对象的算法应该独立于该对象的组成部分以及它们的装配方式时。
2、当构造过程必须允许被构造的对象有不同表示时。

建造者模式的UML结构图:



角色
在这样的设计模式中,有以下几个角色:
1、Builder:为创建一个产品对象的各个部件指定抽象接口。
2、ConcreteBuilder:实现Builder的接口以构造和装配该产品的各个部件,定义并明确它所创建的表示,并提供一个检索产品的接口。
3、Director:构造一个使用Builder接口的对象,指导构建过程。
4、Product:表示被构造的复杂对象。ConcreteBuilder创建该产品的内部表示并定义它的装配过程,包含定义组成部件的类,包括将这些部件装配成最终产品的接口。

角色Builder:
[java] view plain copy
  1. public interface PersonBuilder {  
  2.      void buildHead();  
  3.      void buildBody();  
  4.      void buildFoot();  
  5.      Person buildPerson();  
  6. }  

角色ConcreteBuilder:
[java] view plain copy
  1. public class ManBuilder implements PersonBuilder {  
  2.      Person person;  
  3.      public ManBuilder() {  
  4.           person = new Man();  
  5.      }  
  6.      public void buildbody() {  
  7.           person.setBody("建造男人的身体");  
  8.      }  
  9.      public void buildFoot() {  
  10.           person.setFoot("建造男人的脚");  
  11.      }  
  12.      public void buildHead() {  
  13.           person.setHead("建造男人的头");  
  14.      }  
  15.      public Person buildPerson() {  
  16.           return person;  
  17.      }  
  18. }  

角色ConcreteBuilder:
[java] view plain copy
  1. public class WomanBuilder implements PersonBuilder {  
  2.      Person person;  
  3.      public WomanBuilder() {  
  4.           person = new Woman();  
  5.      }  
  6.      public void buildbody() {  
  7.           person.setBody(“建造女人的身体");  
  8.      }  
  9.      public void buildFoot() {  
  10.           person.setFoot(“建造女人的脚");  
  11.      }  
  12.      public void buildHead() {  
  13.           person.setHead(“建造女人的头");  
  14.      }  
  15.      public Person buildPerson() {  
  16.           return person;  
  17.      }  
  18. }  

角色Director:
[java] view plain copy
  1. public class PersonDirector {  
  2.      public Person constructPerson(PersonBuilder pb) {  
  3.           pb.buildHead();  
  4.           pb.buildBody();  
  5.           pb.buildFoot();  
  6.           return pb.buildPerson();  
  7.      }  
  8. }  

角色Product:
[java] view plain copy
  1. public class Person {  
  2.      private String head;  
  3.      private String body;  
  4.      private String foot;  
  5.   
  6.      public String getHead() {  
  7.           return head;  
  8.      }  
  9.      public void setHead(String head) {  
  10.           this.head = head;  
  11.      }  
  12.      public String getBody() {  
  13.           return body;  
  14.      }  
  15.      public void setBody(String body) {  
  16.           this.body = body;  
  17.      }  
  18.      public String getFoot() {  
  19.           return foot;  
  20.      }  
  21.      public void setFoot(String foot) {  
  22.           this.foot = foot;  
  23.      }  
  24. }  
  25. public class Man extends Person {  
  26.      public Man(){  
  27.           System.out.println(“开始建造男人");  
  28.      }  
  29. }  
  30. public class Woman extends Person {  
  31.      public Woman(){  
  32.           System.out.println(“开始建造女人");  
  33.      }  
  34. }  

测试:
[java] view plain copy
  1. public class Test{  
  2.      public static void main(String[] args) {  
  3.           PersonDirector pd = new PersonDirector();  
  4.           Person womanPerson = pd.constructPerson(new ManBuilder());  
  5.           Person manPerson = pd.constructPerson(new WomanBuilder());  
  6.      }  
  7. }  

建造者模式在使用过程中可以演化出多种形式:
如果具体的被建造对象只有一个的话,可以省略抽象的Builder和Director,让ConcreteBuilder自己扮演指导者和建造者双重角色,甚至ConcreteBuilder也可以放到Product里面实现。
在《Effective Java》书中第二条,就提到“遇到多个构造器参数时要考虑用构建器”,其实这里的构建器就属于建造者模式,只是里面把四个角色都放到具体产品里面了。

上面例子如果只制造男人,演化后如下:
[java] view plain copy
  1. public class Man {  
  2.      private String head;  
  3.      private String body;  
  4.      private String foot;  
  5.   
  6.      public String getHead() {  
  7.           return head;  
  8.      }  
  9.      public void setHead(String head) {  
  10.           this.head = head;  
  11.      }  
  12.      public String getBody() {  
  13.           return body;  
  14.      }  
  15.      public void setBody(String body) {  
  16.           this.body = body;  
  17.      }  
  18.      public String getFoot() {  
  19.           return foot;  
  20.      }  
  21.      public void setFoot(String foot) {  
  22.           this.foot = foot;  
  23.      }  
  24. }  

[java] view plain copy
  1. public class ManBuilder{  
  2.      Man man;  
  3.      public ManBuilder() {  
  4.           man = new Man();  
  5.      }  
  6.      public void buildbody() {  
  7.           man.setBody("建造男人的身体");  
  8.      }  
  9.      public void buildFoot() {  
  10.           man.setFoot("建造男人的脚");  
  11.      }  
  12.      public void buildHead() {  
  13.           man.setHead("建造男人的头");  
  14.      }  
  15.      public Man builderMan() {  
  16.           buildHead();  
  17.           buildBody();  
  18.           buildFoot();  
  19.           return man;  
  20.      }  
  21. }  

测试:
[java] view plain copy
  1. public class Test{  
  2.      public static void main(String[] args) {  
  3.           ManBuilder builder = new ManBuilder();  
  4.           Man man = builder.builderMan();  
  5.      }  
  6. }  
另一种用法:

在这里举个例子:假如辅导员需要你统计班上同学的信息,第一次的时候只让你统计姓名、年龄、性别一般情况下我们会采用构造函数的方式去实现,当你刚暗暗窃喜的时候,这时候辅导员又说在把居住地址和QQ号加上,方便组建班级群。然后你就要去增加一个构造函数,再把这些值传进去。后来辅导员新需求又来了,告诉你还需要添加家长的联系方式,这时候难道又要去修改构造函数?这时候建造着模式就可以闪亮登场了!

public class Person {    private int age;    private int QQ;    private int phone;    private String name;    private String sex;    private String address;    private String email;    public static class Builder {        private Person p;        public Builder() {            p = new Person();        }        public Builder setName(String name) {            p.name = name;            return this;        }        public Builder setSex(String sex) {            p.sex = sex;            return this;        }        public Builder setAddress(String address) {            p.address = address;            return this;        }        public Builder setAge(int age) {            p.age = age;            return this;        }        public Builder setEmail(String email) {            p.email = email;            return this;        }        public Builder setQQ(int QQ) {            p.QQ = QQ;            return this;        }        public Builder setPhone(int phone) {            p.phone = phone;            return this;        }        public Person create() {            return p;        }    }    @Override    public String toString() {        StringBuilder sb = new StringBuilder();        if (name != null) {            sb.append("name:" + name + ",");        }        if (age != 0) {            sb.append("age:" + age + ",");        }        if (sex != null) {            sb.append("sex:" + sex + ",");        }        if (phone != 0) {            sb.append("phone:" + phone + ",");        }        if (QQ != 0) {            sb.append("QQ:" + QQ + ",");        }        if (email != null) {            sb.append("email:" + email + ",");        }        if (address != null) {            sb.append("address:" + address);        }        return sb.toString();    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86

具体的使用

Person p1 = new Person.Builder()                .setName("小明")                .setAge(18)                .setSex("男")                .create();        System.out.println(p1.toString());        Person p2 = new Person.Builder()                .setName("小明")                .setAge(18)                .setSex("男")                .setQQ(999999)                .setAddress("深圳市龙岗区坂田")                .create();        System.out.println(p2.toString());        Person p3 = new Person.Builder()                .setName("小明")                .setAge(18)                .setSex("男")                .setQQ(666888)                .setPhone(1383838438)                .setAddress("深圳市龙岗区坂田")                .create();        System.out.println(p3.toString());
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

程序运行效果

这里写图片描述

建造者模式的实际用法

建造者模式在很多库中都有广泛的应用。然而有很多人都犯了同样的错误:看看下面来自Java标准类库中的StringBuilder,它是建造者模式吗?

1
2
3
4
5
StringBuilder strBuilder= newStringBuilder();
strBuilder.append("one");
strBuilder.append("two");
strBuilder.append("three");
String str= strBuilder.toString();

在Java标准类库中,StringBuilder继承了AbstractStringBuilder。

append()方法是其中一个步骤,就像Starbucks中的一个步骤一样。toString()方法是另一个步骤,而且是最后一个。但是这里缺少上图中waiter。Waiter类在建造者模式扮演指挥的角色。正是因为这里缺少这样的一个角色,所以StringBuilder并不是标准的建造者模式。

MyBatis中的SqlSessionFactoryBuilder就是建造者模式

SqlSessionFactoryBuilder类负责构建SqlSessionFactory,并且提供了多个build的重载方法。但其实很多都是在调同一签名的方法,例如:

 public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) ,只是由于方法参数environment和 propertiese都可以为null,

所以为了提供调用的便利性,才提供了下面的三个方法:

  

public SqlSessionFactory build(InputStream inputStream)public SqlSessionFactory build(InputStream inputStream, String environment) public SqlSessionFactory build(InputStream inputStream, Properties properties)

按照上述思路去除重复的,真正的重载方法只有如下三种:

public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) public SqlSessionFactory build(Reader reader, String environment, Properties properties)public SqlSessionFactory build(Configuration config)

可以看出,配置信息可以以三种形式提供给SqlSessionFactorybuild方法,分别是InputStream(字节流)、Reader(字符流)、Configuration(类),由于字节流与字符流都属于读取配置文件的方式,所以从配置信息的来源就很容易想到构建一个SqlSessionFactory有两种方式,大致代码如下:

 

(1) 读取xml文件构造方式

 

String resource = "org/mybatis/example/mybatis-config.xml";  InputStream inputStream = Resources.getResourceAsStream(resource);  SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream) ;

 

(2) 编程构造方式

DataSource dataSource = BlogDataSourceFactory.getBlogDataSource();TransactionFactory transactionFactory = new JdbcTransactionFactory();Environment environment = new Environment("development", transactionFactory, dataSource);Configuration configuration = new Configuration(environment);configuration.addMapper(BlogMapper.class);SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(configuration) ;

 

 

 

下面先来分析XML文件构造方式的build方法的源码:

 

public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {    try {      XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);      return build(parser.parse());    } catch (Exception e) {      throw ExceptionFactory.wrapException("Error building SqlSession.", e);    } finally {      ErrorContext.instance().reset();      try {        inputStream.close();      } catch (IOException e) {        // Intentionally ignore. Prefer previous error.      }    }  }

 

通过上面这几行代码,就能看出基于XML文件的这种构造方式,通过从XML中读取信息的工作之后,也是构造出Configuration对象之后再继续进行SqlSessionFactory的构建工作的,只是多了些XML的解析工作,所以我们只需单刀直入,直按分析编程构造方式的代码就可以了,或者是直接分析 build(parser.parse())这句代码(参数产生过程先跳过)

编程构造方式的build方法源码如下(基于xml的构造方式的build(parser.parse())最终也是调了这个代码): 

  

  public SqlSessionFactory build(Configuration config) {                return new DefaultSqlSessionFactory(config);    }

 

其实这么看来SqlSessionFactory在mybatis的默认实现类为org.apache.ibatis.session.defaults.DefaultSqlSessionFactory , 其构造过程主要是注入了Configuration的实例对象,Configuration的实例对象即可通过解析xml配置文件产生,也可能通过代码直接构造。以上代码使用了一个设计模式:建设者模式(Builder)SqlSessionFactoryBuilder扮演具体的建造者,Configuration类则负责建造的细节工作



参考:
http://blog.csdn.net/a_zhon/article/details/52557472
http://blog.csdn.net/jason0539/article/details/44992733


原创粉丝点击