Java设计模式-内功修炼-建造者模式

建造者模式

定义

建造者模式(Builder Pattern):将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。建造者模式是一种对象创建型模式。

建造者模式在Android开发时会用到,比如AlertDialog.Builder,LuBan压缩, OkHttpUtils中均用到Builder。

Builder Pattern适用于构建复杂对象,所谓复杂对象就是包含很多成员属性的对象,下面直接以我在Android开发中自己写的一个工具类为例进行讲解。

实例讲解

先看下我是怎么使用的:
在这里插入图片描述
从上图中红框部分应该不难看出,我实现的工具类的功能是保存联系人,我为什么要封装Android中保存联系人这个功能呢?我们看下工具类源码就知道了,其内部涉及判断String是否为空,以及大量的数据库操作,很繁琐:

package com.example.geigei_android.util;

import android.annotation.SuppressLint;
import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.provider.ContactsContract;
import android.text.TextUtils;

/**
 * author:zjp
 * date:2019/5/9
 */
public class JiPeng {
	private String name;
	private String organ;
	private String organTitle;
	private String mobilePhone;
	private String email;
	private String workPhone;
	private String postal;

	private JiPeng(Builder builder) {
		this.name = builder.name;
		this.organ = builder.organ;
		this.organTitle = builder.organTitle;
		this.mobilePhone = builder.mobilePhone;
		this.email = builder.email;
		this.workPhone = builder.workPhone;
		this.postal = builder.postal;
	}

	public static Builder with(Context context) {
		return new Builder(context);
	}

	private void save(Context context) {
		ContentResolver resolver = context.getContentResolver();
		//找到最后一条记录的id
		@SuppressLint("Recycle") Cursor cursor = resolver.query(ContactsContract.RawContacts.CONTENT_URI, new String[]{"_id"},
				null, null, null, null);
		ContentValues temp = new ContentValues();
		long newId = ContentUris.parseId(resolver.insert(ContactsContract.RawContacts.CONTENT_URI,
				temp));
		LogUtil.d("newId is " + newId);
		LogUtil.d("name is " + this.name);
		//插入一个联系人id
		ContentValues dataValues = new ContentValues();
		//插入移动电话数据
		if (!TextUtils.isEmpty(this.mobilePhone)) {
			//指定数据属于哪个联系人
			dataValues.put(ContactsContract.Data.RAW_CONTACT_ID, newId);
			//指定数据类型
			dataValues.put(ContactsContract.Data.MIMETYPE,
					ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE);
			//数据
			dataValues.put(ContactsContract.CommonDataKinds.Phone.NUMBER, this.mobilePhone);
			resolver.insert(ContactsContract.Data.CONTENT_URI, dataValues);
		}


		//插入工作电话数据
		if (!TextUtils.isEmpty(this.workPhone)) {
			dataValues.clear();
			dataValues.put(ContactsContract.Data.RAW_CONTACT_ID, newId);
			dataValues.put(ContactsContract.Data.MIMETYPE,
					ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE);
			dataValues.put(ContactsContract.CommonDataKinds.Phone.NUMBER, this.workPhone);
			resolver.insert(ContactsContract.Data.CONTENT_URI, dataValues);
		}


		//插入姓名数据
		if (!TextUtils.isEmpty(this.name)) {
			dataValues.clear();
			dataValues.put(ContactsContract.Data.RAW_CONTACT_ID, newId);
			dataValues.put(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE);
			dataValues.put(ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME, this.name);
			resolver.insert(ContactsContract.Data.CONTENT_URI, dataValues);
		}

		//插入邮箱
		if (!TextUtils.isEmpty(this.email)) {
			dataValues.clear();
			dataValues.put(ContactsContract.Data.RAW_CONTACT_ID, newId);
			dataValues.put(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE);
			dataValues.put(ContactsContract.CommonDataKinds.Email.DATA, this.email);
			dataValues.put(ContactsContract.CommonDataKinds.Email.TYPE, ContactsContract.CommonDataKinds.Email.TYPE_WORK);
			resolver.insert(ContactsContract.Data.CONTENT_URI, dataValues);
		}

		//添加地址
		if (!TextUtils.isEmpty(this.postal)) {
			dataValues.clear();
			dataValues.put(ContactsContract.Data.RAW_CONTACT_ID, newId);
			dataValues.put(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.StructuredPostal.CONTENT_ITEM_TYPE);
			dataValues.put(ContactsContract.CommonDataKinds.StructuredPostal.DATA, this.postal);
			dataValues.put(ContactsContract.CommonDataKinds.StructuredPostal.TYPE, ContactsContract.CommonDataKinds.StructuredPostal.TYPE_WORK);
			resolver.insert(ContactsContract.Data.CONTENT_URI, dataValues);
		}

		//添加公司
		if (!TextUtils.isEmpty(this.organ)) {
			dataValues.clear();
			dataValues.put(ContactsContract.Data.RAW_CONTACT_ID, newId);
			dataValues.put(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Organization.CONTENT_ITEM_TYPE);
			dataValues.put(ContactsContract.CommonDataKinds.Organization.DATA, this.organ);
			if (!TextUtils.isEmpty(this.organTitle))
				dataValues.put(ContactsContract.CommonDataKinds.Organization.TITLE, this.organTitle);
			dataValues.put(ContactsContract.CommonDataKinds.Organization.TYPE, ContactsContract.CommonDataKinds.Organization.TYPE_WORK);
			resolver.insert(ContactsContract.Data.CONTENT_URI, dataValues);
		}


	}

	public static class Builder {
		private Context context;
		private String name;
		private String organ;
		private String organTitle;
		private String mobilePhone;
		private String email;
		private String workPhone;
		private String postal;

		Builder(Context context) {
			this.context = context;
		}

		private JiPeng build() {
			return new JiPeng(this);
		}

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

		public Builder setOrgan(String organ) {
			this.organ = organ;
			return this;
		}

		public Builder setOrganTitle(String organTitle) {
			this.organTitle = organTitle;
			return this;
		}

		public Builder setMobilePhone(String mobilePhone) {
			this.mobilePhone = mobilePhone;
			return this;
		}

		public Builder setEmail(String email) {
			this.email = email;
			return this;
		}

		public Builder setWorkPhone(String workPhone) {
			this.workPhone = workPhone;
			return this;
		}

		public Builder setPostal(String postal) {
			this.postal = postal;
			return this;
		}

		public void save() {
			build().save(context);
		}
	}
}

我们按照上图中的调用顺序一点一点来看Builder是怎么起作用的。
首先我调用with函数

//我的调用
JiPeng.with(this)
//对应工具源代码
	public static Builder with(Context context) {
		return new Builder(context);
	}

with()函数是一个静态函数,其新建一个Builder,关于Builder的定义见上文代码部分。

然后又调用了一堆setXxx函数,这些函数本质上都是一样的,从上图可以看出,我们的调用格式是

//with之后直接跟.setName()
//也就是说我们后面的操作都是对with函数新建的Builder对象操作的
JiPeng.with(this)
	.setName(......)
//工具类源代码
		public Builder setName(String name) {
			this.name = name;
			return this;
		}

工具类代码里的setName()很简洁,将需要保存的联系人姓名赋值给Builder的成员变量,同理后面的一堆setXxx()都是这么实现的

最后调用

//一堆setXxx后跟
.save()
//对应工具类源码
		public void save() {
			build().save(context);
		}
//builder()函数
		private JiPeng build() {
			return new JiPeng(this);
		}
//save函数较复杂,直接去上面看,这里只是为了理清楚调用顺序
//其实save函数才是保存联系人的关键,鼠标滑轮向上滚一滚看看save函数的实现

如果读者跟着我的思路来,并且看了上面的源码,你就会发现,其实Builder类成员变量和JiPeng类成员变量几乎一模一样,Builder的作用就是接收参数,接收完参数之后调用save函数,将Builder接收的参数传给JiPeng,然后才是保存联系人的主逻辑代码。

试想如果我们不用Builder Pattern来构建一个保存联系人的Utils应该怎么构建,我刚开始的大体思路是:

public static void saveContact(String name, String telephone, ......){
	//TODO,保存联系人的主逻辑
}

可是这样面临几个问题:

  1. 参数的顺序,我可能需要接近6,7个参数,如果读者真正写过6,7参数的函数调用就会发现,参数之间的对号入座是很烦的,需要频繁看函数定义才能搞明白参数顺序。
  2. 如果有些参数为空,当然也可以传入NULL,然后在函数内进行判断,但是我看到Builder的实现时我放弃了这种不优雅的做法。

总结

优点:

  1. 客户端不需要知道产品内部组成的细节,只需要传参就OK了。
  2. 可以更加灵活的处理多参数问题,可以理解成将复杂产品的创建步骤分解在不同的方法中

缺点:

  1. 增加了冗余成分,上面代码中可以看出,Builder的成员变量和JiPeng的成员变量十分类似
  2. 如果产品的内部变化复杂, 可能会导致需要定义很多建造者模式来实现这些变化,导致系统复杂性大大提高。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值