Builder 模式

栏目: 后端 · 发布时间: 7年前

内容简介:在开发中,经常用到builder设计模式,但感觉最常见的应用场景就是构造对象参数较多的时候,本文将builder模式梳理总结一下。非要给builder模式一个定义,我就查看了《Android源码设计模式解析与实战》,以下是其给出的定义:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。

在开发中,经常用到builder设计模式,但感觉最常见的应用场景就是构造对象参数较多的时候,本文将builder模式梳理总结一下。

定义

非要给builder模式一个定义,我就查看了《Android源码 设计模式 解析与实战》,以下是其给出的定义:

将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。

这样的总结比较经典,但是感觉离彻底明白其中的含义还差点距离。前半句可以理解为将一个对象的创建过程分多步,后半句可以这样理解,使用同样的构建过程,传递不同的参数会产生不同的结果。

经典写法

Builder 模式

上图为builder经典写法的uml,其实在实际开发过程中,Director部分经常就被去掉了。 如下所示,定义了AbstractBuilder:

public abstract class AbstractBuilder {
    	public abstract void buildPart1(int numOfWheel);
    	public abstract void buildPart2(int numOfSeat);
    	public abstract void buildPart3(int capacity);
    	public abstract Vehicle build();
    }
复制代码

demo的目标是将车辆Vehicle的构建分离,demo中的Product为Vehicle类型,可以看下Vehicle的定义:

public abstract class Vehicle {
        // 车轮的数量
    	protected int numOfWheel;
    	// 座椅的数量
    	protected int numOfSeat;
    	// 载重,car按照人数,truck按照吨数
    	protected int capacity;
    	
    	public void setNumOfWheel(int numOfWheel) {
    		this.numOfWheel = numOfWheel;
    	}
    	public void setNumOfSeat(int numOfSeat) {
    		this.numOfSeat = numOfSeat;
    	}
    	public abstract void setCapacity(int capacity);
    }
复制代码

demo中Vehicle的子类有Car(小轿车)和Truck(卡车),Car类的定义如下:

public class Car extends Vehicle {

    	@Override
    	public void setCapacity(int capacity) {
    		// TODO Auto-generated method stub
    		super.capacity = capacity;
    	}
    	
    	@Override
    	public String toString() {
    		// TODO Auto-generated method stub
    		return "car parameter:" + numOfWheel + "-" + numOfSeat + "-" + capacity;
    	}
    }
复制代码

Truck的定义如下:

public class Truck extends Vehicle {

    	@Override
    	public void setCapacity(int capacity) {
    		// TODO Auto-generated method stub
    		super.capacity = capacity;
    	}
    	
    	@Override
    	public String toString() {
    		// TODO Auto-generated method stub
    		return "car parameter:" + numOfWheel + "-" + numOfSeat + "-" + capacity + " ton";
    	}
    }
复制代码

接下来就看下Car的builder CarBuilder:

public class CarBuilder extends AbstractBuilder {
    	private Car car = new Car();
    	
    	@Override
    	public void buildPart1(int numOfWheel) {
    		// TODO Auto-generated method stub
    		car.setNumOfWheel(numOfWheel);
    	}
    	
    	@Override
    	public void buildPart2(int numOfSeat) {
    		// TODO Auto-generated method stub
    		car.setNumOfSeat(numOfSeat);
    	}
    	
    	public void buildPart3(int capacity) {
    		// TODO Auto-generated method stub
    		car.setCapacity(capacity);
    	}
    	
    	public Vehicle build() {
    		return car;
    	}
    }
复制代码

TruckBuilder定义如下:

public class TruckBuilder extends AbstractBuilder {
    	private Truck truck = new Truck();
    	@Override
    	public void buildPart1(int numOfWheel) {
    		// TODO Auto-generated method stub
    		truck.setNumOfWheel(numOfWheel);
    	}
    
    	@Override
    	public void buildPart2(int numOfSeat) {
    		// TODO Auto-generated method stub
    		truck.setNumOfSeat(numOfSeat);
    	}
    
    	@Override
    	public void buildPart3(int capacity) {
    		// TODO Auto-generated method stub
    		truck.setCapacity(capacity);
    	}
    
    	@Override
    	public Vehicle build() {
    		// TODO Auto-generated method stub
    		return truck;
    	}
    }
复制代码

以上定义了CarBuiler, TruckBuiler。虽然在实际开发中经常会省略掉Director部分,为了演示,demo也定义了Director

public class Director {
    	private AbstractBuilder builder;
    	public Director(AbstractBuilder builder) {
    		this.builder = builder;
    	}
    	public void construct(int numOfWheel, int numOfSeat, int capacity) {
    		if (builder != null) {
    			builder.buildPart1(numOfWheel);
    			builder.buildPart2(numOfSeat);
    			builder.buildPart3(capacity);
    		}
    	}
    }
复制代码

OK,所有需要定义的部分已经完成,接下来就去调用一下:

public class Client {
    	public static void main(String [] args) {
    		AbstractBuilder builder = new CarBuilder();
    		Director director = new Director(builder);
    		director.construct(4, 5, 5);
    		Car car = (Car) builder.build();
    		System.out.println(car);
    		
    		AbstractBuilder builder2 = new TruckBuilder();
    		Director director2 = new Director(builder2);
    		director2.construct(8, 2, 5);
    		Truck truck = (Truck) builder2.build();
    		System.out.println(truck);
    	}
    }
复制代码

程序输入如下: car parameter:4-5-5 truck parameter:8-2-5 ton

demo演示部分将Vehicle的构造过程分3步,执行完3步构建后返回实例对象。

日常写法

上面的demo是经典的写法,但在实际开发中,很少写的那么标准或者那么复杂,大多数情况下builder模式主要是为了防止在构建对象时传递太多的参数。查看下以下demo:

public class Student {
    	private String name;
    	private String nickName;
    	private String sex;
    	private int age;
    	private int weight;
    	private int height;
    	
    	public Student(String name, String nickName, String sex, int age, int weight, int height) {
    		// TODO Auto-generated constructor stub
    		this.name = name;
    		this.nickName = nickName;
    		this.sex = sex;
    		this.age = age;
    		this.weight = weight;
    		this.height = height;
    	}
    	
    	@Override
    	public String toString() {
    		// TODO Auto-generated method stub
    		return "student info:name=" + name + "\n" +
    							"nickname=" + nickName + "\n" +
    							"sex=" + sex + "\n" + 
    							"age=" + age + "\n" + 
    							"weight=" + weight + "\n" +
    							"height=" + height;
    	}
    	
    	public static class Builder {
    		private String name;
    		private String nickName;
    		private String sex;
    		private int age;
    		private int weight;
    		private int height;
    		
    		public Builder name(String name) {
    			this.name = name;
    			return this;
    		}
    		public Builder nickName(String nickName) {
    			this.nickName = nickName;
    			return this;
    		}
    		public Builder sex(String sex) {
    			this.sex = sex;
    			return this;
    		}
    		public Builder age(int age) {
    			this.age = age;
    			return this;
    		}
    		public Builder weight(int weight) {
    			this.weight = weight;
    			return this;
    		}
    		public Builder height(int height) {
    			this.height = height;
    			return this;
    		}
    		public Student build() {
    			return new Student(name, nickName, sex, age, weight, height);
    		}
    	} 
    }
复制代码

以下是测试程序:

public class Client {
    	public static void main(String [] args) {
    		Student student = new Student.Builder().name("rock")
    											   .nickName("store")
    											   .sex("boy")
    											   .age(12)
    											   .weight(60)
    											    .height(176).build();
    		System.out.println(student);
    	}
    }
复制代码

程序运行结果如下:

student info:name=rock
nickname=store
sex=boy
age=12
weight=60
height=176
复制代码

Android中Builder使用场景

在开发过程中,经常使用的builder模式其实就是上文所说的日常写法,Android中最常见的builder模式就是AlertDialog的创建过程了,以下是AlertDialog创建过程的常见写法。

AlertDialog.Builder builder = new AlertDialog.Builder(context)
                                                 .setTitle(title)
                                                 .setView(view)
                                                 .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
                                                    @Override
                                                    public void onClick(DialogInterface dialog, int which) {
                                                    
                                                    }
                                                })
                                                .setNegativeButton(android.R.string.cancel, null);
    builder.create().show();
复制代码

感觉很熟悉,这就是我们最常用的AlertDialog的构建过程。扒一扒源码,由于AlertDialog.Builder的源码较多,就不全部贴出来,感兴趣的同学可以自行看一下。

public static class Builder {
        private final AlertController.AlertParams P;

        
        public Builder(Context context) {
            this(context, resolveDialogTheme(context, ResourceId.ID_NULL));
        }

        
       ......
       
        public Builder setTitle(@StringRes int titleId) {
            P.mTitle = P.mContext.getText(titleId);
            return this;
        }

        
        public Builder setTitle(CharSequence title) {
            P.mTitle = title;
            return this;
        }

       
        public Builder setCustomTitle(View customTitleView) {
            P.mCustomTitleView = customTitleView;
            return this;
        }

        ......

        
        public Builder setMessage(CharSequence message) {
            P.mMessage = message;
            return this;
        }

       public Builder setPositiveButton(@StringRes int textId, final OnClickListener listener) {
            P.mPositiveButtonText = P.mContext.getText(textId);
            P.mPositiveButtonListener = listener;
            return this;
        }
        ......
        
        public AlertDialog create() {
            // Context has already been wrapped with the appropriate theme.
            final AlertDialog dialog = new AlertDialog(P.mContext, 0, false);
            P.apply(dialog.mAlert);
            dialog.setCancelable(P.mCancelable);
            if (P.mCancelable) {
                dialog.setCanceledOnTouchOutside(true);
            }
            dialog.setOnCancelListener(P.mOnCancelListener);
            dialog.setOnDismissListener(P.mOnDismissListener);
            if (P.mOnKeyListener != null) {
                dialog.setOnKeyListener(P.mOnKeyListener);
            }
            return dialog;
        }

    }
复制代码

AlertDialog.Buidler类中定义各种set方法,执行完set方法之后再执行create方法便创建了一个AlertDialog。这应该是一个标准的builder模式了。可以发现AlertDialog.Builder执行set方法,其实就是将set参数复制给了对象P。对象P是什么结构呢?

public static class AlertParams {
        public final Context mContext;
        public final LayoutInflater mInflater;

        public int mIconId = 0;
        public Drawable mIcon;
        public int mIconAttrId = 0;
        public CharSequence mTitle;
        public View mCustomTitleView;
        public CharSequence mMessage;
        public CharSequence mPositiveButtonText;
        public DialogInterface.OnClickListener mPositiveButtonListener;
        public CharSequence mNegativeButtonText;
        public DialogInterface.OnClickListener mNegativeButtonListener;
        public CharSequence mNeutralButtonText;
        public DialogInterface.OnClickListener mNeutralButtonListener;
        public boolean mCancelable;
        public DialogInterface.OnCancelListener mOnCancelListener;
        public DialogInterface.OnDismissListener mOnDismissListener;
        public DialogInterface.OnKeyListener mOnKeyListener;
        public CharSequence[] mItems;
        public ListAdapter mAdapter;
        public DialogInterface.OnClickListener mOnClickListener;
        public int mViewLayoutResId;
        public View mView;
        public int mViewSpacingLeft;
        public int mViewSpacingTop;
        public int mViewSpacingRight;
        public int mViewSpacingBottom;
        public boolean mViewSpacingSpecified = false;
        public boolean[] mCheckedItems;
        public boolean mIsMultiChoice;
        public boolean mIsSingleChoice;
        public int mCheckedItem = -1;
        public DialogInterface.OnMultiChoiceClickListener mOnCheckboxClickListener;
        public Cursor mCursor;
        public String mLabelColumn;
        public String mIsCheckedColumn;
        public boolean mForceInverseBackground;
        public AdapterView.OnItemSelectedListener mOnItemSelectedListener;
        public OnPrepareListViewListener mOnPrepareListViewListener;
        public boolean mRecycleOnMeasure = true;

        ......
    }
复制代码

可以发现,AlertController.AlertParams类型的对象P其实就是存放了构建AlertDialog需要的各种参数。对象P中海油其他函数操作,感兴趣的同学可以去看一下。

将参数保存到P对象,然后执行create函数,创建新的AlertDialog对象,然后P中存放的属性设置给新建的AlertDilaog对象,这样,就完成了AlertDialog的构建。

总结

Builder模式的目标是将复杂对象的创建过程进行分解,使对象的构建与表示分离,使得同样的构建过程可以创建不同的表示。在实际开发过程中,通常是在复杂对象内部申明静态内部类Builder,在Builder中保存复杂对象的属性,然后使用create或者build函数将保存的属性设置给对象。

其实日常开发过程中使用builder模式还没有让我们领略到builer模式的强大,建议参考下这篇文章体会一下: www.cnblogs.com/happyhippy/…


以上所述就是小编给大家介绍的《Builder 模式》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

JavaScript Patterns

JavaScript Patterns

Stoyan Stefanov / O'Reilly Media, Inc. / 2010-09-21 / USD 29.99

What's the best approach for developing an application with JavaScript? This book helps you answer that question with numerous JavaScript coding patterns and best practices. If you're an experienced d......一起来看看 《JavaScript Patterns》 这本书的介绍吧!

MD5 加密
MD5 加密

MD5 加密工具

正则表达式在线测试
正则表达式在线测试

正则表达式在线测试

HSV CMYK 转换工具
HSV CMYK 转换工具

HSV CMYK互换工具