Builder 模式
Posted gxl1995
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Builder 模式相关的知识,希望对你有一定的参考价值。
概述
建造者模式(Builder Pattern),是创造性模式之一,Builder 模式的目的则是为了将对象的构建与展示分离。Builder 模式是一步一步创建一个复杂对象的创建型模式,它允许用户在不知道内部构建细节的情况下,可以更精细地控制对象的构造流程。
定义
将一个复杂对象的构建与它的表示分离,使同样的构建过程可以创建不同的表示。
使用场景
- 相同的方法,不同的执行顺序,产生不同的事件结果时;
- 多个部件或零件,都可以装配到一个对象中,但是产生的运行结果又不相同时;
- 产品类非常复杂,或者产品类中的调用顺序不同产生了不同的效能,这个时候使用建造者模式非常合适。
- 当初始化一个对象特别复杂,比如参数非常多,且很多参数都具有默认值时。
uml图
角色介绍:
Builder(抽象建造者)
它为创建一个产品对象的各个部件指定抽象接口,在该接口中一般声明两类方法,一类方法是buildXXX(),它们用于创建复杂对象的各个部件;另一类方法是getXXX(),它们用于返回复杂对象。Builder既可以是抽象类,也可以是接口。ConcreteBuilder(具体建造者)
它实现了Builder接口,实现各个部件的具体构造和装配方法,定义并明确它所创建的复杂对象,也可以提供一个方法返回创建好的复杂产品对象。Product(产品角色)
它是被构建的复杂对象,包含多个组成部件,具体建造者ConcreteBuilder创建该产品的内部表示并定义它的装配过程。Director(指挥者)
指挥者又称为导演类,它负责安排复杂对象的建造次序,指挥者与抽象建造者之间存在关联关系,可以在其construct()建造方法中调用建造者对象的部件构造与装配方法,完成复杂对象的建造。客户端一般只需要与指挥者进行交互,在客户端确定具体建造者的类型,并实例化具体建造者对象,然后通过指挥者类的构造函数或者Setter方法将该对象传入指挥者类中。
例子
这里就以经典的组装电脑为例,我们把电脑的组装过程分为构建主机,设置显示器,设置操作系统三个部分。
抽象product:
/**
* 计算机抽象类,Product角色
*/
public abstract class Computer {
private String mBoard;
private String mDisplay;
public String mOS;
protected Computer() {
}
/**
* 设置主板
*/
public void setBoard(String board) {
mBoard = board;
}
/**
* 设置显示器
*/
public void setDisplay(String display) {
mDisplay = display;
}
/**
* 设置操作系统
*/
public abstract void setOS();
@Override
public String toString() {
return "Computer{" +
"mBoard=‘" + mBoard + ‘\\‘‘ +
", mDisplay=‘" + mDisplay + ‘\\‘‘ +
", mOS=‘" + mOS + ‘\\‘‘ +
‘}‘;
}
}
具体的product:
/**
* 具体的Computer类,xiaomicomputer
*/
public class XiaMiComputer extends Computer {
@Override
public void setOS() {
mOS = "Window 10";
}
}
抽象builder
public abstract class Builder {
/**
* 设置主板
*/
public abstract void buildBoard(String board);
/**
* 设置显示器
*/
public abstract void buildDisplay(String display);
/**
* 设置操作系统
*/
public abstract void buildOS();
/**
* 创建电脑
*/
public abstract Computer create();
}
具体的builder
public class ComputerBuilder extends Builder {
private Computer mComputer=new XiaMiComputer();
@Override
public void buildBoard(String board) {
mComputer.setBoard(board);
}
@Override
public void buildDisplay(String display) {
mComputer.setDisplay(display);
}
@Override
public void buildOS() {
mComputer.setOS();
}
@Override
public Computer create() {
return mComputer;
}
}
Director类,负责构造Computer
public class Director {
private final Builder mBuilder;
public Director(Builder builder) {
mBuilder = builder;
}
public void construct(String board, String display) {
mBuilder.buildBoard(board);
mBuilder.buildDisplay(display);
mBuilder.buildOS();
}
}
main方法中测试
public static void main(String[] args) {
Builder builder = new ComputerBuilder();
Director director = new Director(builder);
director.construct("因特尔主板", "retina显示器");
System.out.println("::"+builder.create().toString());
}
总结
上面就是经典的builder设计模式,但是在实际开发过程中,Director经常会被省略。直接使用一个Builder 来进行对象的组装,这个Builder 通常为链式调用,它的关键点是每一个 setter 方法都返回自身,也就是return this,这样使得setter方法可以链式调用,代码如下。
new TestBuilder().setA("A").setB("B").create();
通过这种形式不仅去除了Director角色,整个结构也更加简单,也能对Product对象的组装过程由更精细的控制。
实战
在android中用到builder模式的有很多,其中最著名的可能就是AlertDialog,开源中的有Okhttp等都用到了builder设计模式,具体的源码自己去看吧!
这次就实战就通过Builder模式来打造通用的navigationbar,就是我们项目中经常用到的toolbar,我们通过链式调用可以达到可以一行代码实现任何toolbar效果。
先画一下uml图
INavigation
public interface INavigation {
/**
* 创建navigation bar view
*/
View createView();
/**
* 添加到父布局
*/
void attachParent(View parentView);
/**
* 设置参数
*/
void addParams();
}
AbsNavigation,这个是基础的navigation,其他的扩展navigation都要继承它
public class AbsNavigation implements INavigation {
private Builder mBuilder;
private View mNavigationView;
protected AbsNavigation(Builder builder) {
this.mBuilder = builder;
View navigationView = createView();
attachParent(navigationView);
addParams();
}
public void addParams() {
//设置点击事件
Map<Integer, View.OnClickListener> clickListeners = mBuilder.mClickListeners;
for (Map.Entry<Integer, View.OnClickListener> entry : clickListeners.entrySet()) {
View view = findViewById(entry.getKey());
view.setOnClickListener(entry.getValue());
}
//处理设置text
Map<Integer, CharSequence> texts = mBuilder.mTexts;
for (Map.Entry<Integer, CharSequence> entry : texts.entrySet()) {
TextView textView = (TextView) findViewById(entry.getKey());
textView.setText(entry.getValue());
}
}
/**
* 根据id查找控件
*/
public View findViewById(int viewId) {
View view = mNavigationView.findViewById(viewId);
return view;
}
/**
* 添加到父容器中
*/
public void attachParent(View navigationView) {
ViewGroup parentView = mBuilder.mParentView;
if (parentView == null) {
// 获取activity的根布局,View源码
ViewGroup rootView = ((Activity) (mBuilder.mContext))
.findViewById(android.R.id.content);
//获取根布局
parentView = (ViewGroup) rootView.getChildAt(0);
parentView.addView(navigationView, 0);
} else {
parentView.addView(navigationView, 0);
}
}
/**
* 创建NavigationView
*
* @return
*/
public View createView() {
mNavigationView = LayoutInflater.from(mBuilder.mContext).inflate(mBuilder.mLayoutId, null);
return mNavigationView;
}
/**
* 获取builder
*/
public Builder getBuilder() {
return mBuilder;
}
public static abstract class Builder<T extends Builder> {
private Context mContext;
private ViewGroup mParentView;
private int mLayoutId;
private Map<Integer, View.OnClickListener> mClickListeners;
private Map<Integer, CharSequence> mTexts;
public Builder(Context context, int layoutId, ViewGroup parentView) {
this.mContext = context;
this.mLayoutId = layoutId;
this.mParentView = parentView;
if (mClickListeners == null) {
mClickListeners = new HashMap<>();
}
if (mTexts == null) {
mTexts = new HashMap<>();
}
}
public Builder(Context context, int layoutId) {
this(context, layoutId, null);
}
/**
* 构造
*/
public abstract T create();
/**
* 设置view的点击事件
*/
public T setOnClickListener(int viewId, View.OnClickListener listener) {
mClickListeners.put(viewId, listener);
return (T) this;
}
/**
* 给text控件设置文本
*/
public T setText(int viewId, CharSequence text) {
mTexts.put(viewId, text);
return (T) this;
}
}
}
万能头部布局的NaviagtionBar,当自定义布局时将布局id传入进来
package com.gxl.day9.navigation;
import android.content.Context;
import android.view.ViewGroup;
/**
* Created by Administrator on 2018/5/10/010.
* 指定布局的navigation bar
*/
public class NavigationBar extends AbsNavigation {
protected NavigationBar(Builder builder) {
super(builder);
}
@Override
public void addParams() {
super.addParams();
//设置自定义属性
}
public static class Builder extends AbsNavigation.Builder<Builder> {
public Builder(Context context, int layoutId, ViewGroup parentView) {
super(context, layoutId, parentView);
}
public Builder(Context context, int layoutId) {
super(context, layoutId);
}
@Override
public Builder create() {
NavigationBar navigationBar = new NavigationBar(this);
return this;
}
}
}
注意的功能,在AbsNavigation中都写好了,还有一些需要扩展的可以在addParams方法中进行设置
一般情况下都可以使用的DefaultNavigation
import android.content.Context;
import android.view.View;
import android.view.ViewGroup;
import com.gxl.day9.R;
/**
* Created by Administrator on 2018/5/11/011.
*/
public class DefaultNavigation extends AbsNavigation {
protected DefaultNavigation(Builder builder) {
super(builder);
}
@Override
public void addParams() {
super.addParams();
//处理自己的逻辑
Builder builder= (Builder) getBuilder();
if (builder.mIsHideRight) {
findViewById(R.id.right_icon).setVisibility(View.GONE);
}
}
public static class Builder extends AbsNavigation.Builder<Builder> {
private Boolean mIsHideRight;
public Builder(Context context) {
super(context, R.layout.navigation_bar);
}
public Builder(Context context, ViewGroup parentView) {
super(context, R.layout.navigation_bar, parentView);
}
@Override
public Builder create() {
new DefaultNavigation(this);
return this;
}
/**
* 隐藏右边
*/
public Builder hideRight() {
mIsHideRight = true;
return this;
}
}
}
可以看到,在Builder方法中,添加一些新的功能,在addParams方法中对这些功能进行处理。
附件列表
以上是关于Builder 模式的主要内容,如果未能解决你的问题,请参考以下文章
Builder设计模式,模板设计模式,Adapter设计模式笔记