主目录见:Android高级进阶知识(这是总目录索引)
今天我们正式来讲讲一些常用的设计模式在android中的应用,适当适时地应用设计模式,能使程序看起来更加优雅,今天这里的建造者模式是非常常用的一个设计模式,不仅在框架中或者在android的源码中都能见到这个设计模式,我们先来看他的定义:
建造者模式:是将一个复杂的对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
这个定义并不抽象,所谓的复杂对象的构建与它的表示分离,就比如人这个对象,他有身高,体重,年龄等等表示,但是要构建不同的人这个动作和这些人这个对象的属性的表示可以用建造者模式分离,我们待会会举个例子。接着我们来看看这个设计模式的UML类图:
角色介绍:
- Director:调用具体建造者来创建复杂对象的各个部分,在指导者中不涉及具体产品的信息,只负责保证对象各部分完整创建或按某种顺序创建。
- Builder:给出一个抽象接口,以规范产品对象的各个组成成分的建造。这个接口规定要实现复杂对象的哪些部分的创建,并不涉及具体的对象部件的创建。
- ConcreteBuilder:实现Builder接口,针对不同的商业逻辑,具体化复杂对象的各部分的创建。 在建造过程完成后,提供产品的实例。
- Product:要创建的复杂对象。
一.目标
因为很多时候我们不知道什么时候该用什么设计模式,甚至有些时候根本没想过要用设计模式,我们今天的目标就是:
1.明白建造者模式怎么使用;
2.知道在哪些场景下会用到建造者模式。
二.模式讲解
我们刚才说了,建造者模式就是将一个复杂对象的构建和它的表示分离,那么这个是什么意思呢?我们先来说一个复杂对象的表示。
1.Product
public class Person_Product {
private String name; //名字
private int age; //年龄
private double height; //身高
private double weight; //体重
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 double getHeight() {
return height;
}
public void setHeight(double height) {
this.height = height;
}
public double getWeight() {
return weight;
}
public void setWeight(double weight) {
this.weight = weight;
}
}
可以看到这里的人这个对象可以用名字,年龄,身高,体重来表示。那么这个对象的构建呢?我们怎么构建出不同的人呢?如果没有建造者模式的情况下,我们可能这么做:
public Person_Product() {
super();
}
public Person_Product(String name, int age) {
super();
this.name = name;
this.age = age;
}
public Person_Product(String name, int age, double height) {
super();
this.name = name;
this.age = age;
this.height = height;
}
public Person_Product(String name, int age, double height, double weight) {
super();
this.name = name;
this.age = age;
this.height = height;
this.weight = weight;
}
我们这里可能创建出多个构造函数,因为我们可能只要创建拥有名字和年龄的人或者我们可能需要增加体重和身高这两个属性的人,所以最后我们的创建过程变成:
Person_Product person_Product = new Person_Product();
Person_Product person_Product2 = new Person_Product("java程序员", 35);
Person_Product person_Product3 = new Person_Product("c++程序员", 27, 175);
Person_Product person_Product4 = new Person_Product("python程序员", 30, 170, 130);
我们看到这个构建方法是构建出人这个对象了,但是我们不明白到底这些参数是什么意思,同时在Person这个对象里面需要创建多个构造函数,如果属性变多呢?那不是按照这个结果不是要很多个构造函数。现在我们换个思路,用建造者设计模式来重新改变下这个构建过程。
2.Builder
抽象建造者Builder对象主要是用来定义一个统一的构建过程,其实这个不是必须的,但是为了演示标准的建造者设计模式我们还是来创建这个类:
public String name; //名字
public int age; //年龄
public double height; //身高
public double weight; //体重
public Builder setName(String name) {
this.name = name;
return this;
}
public Builder setAge(int age) {
this.age = age;
return this;
}
public Builder setHeight(double height) {
this.height = height;
return this;
}
public Builder setWeight(double weight) {
this.weight = weight;
return this;
}
public abstract void buildName();
public abstract void buildAge();
public abstract void buildWeight();
public abstract void buildHeight();
public abstract Person_Product getResult();
我们看到类里面有构建名字,年龄,体重,身高的方法。然后我们接下来就创建具体的建造者对象了。
3.ConcreteBuilder
public class ConcreteBuilder extends Builder{
private Person_Product person_Product = new Person_Product();
@Override
public void buildName() {
person_Product.setName(this.name);
}
@Override
public void buildAge() {
person_Product.setAge(this.age);
}
@Override
public void buildWeight() {
person_Product.setWeight(this.weight);
}
@Override
public void buildHeight() {
person_Product.setHeight(this.height);
}
@Override
public Person_Product getResult(){
return person_Product;
}
}
具体建造者模式主要是将建造者的年龄,身高,体重等信息设置给了人对象即Product。最后就是我们的Director对象了,用于指导建造的过程。
4.Director
public class Director {
public void construct(Builder builder){
builder.buildName();
builder.buildAge();
builder.buildHeight();
builder.buildWeight();
}
}
我们看到这个方法就是调用我们的所有构建过程进行构建。那么我们看下这个设计模式是怎么调用的。
5.调用
Builder builder = new ConcreteBuilder().setAge(12)
.setName("android工程师")
.setHeight(170)
.setWeight(132);
Director director = new Director();
director.construct(builder);
Person_Product person_Product = builder.getResult();
System.out.println(person_Product.getName()+"年龄:"+person_Product.getAge()+"身高:"+person_Product.getHeight()+"体重:"+person_Product.getWeight());
我们看到构建的过程Builder链式构建,然后传给Director进行构建。随意Person这个对象的表示和构建是分开的,两者没有耦合。现在我们再根据OkHttp或者EventBus框架这些的变形用法,进行修改。这里我们将Director和Builder的角色跟Product的角色进行合并。具体代码如下:
6.改版后建造者模式
public class Person_Product {
private String name; //名字
private int age; //年龄
private double height; //身高
private double weight; //体重
public Person_Product(){
super();
}
public Person_Product(Builder builder){
this.name = builder.name;
this.age = builder.age;
this.height = builder.height;
this.weight = builder.weight;
}
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 double getHeight() {
return height;
}
public void setHeight(double height) {
this.height = height;
}
public double getWeight() {
return weight;
}
public void setWeight(double weight) {
this.weight = weight;
}
public static class Builder{
public String name; //名字
public int age; //年龄
public double height; //身高
public double weight; //体重
public Builder setName(String name) {
this.name = name;
return this;
}
public Builder setAge(int age) {
this.age = age;
return this;
}
public Builder setHeight(double height) {
this.height = height;
return this;
}
public Builder setWeight(double weight) {
this.weight = weight;
return this;
}
public Person_Product build(){
Person_Product person = new Person_Product(this);
return person;
}
}
}
我们看到Builder对象以一个内部类的形式出现,然后Director的Controuct()方法以构造函数的形式出现。这样的话我们的调用就变得非常简单:
Person_Product.Builder builder = new Person_Product.Builder();
Person_Product person = builder.setName("android工程师")
.setAge(29)
.setHeight(176)
.setWeight(135)
.build();
我们看到修改后的建造者模式写法更加简单了,而且想要set哪个属性就set哪个属性。
7.OkHttp中的建造者模式
我们来看OkHttp的使用方法:
Request requestBuilder = new Request.Builder().url("http://www.baidu.com").build();
我们看到这里的Request对象的构建跟我们上面改进后建造者的调用方法一致,所以我们来看下Request的构造函数:
Request(Builder builder) {
this.url = builder.url;
this.method = builder.method;
this.headers = builder.headers.build();
this.body = builder.body;
this.tag = builder.tag != null ? builder.tag : this;
}
我们看到跟我们上面的做法是一模一样的,都是利用的Builder来赋值Request的属性值。
public static class Builder {
HttpUrl url;
String method;
Headers.Builder headers;
RequestBody body;
Object tag;
......
}
同时我们看到这个Builder也是Request的内部类。还有一些建造方法:
public Builder addHeader(String name, String value) {
headers.add(name, value);
return this;
}
public Builder removeHeader(String name) {
headers.removeAll(name);
return this;
}
......
这里选择Builder里面的一些建造方法来看,我们发现做法跟我们是相同的。我们再来看看另外一个开源框架EventBus的建造者模式身影。
8.EventBus的建造者模式
同样的,我们先来看看EventBus的基本用法是怎么样的。
EventBus.builder().addIndex(new MyEventBusIndex()).installDefaultEventBus();
我们看到这里明显也是用了建造者模式,我们先来看看EventBus类的构造函数:
EventBus(EventBusBuilder builder) {
subscriptionsByEventType = new HashMap<>();
typesBySubscriber = new HashMap<>();
stickyEvents = new ConcurrentHashMap<>();
mainThreadPoster = new HandlerPoster(this, Looper.getMainLooper(), 10);
backgroundPoster = new BackgroundPoster(this);
asyncPoster = new AsyncPoster(this);
indexCount = builder.subscriberInfoIndexes != null ? builder.subscriberInfoIndexes.size() : 0;
subscriberMethodFinder = new SubscriberMethodFinder(builder.subscriberInfoIndexes,
builder.strictMethodVerification, builder.ignoreGeneratedIndex);
logSubscriberExceptions = builder.logSubscriberExceptions;
logNoSubscriberMessages = builder.logNoSubscriberMessages;
sendSubscriberExceptionEvent = builder.sendSubscriberExceptionEvent;
sendNoSubscriberEvent = builder.sendNoSubscriberEvent;
throwSubscriberException = builder.throwSubscriberException;
eventInheritance = builder.eventInheritance;
executorService = builder.executorService;
}
如我们所见,这里的构造函数也是利用了builder来赋值,我们再看看Builder类:
public class EventBusBuilder {
private final static ExecutorService DEFAULT_EXECUTOR_SERVICE = Executors.newCachedThreadPool();
boolean logSubscriberExceptions = true;
boolean logNoSubscriberMessages = true;
boolean sendSubscriberExceptionEvent = true;
boolean sendNoSubscriberEvent = true;
boolean throwSubscriberException;
boolean eventInheritance = true;
boolean ignoreGeneratedIndex;
boolean strictMethodVerification;
ExecutorService executorService = DEFAULT_EXECUTOR_SERVICE;
List<Class<?>> skipMethodVerificationForClasses;
List<SubscriberInfoIndex> subscriberInfoIndexes;
......
}
我们看到这个builder跟EventBus里面对应的属性是一样的,且我们来看看builder的一些建造方法和build方法:
/** Forces the use of reflection even if there's a generated index (default: false). */
public EventBusBuilder ignoreGeneratedIndex(boolean ignoreGeneratedIndex) {
this.ignoreGeneratedIndex = ignoreGeneratedIndex;
return this;
}
/** Enables strict method verification (default: false). */
public EventBusBuilder strictMethodVerification(boolean strictMethodVerification) {
this.strictMethodVerification = strictMethodVerification;
return this;
}
public EventBus installDefaultEventBus() {
synchronized (EventBus.class) {
if (EventBus.defaultInstance != null) {
throw new EventBusException("Default instance already exists." +
" It may be only set once before it's used the first time to ensure consistent behavior.");
}
EventBus.defaultInstance = build();
return EventBus.defaultInstance;
}
}
/** Builds an EventBus based on the current configuration. */
public EventBus build() {
return new EventBus(this);
}
同样的,这个跟我们之前用过的方式是一样的,到这里我们应该很明白建造者的用法了。明显,如果一个对象的属性比较多比较复杂,那么在创建他的时候,我们可以采用建造者来灵活地建造,使我们程序更加优雅。当然了android里面还有很多利用到这个比如AlertDialog,GsonBuilder 等等,有兴趣大家可以都看看。
总结:今天我们讲了建造者设计模式,这个模式用的地方还是非常广的,而且这个建造者在LRouter这个框架里面也是有用到的,用法简单但是有效,希望大家能在适合这个模式的场景中使用到。