肝一肝设计模式【一】-- 单例模式 传送门
肝一肝设计模式【二】-- 工厂模式 传送门
肝一肝设计模式【三】-- 原型模式 传送门
肝一肝设计模式【四】-- 建造者模式 传送门
肝一肝设计模式【五】-- 适配器模式 传送门
肝一肝设计模式【六】-- 装饰器模式 传送门
肝一肝设计模式【七】-- 代理模式 传送门
肝一肝设计模式【八】-- 外观模式 传送门
肝一肝设计模式【九】-- 享元模式 传送门
前文中我们知道设计模式可以分为三大类:创建型模式、结构型模式、行为型模式。创建型模式中有5种,前几节里我们分析了单例模式、工厂方法模式、抽象工厂模式、原型模式,那么创建型模式还有最后一种模式:建造者模式,这一节中我们来分析一下建造者模式。
建造者模式(Builder Pattern),将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的对象。
可以这么理解,对象的构建与它的表示分离,就是创建一个对象的实例和成员变量的赋值相分离。建造者模式使用多个简单的对象一步一步构建成一个复杂的对象,这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
以制作一本儿童绘本为例(默默拿起手边的《白雪公主》┐(・o・)┌ )
通常绘本由封面、标题、内容、插图、页码来组成,这些内容的组成顺序可以随意调整,我们用建造者模式来模拟一下制作绘本的过程。
写在代码:
@Data
public class Book {
private String cover;
private String title;
private String content;
private String pic;
private Integer pageNo;
@Override
public String toString() {
return "Book{" +
"cover='" + cover + '\'' +
", title='" + title + '\'' +
", content='" + content + '\'' +
", pic='" + pic + '\'' +
", pageNo='" + pageNo + '\'' +
'}';
}
}
然后再建一个建造者类,封装创建过程,由使用者根据需要来调用
public class BookBuilder {
private Book book = new Book();
public BookBuilder addCover(String cover){
book.setCover(cover);
return this;
}
public BookBuilder addTitle(String title){
book.setTitle(title);
return this;
}
public BookBuilder addContent(String content){
book.setContent(content);
return this;
}
public BookBuilder addPic(String pic){
book.setPic(pic);
return this;
}
public BookBuilder addPageNo(Integer pageNo){
book.setPageNo(pageNo);
return this;
}
public Book build(){
return book;
}
}
测试一下
public static void main(String[] args) {
BookBuilder builder = new BookBuilder()
.addCover("漂亮的封面")
.addTitle("《白雪公主》")
.addContent("美丽善良的公主被恶毒的皇后嫉妒,被迫逃亡,最终被七个小矮人救助。")
.addPic("漂亮的插图")
.addPageNo(30);
System.out.println(builder.build());
}
是不是有点熟悉,像不像MyBatis Plus中QueryWrapper的写法,后文我们在开源框架的应用中来具体说一说。
通常我们更习惯使用静态内部类的方式实现建造者模式,由它负责产品的组装创建,不再需要Builder和Director,这样的话代码结构更加紧凑,创建过程与产品的属性联系更加紧密,同时使得建造者模式的形式更加简洁。
然后我们来修改下代码:
@Data
public class Book {
private String cover;
private String title;
private String content;
private String pic;
private Integer pageNo;
@Override
public String toString() {
return "Book{" +
"cover='" + cover + '\'' +
", title='" + title + '\'' +
", content='" + content + '\'' +
", pic='" + pic + '\'' +
", pageNo='" + pageNo + '\'' +
'}';
}
public static class Builder {
private Book book = new Book();
public BookBuilder addCover(String cover){
book.setCover(cover);
return this;
}
public BookBuilder addTitle(String title){
book.setTitle(title);
return this;
}
public BookBuilder addContent(String content){
book.setContent(content);
return this;
}
public BookBuilder addPic(String pic){
book.setPic(pic);
return this;
}
public BookBuilder addPageNo(Integer pageNo){
book.setPageNo(pageNo);
return this;
}
public Book build(){
return book;
}
}
}
测试一下
public static void main(String[] args) {
Book book = new Book.Builder()
.addCover("漂亮的封面")
.addTitle("《白雪公主》")
.addContent("美丽善良的公主被恶毒的皇后嫉妒,被迫逃亡,最终被七个小矮人救助。")
.addPic("漂亮的插图")
.addPageNo(30)
.build();
System.out.println(book);
}
是不是简洁和优雅多了(〃‘▽’〃)
刚刚在写样例的时候,有的同学可能已经发现,这种链式写法很像MyBatis Plus的QueryWrapper写法,那么就结合MyBatis Plus这个开源持久层框架的源码,简单来说一说MyBatis Plus是如何基于建造者模式的思想,提供一套流畅的API来构建查询条件的。
由于实际MyBatis Plus源码是很复杂的,我们拿一个简化版的来举例
public class QueryWrapper<T> {
private List<String> conditions;
public QueryWrapper<T> eq(String column, Object value) {
conditions.add(column + " = " + value);
return this;
}
public QueryWrapper<T> ne(String column, Object value) {
conditions.add(column + " != " + value);
return this;
}
public QueryWrapper<T> gt(String column, Object value) {
conditions.add(column + " > " + value);
return this;
}
// 其他条件方法...
public List<T> selectList() {
// 根据conditions构建查询语句并执行查询
}
}
在这个样例中,QueryWrapper类定义了一系列方法,例如eq、ne、gt等,用于设置不同的查询条件。每个条件方法都返回QueryWrapper对象本身,以便实现链式调用。这里的关键点是在每个条件方法中,都将新的条件添加到conditions列表中,并返回当前对象的引用。
这样我们就可以使用链式调用的方式在一个语句中设置多个查询条件
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper
.eq("name", "John")
.ne("age", 30)
.gt("salary", 5000);
这种设计提高了代码的可读性和可维护性,使得构建复杂查询条件变得更加直观和简洁。
建造者模式的使用场景:
建造者模式的优点:
建造者模式的缺点