一、定义 建造者模式:指将一个复杂对象的构造与它的表示分离,使同样的构建过程可以创建不同的表示,这样的设计模式称为建造者模式。
概念较为复杂,我们还是通过实例需求进行建造者模式的理解。
二、需求 这里顶一个电脑类,并且实例化电脑类的对象,并给出该对象的属性赋值。
三、代码实现 3.1 版本一 通过直接创建出电脑类,并通过客户端进行赋值,创建出该对象,如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 class Computer { private String cpu; private String gpu; private String memery; private String hd; } public class AppTest { public static void main (String[] args) { Computer c = new Computer(); c.setCpu("i7 7500u" ); c.setMemery("16g" ); c.setGpu("RTX2080t" ); c.setHd("1T机械" ); System.out.println(c); } }
即:服务端(代码提供者)提供了Computer
这个类,具体需要什么样的电脑交给了客户端自己去实现,根据自己的需求自己设定即可。
缺点:
客户端,在实例化好产品的对象以后,必须为该对象的每一个属性赋值,这样对于客户端来说,太过麻烦。
违反了迪米特法则(一个类应该对自己依赖的类知道的越少越好,而这里却要自己去进行对象的赋值);
这相当于在现实中,去电脑城买电脑,商家给零件扔给你,你自己回家组装。
3.2 版本二 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 class Computer { private String cpu; private String gpu; private String memery; private String hd; } class ComputerBuilder { private Computer computer = new Computer(); public Computer builder () { computer.setCpu("i7 7500u" ); computer.setMemery("32g" ); computer.setGpu("RTX2080t" ); computer.setHd("1T机械" ); return computer; } } public class AppTest { public static void main (String[] args) { ComputerBuilder cb = new ComputerBuilder(); Computer computer = cb.builder(); System.out.println(computer); } }
目前的优点:
客户端需要一个产品时,直接向建造者要即可,建造者封装了创建电脑的”复杂”过程。
目前的缺点:
封装得太死了,无论客户端需求什么样的电脑,都只能采用这一种的配置进行使用。
3.3 版本三 继而改进,针对不同需求,我们需要创建不同的创造者,来分别生产不同配置的产品。
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 class Computer { private String cpu; private String gpu; private String memery; private String hd; } class AdviceComputerBuilder { private Computer computer = new Computer(); public Computer builder () { computer.setCpu("i7 7500u" ); computer.setMemery("32g" ); computer.setGpu("RTX2080t" ); computer.setHd("1T机械" ); return computer; } } class MiddleComputerBuilder { private Computer computer = new Computer(); public Computer builder () { computer.setCpu("i7 7500u" ); computer.setMemery("8g" ); computer.setGpu("RTX1060t" ); computer.setHd("1T机械" ); return computer; } } class LowComputerBuilder { private Computer computer = new Computer(); public Computer builder () { computer.setCpu("i7 7500u" ); computer.setMemery("2g" ); computer.setGpu("gtx940m" ); computer.setHd("500g机械" ); return computer; } } public class AppTest { public static void main (String[] args) { AdviceComputerBuilder acb = new AdviceComputerBuilder(); MiddleComputerBuilder mid = new MiddleComputerBuilder(); LowComputerBuilder low = new LowComputerBuilder(); Computer c = acb.builder(); System.out.println(c); Computer c1 = mid.builder(); System.out.println(c1); Computer c2 = low.builder(); System.out.println(c2); } }
这样根据不同的需求,给出不同的建造者类。
优点:
可以根据客户端的不同需求,使用不同的建造者来生产产品
缺点:
多个不同的建造者中的代码,在重复!既然代码中出现了重复的代码,那么就能继续优化。
建造的过程不稳定,如果在某个建造者创建产品时,漏掉了一步,编译器也不会存在报错。(等于在组装电脑的时候少了某一个步骤,原因就是没有标准,这里标准就是要定义接口)
3.4 版本四 继而继续进行优化:
创建一个建造者接口,把制作产品的具体步骤,稳定下来!
我们让建造者类,去实现建造者接口,接口中的方法步骤,类就必须都要实现,少实现一个抽象方法就会报错。
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 class Computer { private String cpu; private String gpu; private String memery; private String hd; } interface ComputerBuilder { void setCpu () ; void setGpu () ; void setMemery () ; void setHd () ; Computer computer () ; } class LowComputerBuilder implements ComputerBuilder { private Computer computer = new Computer(); @Override public void setCpu () { computer.setCpu("i7 7500u" ); } @Override public void setGpu () { computer.setGpu("gtx940m" ); } @Override public void setMemery () { computer.setMemery("2g" ); } @Override public void setHd () { computer.setHd("500g机械" ); } @Override public Computer computer () { return computer; } } public class AppTest { public static void main (String[] args) { ComputerBuilder c = new LowComputerBuilder(); c.setCpu(); c.setGpu(); c.setHd(); c.setMemery(); Computer computer = c.computer(); System.out.println(computer); } }
目前这种方式,将电脑创建的过程稳定下来,因为我们让具体电脑的类去实现接口,而接口的功能是稳定的,要想使用这个类,必须要实现定义好的接口的全部方法。同时,如果客户端想要扩展电脑的种类,只需要实现ComputerBuilder接口即可,自己定义一个Builder。
优点:
建造者类中的建造过程是稳定的,不会漏掉某一步!当客户端需要扩展建造者时,也不会漏掉一步。
缺点:
代码仍然存在重复
现在又变成了客户端自己去配置电脑(在客户端处,又要进行一系列的set方法),又违反了迪米特法则。(相当于去配置电脑,虽然不需要亲自去组装–配置型号等问题,但是必须”指挥”装机者进行装机…)
3.5 最终版本 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 class Computer { private String cpu; private String gpu; private String memery; private String hd; } interface ComputerBuilder { void setCpu () ; void setGpu () ; void setMemery () ; void setHd () ; Computer computer () ; } class Director { public Computer build (ComputerBuilder cb) { cb.setCpu(); cb.setGpu(); cb.setMemery(); cb.setHd(); return cb.computer(); } } class LowComputerBuilder implements ComputerBuilder { private Computer computer = new Computer(); @Override public void setCpu () { computer.setCpu("i7 7500u" ); } @Override public void setGpu () { computer.setGpu("gtx940m" ); } @Override public void setMemery () { computer.setMemery("2g" ); } @Override public void setHd () { computer.setHd("500g机械" ); } @Override public Computer computer () { return computer; } } public class AppTest { public static void main (String[] args) { ComputerBuilder c = new LowComputerBuilder(); Director director = new Director(); Computer computer = director.build(c); System.out.println(computer); } }
优点:
创建对象的过程是稳定不变的(因为有ComputerBuilder接口来稳定过程)
创建对象的过程只写了一次,没有重复代码(指挥者完成)
当需要扩展指挥者的时候,不需要修改之前的代码,符合了开闭原则。
3.5 类图
Builder
完成我们具体的需求,即创建我们所需的对象,但最后由Director
指挥者设定具体的过程;它是通过Builder
建立对象,Director
来将指挥的过程从客户端中分离出来,交给服务端来实现。
四、总结 建造者与工厂模式的区别:
工厂模式只需要一个简单的new,new出产品即可;
建造者更注重,在new出产品之后的,为产品属性赋值的过程;
建造者模式创建复杂的对象,由各种复杂的部件组成,工厂模式创建出来的对象都一样;
从最后的代码改进中可以看出,builder
用来定义对象的属性(定义对象的模板),而Director
将对对象进行属性的初始化,给对象进行装配。所以,当需要进行扩展时,只需要实现一个builder
类,定义对象的属性模板,然后交给Director
去进行初始化,完成对象的封装。
Builder用来定义对象的模板,而Director用于将对象初始化,给对象进行装配。 最终达到了,对象的创建和对象的属性装配进行了分离。使得不稳定的创建过程,达到了稳定的效果。