建造者模式

根据案例分析传统模式下可能出现的问题

案例

建造房子,假设可以建造普通房子与高楼两种,建造步骤是: 做地基,做墙,做屋顶

代码示例

  1. 普通房子与高楼可以看为是两种不同的产品,抽象出公共的接口或抽象类
//抽象建造者(根据产品的不同在各个产品子类中进行具体实现)
abstract class AbstractHouse{
	//地基
	public abstract void bulidBasic();
	//盖墙
	public abstract void bulidWall();
	//屋顶
	public abstract void roofed();
	
	//默认建房子的实现步骤
	public void bulid() {
		bulidBasic();
		bulidWall();
		roofed();
	}
}
  1. 创建产品类,“普通房子”,“高楼”,根据产品的不同重新抽象方法
//产品: 普通房子
class CommonHouse extends AbstractHouse{
	@Override
	public void bulidBasic() {
		System.out.println("普通房子做地基");
	}
	@Override
	public void bulidWall() {
		System.out.println("普通房子盖墙");
	}
	@Override
	public void roofed() {
		System.out.println("普通房子做屋顶");
	}
}

//产品:高楼
class HighBuilding extends AbstractHouse{
	@Override
	public void bulidBasic() {
		System.out.println("高楼做地基");
	}
	@Override
	public void bulidWall() {
		System.out.println("高楼盖墙");
	}
	@Override
	public void roofed() {
		System.out.println("高楼做屋顶");
	}	
}
  1. 测试
public class DesignTest {
	public static void main(String[]args) {
		//创建普通房子
		AbstractHouse house = new CommonHouse();
		//调用抽象类中的默认方法执行建造
		house.bulid();
		
		
	}
}

分析传统模式下可能存在的问题

将创建产品的过程即构建方法的步骤"–>地基–>砌墙–>屋顶"流程,封装在一个bulid()方法中,没有缓冲层,代码耦合的高,假设后续需要更改构建流程,例如先做屋顶后砌墙,需要更改bulid()方法中的代码

建造者模式示例

实现步骤

  1. 产品与实现流程,细节剥离创建产品类,也就是House
  2. 创建抽象建造者HouseBuilder, 根据实现细节的不同创建多个不同的建造者例如:CommonHouse与HighBuilding,通过建造者返回对应的产品
  3. 创建指挥者HouseDirector,将实现步骤定义在指挥者中,后续如果有更改实现步骤,只需要更改指挥者,例如此处的constructHouse()方法
  4. 对建造者模式,我的理解是: 创建抽象建造者,提供各个步骤的功能的抽象方法,例如"地基",“砌墙”,"屋顶"等,具体的实现由子类根据需求去完成,例如建普通房子,建高楼等,然后创建一个指挥者,由指挥者提供方法,统一管理每个步骤的先后顺序

代码示例

  1. 产品
//产品(产品与执行流程剥离,具有抽象化)
class House{
	 private String basic;
	 private String wall;
	 private String roofed;
	public String getBasic() {
		return basic;
	}
	public void setBasic(String basic) {
		this.basic = basic;
	}
	public String getWall() {
		return wall;
	}
	public void setWall(String wall) {
		this.wall = wall;
	}
	public String getRoofed() {
		return roofed;
	}
	public void setRoofed(String roofed) {
		this.roofed = roofed;
	}
}
  1. 抽象建造者
//抽象的建造者
abstract class HouseBuilder{
	//建房流程执行完毕后通过下方bulidHouse()方法返回的产品
	private House house = new House();
	
	//建造房子的抽象方法
	//地基
	public abstract void bulidBasic();
	//盖墙
	public abstract void bulidWall();
	//屋顶
	public abstract void roofed();
	
	//默认建房子的实现步骤
	public House bulidHouse() {
		//可能在上面三个方法执行后会创建出对应的House,最后通过该方法返回
		return house;
	}	
}
  1. 建造者
//根据产品的不同创建不同的建造者
//普通房子建造者
class CommonHouse extends HouseBuilder{
	@Override
	public void bulidBasic() {
		System.out.println("普通房子做地基");
	}
	@Override
	public void bulidWall() {
		System.out.println("普通房子盖墙");
	}
	@Override
	public void roofed() {
		System.out.println("普通房子做屋顶");
	}
}
//建造者:高楼
class HighBuilding extends HouseBuilder{
	@Override
	public void bulidBasic() {
		System.out.println("高楼做地基");
	}
	@Override
	public void bulidWall() {
		System.out.println("高楼盖墙");
	}
	@Override
	public void roofed() {
		System.out.println("高楼做屋顶");
	}	
}
  1. 指挥者
//指挥者,将建房流程步骤交由指挥者,指挥者需要持一个建造者
//在指挥者中通过持有的建造者的不同执行不同的流程,
//执行完毕后通过持有者返回产品
class HouseDirector{
	
	HouseBuilder houseBuilder = null;
	
	public HouseDirector(HouseBuilder houseBuilder) {
		this.houseBuilder = houseBuilder;
	}
	
	public void setHouseBuilder(HouseBuilder houseBuilder) {
		this.houseBuilder = houseBuilder;
	}
	
	//将建房的流程交给指挥者,在该方法中通过持有的建造者调用建造者自己的方法
	//后续如果更改建造流程,只需要更改此处即可
	public House constructHouse() {
		houseBuilder.bulidBasic();
		houseBuilder.bulidWall();
		houseBuilder.roofed();
		return houseBuilder.bulidHouse();
	}
}
  1. 测试
public class DesignTest {
	public static void main(String[]args) {

		/**
		 * 建造者模式下的角色
		 * 1.产品角色: 即具体产品,在建造者模式中产品与制造流程剥离,产品抽象化
		 * 2.抽象建造者: 创建出产品的抽象
		 * 3.具体建造者: 抽象建造者的具体实现类,根据产品的不同可能存在多个,
		 * 4.指挥者: 持有建造者,定义了建造的执行步骤,通过持有的建造者,执行步骤,
		 * 根据持有的建造者不同返回不同返回不同的结果
		 */
		//1.根据续签建造者对象普通房子CommonHouse (建造者有共同的抽象HouseBuilder
		//,通过建造者可以返回创建返回产品)
		HouseBuilder houseBuilder = new CommonHouse();
		//2.创建指挥者
		HouseDirector houseDirector = new HouseDirector (houseBuilder);
		//3.指挥者中定义了建造流程方法,执行完毕后,通过指挥者持有的建造者返回产品
		House house = houseDirector.constructHouse();
	}
}

JDK 建造者模式案例

在创建StringBuilder时,会调用append()方法,该方法在Appendable中为抽象方法,可以将Appendable看为抽象建造者, AbstractStringBuilder实现了Appendable接口并重写了appernd()方法,可以看为建造者,SpringBuilder继承了AbstractStringBuilder通过调用append()方法创建出对象,可以看为指挥者与具体的建造者

总结

  1. 客户端不必知道产品内部实现步骤组成细节,将产品本身与产品测创建过程解耦
  2. 根据产品实现的不同创建出不同的建造者,可以方法修改,增加新的建造者,
  3. 在建造者中更加方便的控制实现步骤,例如修改建造顺序,先做屋顶后砌墙等

业务与建造者模式落地

  1. 与传统的直接构建类实例的方式相比,建造者模式有以下优点:
  1. 易于构建复杂对象:建造者模式能够将一个复杂对象的构建过程分解为多个简单的步骤,使得构建过程更加清晰和容易管理。
  2. 可以控制对象的构建流程:通过Director类来协调Builder类的构建过程,可以确保每个步骤按照正确的顺序执行,并且可以在构建过程中进行检查和控制,从而保证构建出来的对象的质量。
  3. 可以灵活地改变对象的内部表示:由于建造者模式将对象的构建过程与其最终的表现形式解耦,因此可以方便地改变对象的内部表示方式,而不会影响到客户端代码。
  4. 更好地支持可变参数构建:如果使用传统的构建方式,为了支持可变参数构建,需要提供多个构造函数或者参数列表非常长的构造函数,这不仅增加了代码的冗余度,而且也降低了代码的可读性。而使用建造者模式,可以通过链式调用的方式方便地添加和移除不同的属性,使代码更加简洁和易读。

1. 案例1

  1. 在餐饮业务定制菜单中,客户可以按照自己的需求和口味喜好定制菜品,此时可以使用建造者模式。我们可以使用不同的Builder类来构建不同类型的菜品,如主食Builder、饮料Builder、配菜Builder等,并通过一个Director类来指导Builder如何组装,最终生成一份完整的定制菜单
  2. 定义一个菜品类,用来存储菜品的各种属性
public class Dish {
    private String name;  // 菜品名称
    private double price;  // 菜品价格
    private String category;  // 菜品类别
    
    // 其他getter和setter方法
}
  1. 定义一个Director类,用来协作组装不同Builder类
public class MenuDirector {
    private DishBuilder dishBuilder;  // 主食Builder
    private DrinkBuilder drinkBuilder;  // 饮料Builder
    private SideDishBuilder sideDishBuilder;  // 配菜Builder

    public MenuDirector(DishBuilder dishBuilder, DrinkBuilder drinkBuilder, SideDishBuilder sideDishBuilder) {
        this.dishBuilder = dishBuilder;
        this.drinkBuilder = drinkBuilder;
        this.sideDishBuilder = sideDishBuilder;
    }

    public Dish makeDish() {
        Dish dish = new Dish();
        dish.setName(dishBuilder.getName());
        dish.setPrice(dishBuilder.getPrice());
        dish.setCategory(dishBuilder.getCategory());

        dish.setName(drinkBuilder.getName());
        dish.setPrice(drinkBuilder.getPrice());
        dish.setCategory(drinkBuilder.getCategory());

        dish.setName(sideDishBuilder.getName());
        dish.setPrice(sideDishBuilder.getPrice());
        dish.setCategory(sideDishBuilder.getCategory());

        return dish;
    }
}
  1. Director类中包含了三个Builder类,分别用于构建主食、饮料、配菜。在makeDish()方法中,我们可以使用这些Builder类来逐步构建完整的Dish对象
  2. 主食Builder类的实现示例
public class DishBuilder {
    private String name;
    private double price;
    private String category;

    public DishBuilder setName(String name) {
        this.name = name;
        return this;
    }

    public DishBuilder setPrice(double price) {
        this.price = price;
        return this;
    }

    public DishBuilder setCategory(String category) {
        this.category = category;
        return this;
    }

    public Dish build() {
        Dish dish = new Dish();
        dish.setName(name);
        dish.setPrice(price);
        dish.setCategory(category);
        return dish;
    }
}
  1. 在Controller层中调用上述类和方法来实现定制菜单功能
@RestController
public class MenuController {
    @PostMapping("/menu")
    public Dish makeMenu(String mainDish, double mainDishPrice, String drink, double drinkPrice, String sideDish, double sideDishPrice) {
        DishBuilder dishBuilder = new DishBuilder().setName(mainDish).setPrice(mainDishPrice).setCategory("主食");
        DrinkBuilder drinkBuilder = new DrinkBuilder().setName(drink).setPrice(drinkPrice).setCategory("饮料");
        SideDishBuilder sideDishBuilder = new SideDishBuilder().setName(sideDish).setPrice(sideDishPrice).setCategory("配菜");
        MenuDirector menuDirector = new MenuDirector(dishBuilder, drinkBuilder, sideDishBuilder);
        return menuDirector.makeMenu();
    }
}

2. 案例2

  1. 在后台开发中,对数据库进行复杂的查询是很常见的任务。复杂的查询条件可能由多个参数组成,而且这些参数之间的组合方式也可能会存在多种情况。使用建造者设计模式可以将查询条件拆解成多个Builder类,然后使用一个Director类来协调它们的执行步骤和组合方式,最终得到一个完整的SQL查询语句
  2. 定义多个Builder类,每个Builder类负责处理一个查询参数:

WhereBuilder用来构建WHERE子句,SelectBuilder用来构建SELECT子句,OrderByBuilder用来构建ORDER BY子句。这里只是示例,实际应用中可能需要根据具体业务需求定义更多的Builder类

public interface IQueryBuilder {
    String build();  // 构建完整的查询语句
}

public class WhereBuilder implements IQueryBuilder {
    private String whereClause = "";

    public WhereBuilder() {
        this.whereClause = "WHERE 1=1 ";
    }

    public WhereBuilder addCondition(String condition) {
        this.whereClause += "AND " + condition + " ";
        return this;
    }

    @Override
    public String build() {
        return this.whereClause;
    }
}

public class SelectBuilder implements IQueryBuilder {
    private List<String> columns = new ArrayList<>();

    public SelectBuilder addColumn(String column) {
        this.columns.add(column);
        return this;
    }

    @Override
    public String build() {
        if (this.columns.size() == 0) {
            return "*";
        } else {
            return StringUtils.join(this.columns.toArray(), ",");
        }
    }
}

public class OrderByBuilder implements IQueryBuilder {
    private String orderBy = "";

    public OrderByBuilder() {
        this.orderBy = "";
    }

    public OrderByBuilder addCondition(String orderBy) {
        this.orderBy += orderBy;
        return this;
    }

    @Override
    public String build() {
        if (StringUtils.isBlank(this.orderBy)) {
            return "";
        } else {
            return "ORDER BY " + this.orderBy;
        }
    }
}
  1. 定义Director类,该类负责协调各个Builder类的执行步骤和构建顺序
public class QueryBuilder {
    private IQueryBuilder whereBuilder;
    private IQueryBuilder selectBuilder;
    private IQueryBuilder orderByBuilder;

    public QueryBuilder(IQueryBuilder whereBuilder, IQueryBuilder selectBuilder, IQueryBuilder orderByBuilder) {
        this.whereBuilder = whereBuilder;
        this.selectBuilder = selectBuilder;
        this.orderByBuilder = orderByBuilder;
    }

    public String buildQuery() {
        StringBuilder queryBuilder = new StringBuilder();
        queryBuilder.append("SELECT ").append(selectBuilder.build()).append(" FROM employee ");
        queryBuilder.append(whereBuilder.build());
        queryBuilder.append(orderByBuilder.build());

        return queryBuilder.toString();
    }
}
  1. 请求入口,调用QueryBuilder类来构建SQL查询语句
@Controller
@RequestMapping("/employee")
public class EmployeeController {
    @Autowired
    private EmployeeMapper employeeMapper;

    @GetMapping("/list")
    public String list(@RequestParam(required = false) String name,
                       @RequestParam(required = false) Integer departmentId) {
        WhereBuilder whereBuilder = new WhereBuilder();
        if (StringUtils.isNotBlank(name)) {
            whereBuilder.addCondition("name='" + name + "'");
        }
        if (departmentId != null) {
            whereBuilder.addCondition("department_id=" + departmentId);
        }

        SelectBuilder selectBuilder = new SelectBuilder();
        selectBuilder.addColumn("id").addColumn("name").addColumn("department_id");

        OrderByBuilder orderByBuilder = new OrderByBuilder();
        orderByBuilder.addCondition("id ASC");

        QueryBuilder queryBuilder = new QueryBuilder(whereBuilder, selectBuilder, orderByBuilder);
        String sql = queryBuilder.buildQuery();
        
        List<Employee> employees = employeeMapper.findBySql(sql);
        // ...
        return "employee/list";
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值