Builder -创建者设计模式 是一种非常常见的设计模式,在安卓源码和第三方组件中经常可以见到:
边学编写 我将从以下几个方面整理
- 一.概念
- 二.使用场景,案例,标准写法,其他实现方法
- 三.对比优缺点,总结
太长不看版本:
使用场景:当一个类较复杂,创建时候容易出问题或者不便于理解或者较复杂,希望规范化创建的时候使用创建者模式。
使用:对外通过导演类(规范流程)使用一个实现预期接口的建造者(builder)作为参数来进行创建,不同建造者可以创建不同对象,建造者中实现商业逻辑,最后返回一个造好的商品。导演类调用建造者逻辑,返回造好的商品。
优点:规范,对外方便简洁,利于扩展,内部逻辑可以自己把控。
缺点:如果内部逻辑过于复杂,需要创建很多数据类(Builder),得不偿失。
一、概念
(概念这个东西从来都是 开始看的不知所云或者总觉得很鸡肋的话,但是后来对问题有了深入了解就会发现很重要的的总结)
1 定义: 将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的展示。
2 Builder模式属于创建型,一步一步将一个复杂对象创建出来,允许用户在不知道内部构建细节的情况下,可以更精细地控制对象的构造流程。
二、使用场景,案例
当我们阅读android源码时候会经常看到创造者模式,如常见的Dialog的创建
new AlertDialog.Builder(context)
.setIcon(R.drawable.navigation_empty_icon).
create();
而在我们实际工作中也经常会遇到这样需求:
由于业务需要创建一个很复杂的类的时候,需要多次创建一个类,并挨个对其属性进行赋值,在创建过程中往往还需要加入只有自己重读都费力的逻辑顺序,这样的代码无疑是十分丑陋和不规范的。而创建者设计模式可以解决这样的问题,那么我们根据什么条件来判断是否应该使用创建者设计模式呢?
- 需要生成的产品对象有复杂的内部结构,这些产品对象通常包含多个成员属性。
需要生成的产品对象的属性相互依赖,需要指定其生成顺序。 - 对象的创建过程独立于创建该对象的类。在建造者模式中引入了指挥者类,将创建过程封装在-指挥者类中,而不在建造者类中。
- 隔离复杂对象的创建和使用,并使得相同的创建过程可以创建不同的产品。
这里使用另外一个例子进行讲解。
如一个学生类 由基本属性(年龄、姓名) 成绩属性(各科分数,总分数由各课分数之和得到 )
public class Student {
传统方法
private String name;
private int age;
private int totleGrade;
private int mathGrade;
private int chineseGrade;
// 等等 ……
}
传统方法
当我们需要创建这样一个对象时一般方法是2种: 1创建一个空对象使用set方法设置属性,2使用带参数构造方法传入参数。
Student student = new Student();
student.setName("张三");
student.setAge(12);
……
或者
public Student(String name, int age, ……){
this.name = name;
this.age = age;
……
}
这两种方法在数据量少并且逻辑简单的时候使用并没有什么问题,但是当我们成员多创建逻辑复杂时(比如总成绩为各科成绩之和),会增加学习成本并且不利于代码阅读和维护。
下面我们了解一下创建者模式:
创建者模式
1、Builder:给出一个抽象接口,规范建造者对于生产的产品的各个组成部分的建造。这个接口只是定一个规范,不涉及具体的建造,具体的建造让继承于它的子类(ConcreteBuilder)去实现。
2、ConcreteBuilder:实现builder接口,针对不同的商业逻辑,具体化各对象部分的建造,最后返回一个建造好的产品。
3、Director:导演,顾名思义,负责规范流程之用。在指导中不涉及产品的创建,只负责保证复杂对象各部分被创建或按某种顺序创建。
4、Product:复杂对象。
按照惯例,给出建造者模式的UML图(这个先记下来,非常有用,学一个设计模式,先把这个图记下来!)
简单理解一下就是:
导演(Diector)根据大纲(Builder)写好的剧本(ConcreteBuilder) 拍出作品(Product)。
//1、Builder:给出一个抽象接口,规范建造者对于生产的产品的各个组成部分的建造。
// 这个接口只是定一个规范,不涉及具体的建造,具体的建造让继承于它的子类(ConcreteBuilder)去实现。
public interface Builder {
void setName();
void setAge();
void setMathGrade();
void setChineseGrade();
Student build();
}
//ConcreteBuilder:实现builder接口,针对不同的商业逻辑,
// 具体化各对象部分的建造,最后返回一个建造好的产品。
public class BuilderXiaoZhang implements Builder {
private Student student = new Student();
@Override
public void setName() {
student.setName("xiaoZhang");
}
public void setAge() {
student.setAge(14);
}
@Override
public void setMathGrade() {
student.setMathGrade(99);
}
public void setChineseGrade() {
student.setChineseGrade(100);
}
@Override
public Student build() {
return student;
}
}
//Product:复杂对象。
public class Student {
private String name;
private int age;
private int mathGrade;
private int chineseGrade;
//省略setxxx() getxxx()方法
public Student() {
}
}
//Director:导演,顾名思义,负责规范流程之用。
//在指导中不涉及产品的创建,只负责保证复杂对象各部分被创建或按某种顺序创建。
public class StudentRegistrar {
private Builder builder;
public StudentRegistrar(Builder builder) {
this.builder = builder;
}
//产生产品,这里控制逻辑、顺序
public Student construct(){
builder.setName();
builder.setAge();
builder.setChineseGrade();
builder.setMathGrade();
return builder.build();
}
}
使用方法为
BuilderXiaoZhang builderXiaoZhang = new BuilderXiaoZhang();
Student stuZhang = new StudentRegistrar(builderXiaoZhang).construct();
我们可以看到导演类(StudentRegistrar )通过创建方法(construct())控制了复杂对象的赋值,外部调用者只需要创建合适的数据类(实现Builder接口)就可以实现复杂对象的创建。
契合了创建者设计模式的理念:
- 在建造者模式中, 客户端不必知道产品内部组成的细节,将产品本身与产品的创建过程解耦,使得相同的创建过程可以创建不同的产品对象。
- 每一个具体建造者都相对独立,而与其他的具体建造者无关,因此可以很方便地替换具体建造者或增加新的具体建造者,用户使用不同的具体建造者即可得到不同的产品对象。
- 可以更加精细地控制产品的创建过程 。将复杂产品的创建步骤分解在不同的方法中,使得创建过程更加清晰,也更方便使用程序来控制创建过程。
- 增加新的具体建造者无须修改原有类库的代码,指挥者类针对抽象建造者类编程,系统扩展方便,符合“开闭原则”。
变形
但是我们以往见到的和通常希望的是不创建新类,仅通过合适的接口来进行属性赋值来完成创建工作如下
public class Student {
private String name;
private int age;
private int mathGrade;
private int chineseGrade;
//私有构造方法
private Student() {
}
//builder接口 规范需要实现必要方法
public interface Builder {
Builder setName(String name);
Builder setAge(int age);
Builder setMathGrade(int mathGrade);
Builder setChineseGrade(int chineseGrade);
Student build();
}
//静态内部类
public static class BuilderContrete implements Builder {
private Student student;
public BuilderContrete() {
student = new Student();
}
@Override
public Builder setName(String name) {
student.name = name;
return this;
}
@Override
public Builder setAge(int age) {
student.age = age;
return this;
}
@Override
public Builder setMathGrade(int mathGrade) {
student.mathGrade = mathGrade;
return this;
}
@Override
public Builder setChineseGrade(int chineseGrade) {
student.chineseGrade = chineseGrade;
return this;
}
//返回设计结果类
@Override
public Student build() {
return student;
}
}
}
使用如下方法既可获得学生类
Student stu = new Student.BuilderContrete()
.setName("张三")
.setAge(14)
.build();
简单理解一下就是 小成本电影:
导演(Diector)自己写剧本(ConcreteBuilder)不要大纲(Builder)直接拍摄出作品(Product);
这样即满足了创建对象的灵活性,也在一定程度上节约了学习创建成本,链式的创建方法也清晰明了。
三.对比优缺点,总结
优点
- 1.使用建造者模式可以使客户端不必知道产品内部组成的细节。
- 2.具体的建造者类之间是相互独立的,对系统的扩展非常有利。
- 3.由于具体的建造者是独立的,因此可以对建造过程逐步细化,而不对其他的模块产生任何影响。
缺点
- 1.建造者模式所创建的产品一般具有较多的共同点,其组成部分相似,如果产品之间的差异性很大,例如很多组成部分都不相同,不适合使用建造者模式,因此其使用范围受到一定的限制。
- 2.如果产品的内部变化复杂,可能会导致需要定义很多具体建造者类来实现这种变化,导致系统变得很庞大,增加系统的理解难度和运行成本。
网友评论