今天周末,有小雨,正好也不用出门了,那就在家学习吧,通过了两周的面试,拿到了几个offer,可是都不是本身很想去的那种,要么就是几我的的初创小公司,要么就是开发企业内部系统的这种传统开发,感受这种传统开发已经不能给本身带来多大的提高了,由于工做了这几年这种系统经历了很多了,成天的就是增删改查。创业小公司已经不想再去了,工做了这几年去的都是这种小公司,风险大,压力大,节奏快,没时间沉淀学习。上上家东家还欠我几个月工资呢,就是由于创业公司资金链断了,而后老板忽悠领导,领导再忽悠咱们,后来实在发不出工资了,忽悠不住了,就大批大批的走人了。前端
因此如今非常纠结,大点公司又去不了小的公司还看不上,目前就是这么个高不成低不就的状态,因此仍是抓紧时间学习,充实本身吧,哪怕如今进不去稍微大点的公司,那通过努力的学习后说不定仍是有机会的,可是不努力是一点机会都没有的。面试
好了,言归正传,此次要介绍的是建立型设计模式的最后一个,建造者模式,这个模式其实我在平时开发中用的不少,只不过是用了这个模式的更深一种形式吧。后面我会介绍到这一部份内容的。数据库
建造者模式可以将一个复杂的构建与其表示相分离,使得一样的构建过程能够建立不一样的表示。这句话理解起来可能有点抽象,简单来讲就是调用相同的建立对象的方法(建造过程)能够建立出不一样的对象。 后端
仍是举例来讲明吧,若是说我要建立一部手机,我须要先制造手机的几个核心部件,例如:屏幕、电池、听筒、话筒、机身等。设计模式
public class MobilePhone { //手机屏幕 private String screen; //电池 private String battery; //话筒 private String microphone; //听筒 private String phoneReceiver; //机身 private String phoneBody; public String getScreen() { return screen; } public void setScreen(String screen) { this.screen = screen; } public String getBattery() { return battery; } public void setBattery(String battery) { this.battery = battery; } public String getMicrophone() { return microphone; } public void setMicrophone(String microphone) { this.microphone = microphone; } public String getPhoneReceiver() { return phoneReceiver; } public void setPhoneReceiver(String phoneReceiver) { this.phoneReceiver = phoneReceiver; } public String getPhoneBody() { return phoneBody; } public void setPhoneBody(String phoneBody) { this.phoneBody = phoneBody; } }
每一部手机都是这个类的对象,在建立一部手机的时候都要保证这几个核心部件的建立。因此建立手机是须要一个标准规范的,由于这几个核心部件均可以是不一样的型号,不一样的型号的部件制造出来的手机也是不一样的,这样就有了下面建造规范的接口。框架
public interface IBuildPhone { /** * 建造手机屏幕 */ void buildScreen(); /** * 建造手机电池 */ void buildBattery(); /** * 建造手机听筒 */ void buildMicrophone(); /** * 建造手机话筒 */ void buildPhoneReceiver(); /** * 建造手机机身 */ void buildPhoneBody(); }
有了规范了,就能够建立手机了,先建立一个iphoneX。iphone
public class IPhoneX implements IBuildPhone { private MobilePhone mobilePhone; public IPhoneX(){ mobilePhone = new MobilePhone(); } /** * 建造手机屏幕 */ @Override public void buildScreen() { mobilePhone.setScreen("OLED显示屏"); } /** * 建造手机电池 */ @Override public void buildBattery() { mobilePhone.setBattery("2700mAh电池容量"); } /** * 建造手机听筒 */ @Override public void buildMicrophone() { mobilePhone.setMicrophone("听筒"); } /** * 建造手机话筒 */ @Override public void buildPhoneReceiver() { mobilePhone.setPhoneReceiver("话筒"); } /** * 建造手机机身 */ @Override public void buildPhoneBody() { mobilePhone.setPhoneBody("iphoneX机身"); } /** * 建立手机 * @return */ public MobilePhone build(){ return mobilePhone; } }
建立手机的工具写好了,下面就可使用了。ide
public class Director { /** * 建造一部手机 * @param buildPhone * @return */ public MobilePhone createMobilePhone(IBuildPhone buildPhone){ buildPhone.buildBattery(); buildPhone.buildMicrophone(); buildPhone.buildScreen(); buildPhone.buildPhoneReceiver(); buildPhone.buildPhoneBody(); return buildPhone.createMobilePhone(); } @Test public void thatTest(){ System.out.println(JSON.toJSONString(createMobilePhone(new IPhoneX()))); } }
关键的方法在createMobilePhone()方法,这个方法接收一个IBuildPhone接口的对象,因此只要符合这个建立手机规范的对象均可以建立一部手机。createMobilePhone()方法能够接收new IPhoneX()这样一个对象,也能够接收new IPhone8()、new FindX()等等。函数
具体使用方法在thatTest()方法中。这个方法的运行结果是:工具
{"battery":"2700mAh电池容量","microphone":"听筒","phoneBody":"iphoneX机身","phoneReceiver":"话筒","screen":"OLED显示屏"}
上面这个例子的实现过程就使用了咱们今天要说的建造者模式,咱们来分析一下建造者模式的结构。
以下图:
在建造者模式的结构图中包含以下4个角色。
Builder(抽象建造者):它(IBuildPhone)为建立一个产品的各个部件指定了标准,规定了要建立复杂对象须要建立哪些部分,并不直接建立对象的具体部分。
ConcreteBuilder(具体建造者):它实现了Builder接口(IPhoneX),实现各个部分的具体构造和装配方法,定义并明确它所建立的复杂对象,也能够提供一个方法返回建立好的复杂产品对象。
Product(产品角色):它(MobilePhone)是被建造的复杂对象,包含多个组成部分,具体建造者建立该产品的内部表示并定义它的装配过程。
Director(指挥者):指挥者(Director),它复杂安排复杂对象的建造次序,指挥者与抽象建造者之间存在关联关系,能够在Director的方法中调用建造者对象的部件构造与装配方法,完成建造复杂对象的任务。客户端通常只需与Director进行交互。
好了,建造者模式到这里就算是介绍完了,而后说一说咱们平时在项目中是怎么使用建造者模式的。先说一下场景,咱们通常在开发的过程当中都是须要分层的,MVC这个不通常人都不陌生吧,Model-View-Controller。(我这里只是举例子不必定真的项目中就这样用)那咱们的数据在每一层的传输过程当中若是须要增长或删除些额外的功能怎么实现呢?
仍是举例子吧,以下面一个实体类:
public class Person { private Long id; private String name; private int age; private String address; public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } }
若是说这个类是一个orm框架须要的实体类,它最好的场景是只被后端的数据操做使用,可是controller中有一个add方法,这个方法是新增一我的员,add方法接收的参数是一我的员对象,可是这个对象和上面这个Person得属性有些差异,例如这个对象里面有请求ip,以及这个对象中没有id这个字段(id在数据库中自增,因此前端不容许传过来id )。这个时候就不能使用Person类的对象做为add的方法了,须要再建立一个类专门来给Controller使用。
以下代码:
/** * Controller使用的参数类 */ public class PersonVO { private String name; private int age; private String address; //ip地址 private String requestIP; public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } public String getRequestIP() { return requestIP; } public void setRequestIP(String requestIP) { this.requestIP = requestIP; } }
参数对象能够建立了, 可是PersonVO的对象是须要转成Person的对象的,这样才能插入到数据库中(数据库的insert方法的参数是Person对象)。这种转换操做其实也简单以下代码:
public Person convert2Person(PersonVO personVO){ Person person = new Person(); person.setName(personVO.getName()); person.setAge(personVO.getAge()); person.setAddress(personVO.getAddress()); return person; }
可是咱们一般是不这么作的,由于若是要转换的这个对象的字段不少那须要写不少次对象调setter方法来进行赋值。一种方式是直接写一个将全部属性当作参数的构造方法,直接一个一个把属性值传入就能够了,这种方式最简单暴力。还有一种方式就是须要包装一下这种方式,把Person改造一下。
以下代码:
public class Person { private Long id; private String name; private int age; private String address; public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } Person(){} Person(String name,int age,String address){ this.name = name; this.age = age; this.address = address; } public static Person builder(){ return new Person(); } public Person name(String name){ this.name = name; return this; } public Person age(int age) { this.age = age; return this; } public Person address(String address) { this.address = address; return this; } public Person build(){ return new Person(name,age,address); } }
后面新增了两个构造函数,以及一个builder()方法和一个build()方法,还有几个赋值方法,须要注意的是赋值方法和setter方法的区别,这样的赋值方法是在赋值后将当前对象返回,用来实现链式调用。
这样在对象转换的时候就能够这样用了:
public Person convert2Person(PersonVO personVO){ return Person.builder() .name(personVO.getName()) .age(personVO.getAge())
.address(personVO.getAddress()) .build(); }
这种方式其实也是一种建造者模式的应用,这种方式在构建对象的过程实现起来更灵活,例如若是这个对象就只有前两个参数有值,address是没有内容的,那能够直接这样写:
public Person convert2Person(PersonVO personVO){ return Person.builder() .name(personVO.getName()) .age(personVO.getAge()) .build(); }
在填充了两个属性后就直接调用build()方法区建立对象。
其实为了实现这种建立对象的方式,每次除了写getter/setter方法后还须要写这么多其余的代码,这样是有点麻烦的,因此在平常的开发过程当中,咱们是不必写额外的代码来实现这种方式,能够用工具来实现。推荐一个工具包Lombok,咱们的开发工具是使用IDEA,IDEA在使用Lombok时是须要下载一个lombok的插件,而后在项目中依赖lombok的工具包,就可使用了。使用了lombok后的代码变的很是简洁,连getter/setter方法都不用写了。
@Data @Builder @AllArgsConstructor @NoArgsConstructor public class Person { private Long id; private String name; private int age; private String address; }
@Data 这个注解表明实现了全部非final属性的getter/setter方法,以及重写了toString方法和hashCode方法。
@AllArgsConstructor 这个注解表明实现了一个包含所有属性为参数的构造方法(Person(Long id,String name,int age, String address))。
@NoArgsConstructor 这个注解表明实现了一个没有任何参数的构造方法(Person())。
@Builder 这个注解表明实现了上面介绍的那种灵活的建立对象的建造者模式(使用这个注解时须要依赖上面3个注解,缘由看这种方式的实现过程就能明白了)。
在建立对象时,使用方式没有变化也是链式调用方法赋值,这里就再也不写建立对象的过程了。
其实lombok还有一些其余的注解也很强大,使用这个工具包的好处是,不但使代码变得简洁,也提升了开发效率。
在这里想到了jQuery插件倡导的那个原则:“写的更少,作的更多”。