设计模式之建造者模式
lijulin
8年前
<p>建造者模式(Builder Pattern)也叫做生成器模式,今天让我们一起学习一下建造者模式。</p> <h3>一、基本介绍</h3> <p>建造者模式的定义为: 将一个复杂对象的构建和它的表示分离开,使得同样的构建过程可以创建不同的表示。</p> <p>建造者模式主要由4个角色来组成:</p> <p>1 . 抽象建造者(Builder)角色:该角色用于规范产品的各个组成部分,并进行抽象,一般独立于应用程序的逻辑。</p> <p>2 . 具体建造者(Concrete Builder)角色:该角色实现抽象建造者中定义的所有方法,并且返回一个组建好的产品实例。</p> <p>3 . 产品(Product)角色:该角色是建造者中的复杂对象,一个系统中会有多于一个的产品类,这些产品类并不一定有共同的接口,完全可以是不相关联的。</p> <p>4 . 导演者(Director)角色:该角色负责安排已有模块的顺序,然后告诉Builder开始建造。</p> <h3>二、代码实现建造者模式</h3> <p>上面说的东西都只是理论,多少有些空洞,现在我们就通过一个简单的例子来体验一下建造者模式。</p> <p>1 . 创建产品类 Product.java 类</p> <pre> <code class="language-java">/** * 产品类 * */ public class Product { //业务处理方法 }</code></pre> <p>由于我们的目的是最终生产产品,所以产品类中的逻辑实现我们暂且不关注,具体的方法要根据业务来写。</p> <p>2 . 创建抽象建造者类 Builder.java 类</p> <pre> <code class="language-java">/** * 抽象建造者类 * */ public abstract class Builder { //设置产品的不同部分,以获得不同的产品 public abstract void setPart1(); public abstract void setPart2(); public abstract void setPart3(); //建造产品 public abstract Product builderProduct(); }</code></pre> <p>该类是一个抽象类,其中我们声明了4个抽象方法,前面三个是负责给产品添加不同的部件,第四个方法是负责建造产品。但这只是一个框架,还没有具体的实现。</p> <p>3 . 创建具体建造者类 ConcreteBuilder.java 类</p> <pre> <code class="language-java">/** *具体建造者类 * */ public class ConcreteBuilder extends Builder{ //一个产品 private Product product = new Product(); //开始安装产品的部件 @Override public void setPart1() { //为product安装部件1 } @Override public void setPart2() { //为product安装部件2 } @Override public void setPart3() { //为product安装部件3 } //建造一个产品 @Override public Product builderProduct() { // TODO Auto-generated method stub return product; } }</code></pre> <p>该类会继承自抽象建造者类 Builder ,并实现其中的方法。开始先声明一个产品,然后在各个 setPart3 方法中添加具体的逻辑,然后在 builderProduct() 方法中返回生产好的产品。</p> <p>4 . 创建导演类 Director() 类</p> <p>上面我们已经实现了具体的建造者类,也具体写好了安装每个部件的方法,最后一步就是由导演类来知道具体构建者类如何制造产品啦。制造完的产品会交给导演类负责处理。</p> <pre> <code class="language-java">/** * 导演类 * */ public class Director1 { private Builder builder = new ConcreteBuilder(); //构建产品,调用各个添加部件的方法 public Product build(){ builder.setPart1(); builder.setPart2(); builder.setPart3(); //调用构建产品的方法来构建产品 return builder.builderProduct(); } }</code></pre> <p>这个类很简单,其实就是获得一个具体的建造者对象,然后调用具体的方法给产品安装部件,安装完成后调用 builder.builderProduct() 方法获得建造好的产品,此处导演类是在 build() 方法中完成了建造过程,同时将获得的建造好的产品返回出去,以供其他模块使用该产品。</p> <p>此处的导演类起到了封装左右,可以避免高层模块深入到建造者内部的实现类,而且导演类可以有多个,根据业务逻辑分别用来建造不同的产品并输出。</p> <h3>三、建造者模式的优点</h3> <p>建造者模式的有点主要有以下几点:</p> <p>1 . 封装性 。使用建造者模式可以使客户端不必知道产品的内部实现细节</p> <p>2 . 独立易扩展 。由于建造过程是独立的,更利于后期扩展</p> <p>3 . 便于控制细节风险 。由于具体的产品建造是独立的,因此可以对建造过程逐步细化,而不对其他的模块产生任何影响</p> <h3>四、建造者模式的使用场景</h3> <p>说了这么多建造者模式的好处,那我们应该在什么场合使用它们呢,看下面:</p> <p>1 . 相同的方法,不同的执行顺序,产生不同的结果。这种情况我们只要利用不同的导演类来控制建造过程,就可以产生不同的产品,其他部分不用修改</p> <p>2 . 多个零件和部件,都可以装配到一个对象中,装配不同的零件,产生不同的运行结果,我们同样可以通过修改导演类来产生不同的产品</p> <p>3 . 产品类非常复杂,此时我们也可以将产品的建造方法和具体的建造顺序分离开来处理</p> <p>4 . 在对象创建过程中会用到系统的一些其他对象,这些对象在产品对象的创建过程中不容易得到,可以采用建造者模式封装该对象的创建过程</p> <p>注:建造者模式关注的是零件的类型和装配工艺的顺序</p> <h3>五、建造者模式实站</h3> <p>说了半天建造产品,没行动有卵用,来来来,咋们就用刚学的 建造者模式生产两台不同类型的电脑 练练手,代码敲起来</p> <p>1 . 创建产品父类 Computer 类</p> <p>该类是我们建造的计算机的父类,其中包含了计算机的公共属性以及属性的get和set方法</p> <pre> <code class="language-java">package cn.codekong.start; /** * 计算机类 */ public class Computer { //型号 private String type; //CPU private String cpu; //内存 private String ram; //硬盘 private String hardDisk; //显示器 private String monitor; //操作系统 private String os; //对应的get和set方法 public String getType() { return type; } public void setType(String type) { this.type = type; } public String getCpu() { return cpu; } public void setCpu(String cpu) { this.cpu = cpu; } public String getRam() { return ram; } public void setRam(String ram) { this.ram = ram; } public String getHardDisk() { return hardDisk; } public void setHardDisk(String hardDisk) { this.hardDisk = hardDisk; } public String getMonitor() { return monitor; } public void setMonitor(String monitor) { this.monitor = monitor; } public String getOs() { return os; } public void setOs(String os) { this.os = os; } }</code></pre> <p>2 . 创建具体的产品类 T410 类和 X201 类</p> <p>这两个类均继承自上面的 Computer 类,并且我们在该类在添加了两台计算机特有的属性, T410 计算机用于独立显卡,而 X201 没有,同时重写了它们的 toString() 方法,返回它们的参数,便于最后我们建造完计算机后输出它们各自的配置参数.</p> <pre> <code class="language-java">T410.java</code></pre> <pre> <code class="language-java">package cn.codekong.start; public class T410 extends Computer{ //显卡 private String graphicCard; public T410() { this.setType("Thinkpad T410"); } public String getGraphicCard(){ return graphicCard; } public void setGraphicCard(String graphicCard){ this.graphicCard = graphicCard; } @Override public String toString() { return "型号:\t" + this.getType() + "\nCPU\t" + this.getCpu() + "\n内存\t" + this.getRam() + "\n硬盘\t" + this.getHardDisk() + "\n显卡\t" + this.getGraphicCard() + "\n显示器\t" + this.getMonitor() + "\n操作系统\t" + this.getOs(); } }</code></pre> <pre> <code class="language-java">X201.java</code></pre> <pre> <code class="language-java">package cn.codekong.start; public class X201 extends Computer{ public X201() { this.setType("Thinkpad X201"); } @Override public String toString() { return "型号:\t" + this.getType() + "\nCPU\t" + this.getCpu() + "\n内存\t" + this.getRam() + "\n硬盘\t" + this.getHardDisk() + "\n显示器\t" + this.getMonitor() + "\n操作系统\t" + this.getOs(); } }</code></pre> <p>上面的(1)(2)步只是相当于我们有了我们需要的产品类已经声明好了,下面开始写我们的抽象建造类</p> <p>3 . 抽象计算机建造类 ComputerBuilder 类</p> <p>我们创建了一个接口,在其中声明了我们要建造产品过程中需要用到的方法,其实就是产品的各个建造步骤</p> <pre> <code class="language-java">package cn.codekong.start; /** * 抽象的计算机建造者 * 声明建造的公共方法 */ public interface ComputerBuilder { //建造CPU void buildCpu(); //建造内存 void buildRam(); //建造硬盘 void buildHardDisk(); //建造显卡 void buildGraphicCard(); //建造显示器 void buildMonitor(); //建造操作系统 void buildOs(); //得到建造好的计算机 Computer getResult(); }</code></pre> <p>4 . 创建具体的建造类 T410Builder 类和 X201Builder 类</p> <p>这两个类要实现上一步定义的接口,然后实现里面的各个方法,其实就是实现各个具体的组装方法中的逻辑,但此时只是把每一个组装的步骤的逻辑具体化了,还没有开始正式组装。</p> <pre> <code class="language-java">T410Builder.java</code></pre> <pre> <code class="language-java">package cn.codekong.start; /** * T410的具体建造者实现抽象的计算机建造者 */ public class T410Builder implements ComputerBuilder{ private T410 computer = new T410(); @Override public void buildCpu() { computer.setCpu("i5-450"); } @Override public void buildRam() { computer.setRam("4G 1333MHz"); } @Override public void buildHardDisk() { computer.setHardDisk("500G 7200转"); } @Override public void buildGraphicCard() { computer.setGraphicCard("Nvidia"); } @Override public void buildMonitor() { computer.setMonitor("14英寸 1280*800"); } @Override public void buildOs() { computer.setOs("Windows7 旗舰版"); } @Override public Computer getResult() { return computer; } }</code></pre> <pre> <code class="language-java">X201Builder.java</code></pre> <pre> <code class="language-java">package cn.codekong.start; /** * X201计算机的具体建造类实现抽象的计算机建造类 */ public class X201Builder implements ComputerBuilder{ private X201 computer = new X201(); @Override public void buildCpu() { computer.setCpu("i3-350"); } @Override public void buildRam() { computer.setRam("2G 1333M"); } @Override public void buildHardDisk() { computer.setHardDisk("250G 5400 转"); } @Override public void buildGraphicCard() { //无独立显卡 } @Override public void buildMonitor() { computer.setMonitor("12英寸 1280*800"); } @Override public void buildOs() { computer.setOs("Windows7 Home版"); } @Override public Computer getResult() { return computer; } }</code></pre> <p>5 . 导演类知道具体的建造者类建造产品 ComputerDirector 类</p> <p>该类就比较简单了,在该类内部实现两个构造方法,分别对应实现两种计算机的过程,这时候才是正式的建造过程。</p> <pre> <code class="language-java">package cn.codekong.start; /** * 计算机导演类,知道具体建造者建造计算机 */ public class ComputerDirector { ComputerBuilder builder; //建造T410计算机 public T410 constructT410(){ builder = new T410Builder(); builder.buildCpu(); builder.buildRam(); builder.buildHardDisk(); builder.buildGraphicCard(); builder.buildMonitor(); builder.buildOs(); //建造结束将产品返回供外部使用 return (T410)builder.getResult(); } //建造X201计算机 public X201 constructX201(){ builder = new X201Builder(); builder.buildCpu(); builder.buildRam(); builder.buildHardDisk(); //由于X201没有独立显卡,则不调用buildGraphicCard()函数 //builder.buildGraphicCard(); builder.buildMonitor(); builder.buildOs(); //建造结束将产品返回供外部使用 return (X201)builder.getResult(); } }</code></pre> <p>6 . 最后让我们测试一下建造的产品是否是好的,新建测试类 ComputerTest 类</p> <pre> <code class="language-java">package cn.codekong.start; /** * 计算机建造测试类 */ public class ComputerTest { public static void main(String[] args) { ComputerDirector computerDirector = new ComputerDirector(); //建造T410计算机 Computer t410 = computerDirector.constructT410(); //输出T410计算机的配置参数 System.out.println(t410); System.out.println("------------我是分割线----------------"); //建造X201计算机 Computer x201 = computerDirector.constructX201(); //输出X201的计算机配置 System.out.println(x201); } }</code></pre> <p>输出结果如下</p> <pre> <code class="language-java">型号: Thinkpad T410 CPU i5-450 内存 4G 1333MHz 硬盘 500G 7200转 显卡 Nvidia 显示器 14英寸 1280*800 操作系统 Windows7 旗舰版 ------------我是分割线---------------- 型号: Thinkpad X201 CPU i3-350 内存 2G 1333M 硬盘 250G 5400 转 显示器 12英寸 1280*800 操作系统 Windows7 Home版</code></pre> <p>好了,经过上面的步骤,我们的产品就建造好咯。</p> <p>这时候有人会说,你这个例子还是不接地气啊,我怎么没见过什么程序这么写呢,好,那咋就来一个接地气的例子,看下面的代码:</p> <pre> <code class="language-java">AlertDialog alertDialog = new AlertDialog.Builder(this) .setTitle("我是标题") .setIcon(R.drawable.icon) .show();</code></pre> <p>上面是一个Android里面警告框的例子,我们可以通过链式调用的方法将标题和图标传入,然后调用show()方法就构建了一个警告框。这个例子是不是很常见,那我们就用一个类使用建造者模式实现一下吧:</p> <pre> <code class="language-java">package com.codekong.my; import javax.naming.Context; public class MyDialog { //警告框标题 private String title; //警告框图标资源Id private int iconId; //上下文环境 private Context context; public String getTitle() { return title; } public int getIconId() { return iconId; } public Context getContext() { return context; } public static class Builder{ //设置默认值 private String title = "Title"; private int iconId = 0; private Context context; public Builder(Context context){ this.context = context; } public Builder setTitle(String title) { this.title = title; return this; } public Builder setIconId(int iconId) { this.iconId = iconId; return this; } //应用我们的设置 private void applyConfig(MyDialog myDialog){ myDialog.title = title; myDialog.iconId = iconId; myDialog.context = context; } public MyDialog show(){ MyDialog myDialog = new MyDialog(); applyConfig(myDialog); return myDialog; } } }</code></pre> <p>上面的类主要涉及到以下几步:</p> <p>1 . 创建一个类,先声明他的成员变量以及成员变量的get方法(其实这一步就是建造者模式里面的 产品角色 ,get方法是为了我们使用时可以随时查看我们自定义的产品属性)</p> <p>2 . 定义一个静态内部类 Builder ,然后把我们产品定义的属性在静态内部类中复制一份,同时生成它的set方法(这一步呢其实就是我们的 抽象建造者角色 ,要注意的一点是为了实现链式调用,我们要让我们的set方法返回值为Builder, 同时在set方法中返回this,也就是返回本对象)</p> <p>3 . 接着定义 applyConfig() 方法,把我们通过set方法设置的值全部赋值给我们的外部类对应的成员变量(这一步就是我们的 具体的建造者 角色)</p> <p>4 . 最后对外提供一个 show() 方法,在其中先 new 出一个我们的MyDialog对象,然后把它传入调用applyConfig()方法,调用过后我们的myDialog对象就已经被设置属性了,我们此时就可以将这个设置过的对象传到外部供其他类使用(这一步就是我们的 导演角色 )</p> <p>当我们使用的时候就可以通过下面代码使用:</p> <pre> <code class="language-java">MyDialog myDialog = new MyDialog.Builder(this) .setTitle("我是标题") .setIconId(R.drawable.icon) .show();</code></pre> <p> </p> <p> </p> <p>来自:http://www.jianshu.com/p/4d074224117d</p> <p> </p>