前言
在高仿Pinterest交互的实现思路这篇文章中,其实对于封装后的入口类,是需要提供很多参数的,那对于需要设置很多参数的类,我们如果全部在方法中提供set方法来设置,那将会显得非常凌乱,这个时候应该要将提供参数的方法封装起来作为一个单独的类,然后供给需要这些参数的产品类,这样可以统一提供参数入口,让这个本来需要参数就多的类减少与外部的调用关系,即方便日后扩展自身功能又不影响到与外部的调用
简物中的Builder模式
高仿Pinterest交互入口类在没有使用Builder模式是怎么传参的呢?以下为部分代码
public class PinterestSelector implements PinterestSelectorContent.OnTouchToSelectorListener, PinterestSelectorContent.OnItemSelectListener{
int mScrollViewId;
View mView;
View mItemLayout;
boolean onlyShowItemLayout;
String mShowItemViewBackgroundColor;
List<ITouchView> mITouchViews;
....
private PinterestSelector(){
}
/**
* 该方法调用不存在线程安全问题
*/
public static PinterestSelector newInstance(){
if(instance == null){
instance = new PinterestSelector();
}
return instance;
}
public PinterestSelector scroll(int scrollviewId){
this.mScrollViewId = scrollviewId;
return this;
}
/**
* 设置长按item时候周边的背景色
* eg: #f2ffffff
* @param color
* @return
*/
public PinterestSelector backgroundColor(String color){
this.mShowItemViewBackgroundColor = color;
return this;
}
public PinterestSelector show(View itemLayout){
this.mItemLayout = itemLayout;
this.onlyShowItemLayout = true;
return this;
}
public PinterestSelector addITouchView(ITouchView iTouchView){
if(mITouchViews == null){
mITouchViews = new ArrayList<>();
}
this.mITouchViews.add(iTouchView);
return this;
}
public PinterestSelector addITouchViews(List<ITouchView> iTouchViews){
this.mITouchViews.clear();
this.mITouchViews.addAll(iTouchViews);
return this;
}
....
}
调用方式为
@Override
public boolean onTouch(View v, MotionEvent event) {
if(!v.isClickable()){
return false;
}
PinterestSelector.newInstance().
show((View)v.getParent()).
scroll(getBindId()).
backgroundColor("#f0ffffff").
addITouchView(new ShoppingCartPinterestViewFactory().create()).
addITouchView(new LikePinterestViewFactory().withLike(getAdapter().getItem(position).isLike()).create()).
setOnLongClickListener(new PinterestSelector.OnLongClickListener() {
@Override
public void onLongClick(View v) {
handleLongClick();
}
}).
setOnCancelListener(new PinterestSelector.OnCancelListener() {
@Override
public void onCancel() {
}
@Override
public void onSyncCancel() {
handleSyncCancel();
}
}).
setOnItemSelectListener(new PinterestSelector.OnItemSelectListener() {
@Override
public void onItemSelect(int index) {
switch (index){
case IPinterestView.LIKE:
handleCollectSelect();
break;
case IPinterestView.CART:
handleSlideToCart();
break;
}
}
}).
onTouch(v, event);
}
其实也是链式编程调用形式,但这样有什么弊端呢,我需要在PinterestSelector产品类维护大量供外界调用的方法,如果哪个函数中赋值变量的地方需要修改,那大量调用这个类的地方都要修改,这不是我所希望的,我希望在我维护PinterestSelector产品的时候不因为内部功能的修改而牵扯到外部类的调用,类与类之间调用关系越密切,其耦合度约会越来越大,我们学习编程的时候总是听到什么“低耦合、高内聚”,无论是做什么功能,各个模块之间的耦合度应该尽量降低,六大设计原则中有一条叫“迪米特法则”,另一种叫法叫“最少知道原则”说的就是这个意思
那对于这种情况我们应该怎么做呢,我们应该单独建一个类,作为建造者类提供外界设置参数,然后将设置好的参数“打包”给产品类,这样外界与产品之间的供给关系就交给建造者去完成了,产品做了什么内部修改,都不影响参数的传递,如果后面有什么新的参数增加,那也只需要在建造者内添加即可,打包的入口不用做任何修改,添加赋值即可
那我们应该这样修改代码:
public class PinterestSelector implements PinterestSelectorContent.OnTouchToSelectorListener, PinterestSelectorContent.OnItemSelectListener{
int mScrollViewId;
View mView;
View mItemLayout;
boolean onlyShowItemLayout;
String mShowItemViewBackgroundColor;
List<ITouchView> mITouchViews;
....
private PinterestSelector(){
}
/**
* 该方法调用不存在线程安全问题
*/
public static PinterestSelector newInstance(){
if(instance == null){
instance = new PinterestSelector();
}
return instance;
}
public PinterestSelector with(Builder builder){
this.mItemLayout = builder.mItemView;
this.mScrollViewId = builder.mScrollViewId;
this.mShowItemViewBackgroundColor = builder.mColor;
this.mITouchViews = builder.mITouchViews;
this.onLongClickListener = builder.mOnLongClickListener;
this.onCancelListener = builder.mOnCancelListener;
this.onItemSelectListener = builder.mOnItemSelectListener;
this.mDialogMode = builder.mDialogMode;
return this;
}
public static class Builder {
/**
* Dialog模式
*/
private boolean mDialogMode;
/**
* 空白部分之外要显示的View
*/
private View mItemView;
/**
* 当前选中view外层可滚动的view(如果没有就是Activity)
*/
private int mScrollViewId;
/**
* 不显示区域背景色
*/
private String mColor;
/**
* TouchViews
*/
private List<IPinterestView> mITouchViews;
/**
* 长按监听
*/
private PinterestSelector.OnLongClickListener mOnLongClickListener;
/**
* 选择监听
*/
private OnItemSelectListener mOnItemSelectListener;
/**
* 退出监听
*/
private OnCancelListener mOnCancelListener;
public Builder(){
mITouchViews = new ArrayList<>();
}
/**
* 空白部分外部的view
* @param itemView
*/
public Builder show(View itemView){
this.mItemView = itemView;
return this;
}
public Builder scroll(int viewId){
this.mScrollViewId = viewId;
return this;
}
public Builder backgroundColor(String color){
this.mColor = color;
return this;
}
public Builder addITouchView(IPinterestView iTouchView){
if(mITouchViews != null){
mITouchViews.add(iTouchView);
}
return this;
}
public Builder setOnLongClickListener(OnLongClickListener onLongClickListener){
this.mOnLongClickListener = onLongClickListener;
return this;
}
public Builder setOnCancelListener(OnCancelListener onCancelListener){
this.mOnCancelListener = onCancelListener;
return this;
}
public Builder setOnItemSelectListener(PinterestSelector.OnItemSelectListener onItemSelectListener){
this.mOnItemSelectListener = onItemSelectListener;
return this;
}
public Builder dialogMode() {
this.mDialogMode = true;
return this;
}
public PinterestSelector create(){
return PinterestSelector.newInstance().with(this);
}
....
}
那外部的调用就变成这样了
@Override
public boolean onTouch(View v, MotionEvent event) {
if(!v.isClickable()){
return false;
}
new PinterestSelector.Builder()
.show((View)v.getParent())
.scroll(getBindId())
.backgroundColor("#f0ffffff")
.dialogMode()
.addITouchView(new ShoppingCartPinterestViewFactory().create())
.addITouchView(new LikePinterestViewFactory().withLike(getAdapter().getItem(position).isLike()).create())
.setOnLongClickListener(new PinterestSelector.OnLongClickListener() {
@Override
public void onLongClick(View v) {
handleLongClick();
}
})
.setOnCancelListener(new PinterestSelector.OnCancelListener() {
@Override
public void onCancel() {
}
@Override
public void onSyncCancel() {
handleSyncCancel();
}
})
.setOnItemSelectListener(new PinterestSelector.OnItemSelectListener() {
@Override
public void onItemSelect(int index) {
switch (index){
case IPinterestView.LIKE:
handleCollectSelect();
break;
case IPinterestView.CART:
handleSlideToCart();
break;
}
}
})
.create()
.onTouch(v, event);
return super.onTouch(v, event);
}
这样一修改是不是觉得这个功能的两个模块非常清晰,传参的模块只负责接收外部类提供的参数,而产品类只需要给参数赋值并且做自己的产品实现,其它的与外界没有任何关联,这就是Builder模式的巧妙之处!
Android中有什么地方用到了Builder模式呢,最常见的就是AlertDialog,在AlertDialog使用中我们这样写过(以下代码来自网络)
protected void showDialog() {
AlertDialog.Builder builder = new Builder(Main.this);
builder.setMessage("确认退出吗?");
builder.setTitle("提示");
builder.setPositiveButton("确认", new OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
Main.this.finish();
}
});
builder.setNegativeButton("取消", new OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
}
});
builder.create().show();
}
还有OkHttp的创建也用到了Builder模式
OkHttpClient okHttpClient = new OkHttpClient.Builder()
.cache(getCache())
.addInterceptor(new HttpCacheInterceptor())
.addInterceptor(new LogInterceptor())
.addNetworkInterceptor(new HttpRequestInterceptor())
.build();
好了,现在你对Builder(建造者)模式应该也有一个大概的了解了,如果你喜欢这篇文章,那请不要吝啬给个like吧!