Android建造者模式笔记
来源:互联网 发布:mac cad格式问题 编辑:程序博客网 时间:2024/05/17 05:19
前言
最近在进行Android编程的时候,通过自己的体会和查看学习别人的代码对Android建造者模式有了自己的理解,这里就做一个笔记,以供参考。建造者模式是一种常见的软件设计模式,因为我目前主要在Android平台上进行编程,所以就用Android代码举例子。如果写的不是很详细的话,请见谅。
什么是建造者模式
首先,什么是建造者模式。建造者模式是设计模式的一种,它将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
如下图[百度百科]:
建造者模式一般有四个角色:
- 抽象建造者(Builder)角色:给出一个抽象接口,以规范产品对象的各个组成成分的建造。
- 具体建造者(ConcreteBuilder)角色:担任这个角色的是与应用程序紧密相关的一些类,它们在应用程序调用下创建产品的实例。这个角色要完成的任务包括:1.实现抽象建造者Builder所声明的接口,给出一步一步地完成创建产品实例的操作。2.在建造过程完成后,提供产品的实例。
- 导演者(Director)角色:担任这个角色的类调用具体建造者角色以创建产品对象。
- 产品(Product)角色:产品便是建造中的复杂对象。
一般来说,每有一个产品者,就有一个相应的具体建造者。这些产品应当有一样数目的零件,而每有一个零件就相应地在所有的建造者角色里有一个建造方法。
这些都是一些定义类的语言,后面举例子。
Android例子
Android中AlertDialog是一个建造者模式的例子,可以有着不同的样式和呈现,这样通过Builder就可以有效实现构建和表示的分离。
不过我后面要提的例子是Paginate,它是Github上的一个开源库。它的作用是:Library for creating simple pagination functionality upon RecyclerView and AbsListView。(为AbsListView和RecyclerView 提供简单的分页功能。)
它的构造是:
paginate = Paginate.with(refreshView, this) .setLoadingTriggerThreshold(2) .addLoadingListItem(loadListener != null) .build();
它的主干代码如下:
AbsListViewPaginate.java
package com.paginate.abslistview;import android.database.DataSetObserver;import android.widget.AbsListView;import android.widget.AdapterView;import android.widget.BaseAdapter;import android.widget.HeaderViewListAdapter;import com.paginate.Paginate;public final class AbsListViewPaginate extends Paginate implements EndScrollListener.Callback { private final AbsListView absListView; private final Callbacks callbacks; private EndScrollListener scrollListener; private WrapperAdapter wrapperAdapter; AbsListViewPaginate(AbsListView absListView, Paginate.Callbacks callbacks, int loadingTriggerThreshold, AbsListView.OnScrollListener onScrollListener, boolean addLoadingListItem, LoadingListItemCreator loadingListItemCreator) { this.absListView = absListView; this.callbacks = callbacks; // Attach scrolling listener in order to perform end offset check on each scroll event scrollListener = new EndScrollListener(this); scrollListener.setThreshold(loadingTriggerThreshold); scrollListener.setDelegate(onScrollListener); absListView.setOnScrollListener(scrollListener); if (addLoadingListItem) { BaseAdapter adapter; if (absListView.getAdapter() instanceof BaseAdapter) { adapter = (BaseAdapter) absListView.getAdapter(); } else if (absListView.getAdapter() instanceof HeaderViewListAdapter) { adapter = (BaseAdapter) ((HeaderViewListAdapter) absListView.getAdapter()).getWrappedAdapter(); } else { throw new IllegalStateException("Adapter needs to be subclass of BaseAdapter"); } // Wrap existing adapter with new adapter that will add loading row wrapperAdapter = new WrapperAdapter(adapter, loadingListItemCreator); adapter.registerDataSetObserver(dataSetObserver); ((AdapterView) absListView).setAdapter(wrapperAdapter); } } @Override public void setHasMoreDataToLoad(boolean hasMoreDataToLoad) { if (wrapperAdapter != null) { wrapperAdapter.displayLoadingRow(hasMoreDataToLoad); } } @Override public void onEndReached() { if (!callbacks.isLoading() && !callbacks.hasLoadedAllItems()) { callbacks.onLoadMore(); } } @Override public void unbind() { // Swap back original scroll listener absListView.setOnScrollListener(scrollListener.getDelegateScrollListener()); // Swap back source adapter if (absListView.getAdapter() instanceof WrapperAdapter) { WrapperAdapter wrapperAdapter = (WrapperAdapter) absListView.getAdapter(); BaseAdapter adapter = (BaseAdapter) wrapperAdapter.getWrappedAdapter(); adapter.unregisterDataSetObserver(dataSetObserver); ((AdapterView) absListView).setAdapter(adapter); } } private final DataSetObserver dataSetObserver = new DataSetObserver() { @Override public void onChanged() { wrapperAdapter.displayLoadingRow(!callbacks.hasLoadedAllItems()); wrapperAdapter.notifyDataSetChanged(); } @Override public void onInvalidated() { wrapperAdapter.displayLoadingRow(!callbacks.hasLoadedAllItems()); wrapperAdapter.notifyDataSetInvalidated(); } }; public static class Builder { private final AbsListView absListView; private final Paginate.Callbacks callbacks; private int loadingTriggerThreshold = 5; private AbsListView.OnScrollListener onScrollListener; private boolean addLoadingListItem = true; private LoadingListItemCreator loadingListItemCreator; public Builder(AbsListView absListView, Paginate.Callbacks callbacks) { this.absListView = absListView; this.callbacks = callbacks; } /** * Set the offset from the end of the list at which the load more event needs to be triggered. Default offset * if 5. * * @param threshold number of items from the end of the list. * @return {@link com.paginate.abslistview.AbsListViewPaginate.Builder} */ public Builder setLoadingTriggerThreshold(int threshold) { this.loadingTriggerThreshold = threshold; return this; } /** * Paginate is using OnScrollListener in order to detect when list is scrolled near the end. That means that * internal listener is attached on AbsListView. Since AbsListView can have only one OnScrollListener it is * needed to use this method to add additional OnScrollListener (as delegate). * * @param onScrollListener that will be called when list is scrolled. * @return {@link com.paginate.abslistview.AbsListViewPaginate.Builder} */ public Builder setOnScrollListener(AbsListView.OnScrollListener onScrollListener) { this.onScrollListener = onScrollListener; return this; } /** * Setup loading row. If loading row is used original adapter set on AbsListView will be wrapped with * internal adapter that will add loading row as the last item in the list. Paginate will observer the * changes upon original adapter and remove loading row if there is no more data to load. By default loading * row will be added. * * @param addLoadingListItem true if loading row needs to be added, false otherwise. * @return {@link com.paginate.abslistview.AbsListViewPaginate.Builder} * @see {@link com.paginate.Paginate.Callbacks#hasLoadedAllItems()} * @see {@link com.paginate.abslistview.AbsListViewPaginate.Builder#setLoadingListItemCreator(LoadingListItemCreator)} */ public Builder addLoadingListItem(boolean addLoadingListItem) { this.addLoadingListItem = addLoadingListItem; return this; } /** * Set custom loading list item creator. If no creator is set default one will be used. * * @param loadingListItemCreator Creator that will ne called for inflating and binding loading list item. * @return {@link com.paginate.abslistview.AbsListViewPaginate.Builder} */ public Builder setLoadingListItemCreator(LoadingListItemCreator loadingListItemCreator) { this.loadingListItemCreator = loadingListItemCreator; return this; } /** * Create pagination functionality upon AbsListView. * * @return {@link Paginate} instance. */ public Paginate build() { if (absListView.getAdapter() == null) { throw new IllegalStateException("Adapter needs to be set!"); } if (loadingListItemCreator == null) { loadingListItemCreator = LoadingListItemCreator.DEFAULT; } return new AbsListViewPaginate(absListView, callbacks, loadingTriggerThreshold, onScrollListener, addLoadingListItem, loadingListItemCreator); } }}
Paginate.java
package com.paginate;import android.support.v7.widget.RecyclerView;import android.widget.AbsListView;import com.paginate.abslistview.AbsListViewPaginate;import com.paginate.recycler.RecyclerPaginate;public abstract class Paginate { public interface Callbacks { /** Called when next page of data needs to be loaded. */ void onLoadMore(); /** * Called to check if loading of the next page is currently in progress. While loading is in progress * {@link com.paginate.Paginate.Callbacks#onLoadMore} won't be called. * * @return true if loading is currently in progress, false otherwise. */ boolean isLoading(); /** * Called to check if there is more data (more pages) to load. If there is no more pages to load, {@link * com.paginate.Paginate.Callbacks#onLoadMore} won't be called and loading row, if used, won't be added. * * @return true if all pages has been loaded, false otherwise. */ boolean hasLoadedAllItems(); } /** * Use this method to indicate that there is or isn't more data to load. If there isn't any more data to load * loading row, if used, won't be displayed as the last item of the list. Adding/removing loading row is done * automatically each time when underlying adapter data is changed. Use this method to explicitly add/remove * loading row. * * @param hasMoreDataToLoad true if there is more data to load, false otherwise. */ abstract public void setHasMoreDataToLoad(boolean hasMoreDataToLoad); /** * Call unbind to detach list (RecyclerView or AbsListView) from Paginate when pagination functionality is no * longer needed on the list. * <p/> * Paginate is using scroll listeners and adapter data observers in order to perform required checks. It wraps * original (source) adapter with new adapter that provides loading row if loading row is used. When unbind is * called original adapter will be set on the list and scroll listeners and data observers will be detached. */ abstract public void unbind(); /** * Create pagination functionality upon RecyclerView. * * @param recyclerView RecyclerView that will have pagination functionality. * @param callback pagination callbacks. * @return {@link com.paginate.recycler.RecyclerPaginate.Builder} */ public static RecyclerPaginate.Builder with(RecyclerView recyclerView, Callbacks callback) { return new RecyclerPaginate.Builder(recyclerView, callback); } /** * Create pagination functionality upon AbsListView. * * @param absListView AbsListView that will have pagination functionality (ListView or GridView). * @param callback pagination callbacks. * @return {@link com.paginate.abslistview.AbsListViewPaginate.Builder} */ public static AbsListViewPaginate.Builder with(AbsListView absListView, Callbacks callback) { return new AbsListViewPaginate.Builder(absListView, callback); }}
由上面贴出的大段代码可以看出:
这个构建方法使用的是建造者模式。通过Builder来构建产品对象, 不对外隐藏构建细节。
Builder为AbsListViewPaginate的内部类,而AbsListViewPaginate是继承自Paginate的子类。
Product:
一般Product角色都是如下:
/** * Computer产品抽象类, 为了例子简单, 只列出这几个属性 * * @author mrsimple * */public abstract class Computer { protected int mCpuCore = 1; protected int mRamSize = 0; protected String mOs = "Dos"; protected Computer() { } // 设置CPU核心数 public abstract void setCPU(int core); // 设置内存 public abstract void setRAM(int gb); // 设置操作系统 public abstract void setOs(String os);}
但是在Paginate类中,它直接将成员变量定义在了Builder类里,如下:
public static class Builder { private final AbsListView absListView; private final Paginate.Callbacks callbacks; private int loadingTriggerThreshold = 5; private AbsListView.OnScrollListener onScrollListener; private boolean addLoadingListItem = true; private LoadingListItemCreator loadingListItemCreator;
所以这一部分产品应该没有单独列出来。
builder角色:
一般builder角色都如下:
/** * builder抽象类 * */public abstract class Builder { // 设置CPU核心数 public abstract void buildCPU(int core); // 设置内存 public abstract void buildRAM(int gb); // 设置操作系统 public abstract void buildOs(String os); // 创建Computer public abstract Computer create();}
但是在Paginate类中,也同样是在Builder类里做了处理,并没有单独写出来:
public Builder setLoadingListItemCreator(LoadingListItemCreator loadingListItemCreator) { this.loadingListItemCreator = loadingListItemCreator; return this; } /** * Create pagination functionality upon AbsListView. * * @return {@link Paginate} instance. */ public Paginate build() { if (absListView.getAdapter() == null) { throw new IllegalStateException("Adapter needs to be set!"); } if (loadingListItemCreator == null) { loadingListItemCreator = LoadingListItemCreator.DEFAULT; } return new AbsListViewPaginate(absListView, callbacks, loadingTriggerThreshold, onScrollListener, addLoadingListItem, loadingListItemCreator); } }
具体建造者:
具体建造者应该是这里的builder类:它为成员变量赋值,并最后用builder方法创建出了当前类的对象。
导演者:
导演者一般是最后在客户端调用的时候面对客户端的一个角色,但是它通常可以省略。如下:
public class Director { Builder mBuilder = null; /** * * @param builder */ public Director(Builder builder) { mBuilder = builder; } /** * 构建对象 * * @param cpu * @param ram * @param os */ public void construct(int cpu, int ram, String os) { mBuilder.buildCPU(cpu); mBuilder.buildRAM(ram); mBuilder.buildOs(os); }}
但是在Paginate类中,这一部分被省略了,而直接使用build方法构建了对象。
其实写这么多,主要是为了记忆怎么使用构造者模式,它有着非常明显的优势:良好的封装性, 使用建造者模式可以使客户端不必知道产品内部组成的细节。写这篇笔记也是为了让我自己能更好地了解如何使用构造者模式,同时也是对Paginate类的一个简单的分析,如果写的不详细的话,见谅~
参考:
https://github.com/simple-android-framework/android_design_patterns_analysis/tree/master/builder/mr.simple
- Android建造者模式笔记
- Android建造者模式
- 建造者模式(学习笔记)
- 建造者模式学习笔记
- 建造者模式学习笔记
- 建造者模式学习笔记
- Android开发笔记(九十)建造者模式
- Android 建造者(Builder)模式
- android 建造者设计模式
- 设计模式笔记之---建造者模式
- 3.建造者模式(设计模式笔记)
- 设计模式学习笔记-建造者模式
- 设计模式学习笔记--建造者模式
- 设计模式笔记--建造者模式
- 设计模式学习笔记--建造者模式
- 设计模式笔记之----建造者模式
- 设计模式笔记四:建造者模式
- Java建造者模式,Android建造者模式的AlertDialog
- STL-priority_queue用法(重点: 升序,小根堆)
- setInterval/setTimeout特殊用法
- Idea常用插件列表
- 欢迎使用CSDN-markdown编辑器
- 深入理解jvm--自动内存管理机制
- Android建造者模式笔记
- Linux学习---帮助命令
- ok6410中断架构伪代码
- Java lombok
- vmware虚拟机上运行ubuntu14没声音解决方法之一
- MySQL5.7版本以后的初始密码问题
- 小技巧
- 纯洁
- JS原生addClass、removeClass实现