Android设计模式之Builder模式解析
作者:xxq2dream 发布时间:2022-02-11 20:07:46
在日常开发过程中时常需要用到设计模式,但是设计模式有23种,如何将这些设计模式了然于胸并且能在实际开发过程中应用得得心应手呢?和我一起跟着《Android源码设计模式解析与实战》一书边学边应用吧!
今天我们要讲的是Builder模式(建造者模式)
定义
将一个复杂对象的构建和它的表示分离,使得同样的构建过程可以创建不同的表示
使用场景
当初始化一个对象特别复杂时,如参数多,且很多参数都具有默认值时
相同的方法,不同的执行顺序,产生不同的事件结果时
多个部件或零件,都可以装配到一个对象中,但是产生的运行效果又不相同时
产品类非常复杂,或者产品类中的调用顺序不同产生了不同的作用,这个时候使用建造者模式非常合适
使用例子
AlertDialog
universal-image-loader
实现
实现的要点
简言之,就是把需要通过set方法来设置的多个属性封装在一个配置类里面
每个属性都应该有默认值
具体的set方法放在配置类的内部类Builder类中,并且每个set方法都返回自身,以便进行链式调用
实现方式
下面以我们的图片加载框架ImageLoder为例来看看Builder模式的好处
未采用Builder模式的ImageLoader
public class ImageLoader {
//图片加载配置
private int loadingImageId;
private int loadingFailImageId;
// 图片缓存,依赖接口
ImageCache mImageCache = new MemoryCache();
// 线程池,线程数量为CPU的数量
ExecutorService mExecutorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
//省略单例模式实现
/**
* 设置图片缓存
* @param cache
*/
public void setImageCache(ImageCache cache) {
mImageCache = cache;
}
/**
* 设置图片加载中显示的图片
* @param resId
*/
public Builder setLoadingPlaceholder(int resId) {
loadingImageId = resId;
}
/**
* 设置加载失败显示的图片
* @param resId
*/
public Builder setLoadingFailPlaceholder(int resId) {
loadingFailImageId = resId;
}
/**
* 显示图片
* @param imageUrl
* @param imageView
*/
public void displayImage(String imageUrl, ImageView imageView) {
Bitmap bitmap = mImageCache.get(imageUrl);
if (bitmap != null) {
imageView.setImageBitmap(bitmap);
return;
}
// 图片没有缓存,提交到线程池下载
submitLoadRequest(imageUrl, imageView);
}
/**
* 下载图片
* @param imageUrl
* @param imageView
*/
private void submitLoadRequest(final String imageUrl, final ImageView imageView) {
imageView.setImageResource(loadingImageId);
imageView.setTag(imageUrl);
mExecutorService.submit(new Runnable() {
@Override
public void run() {
Bitmap bitmap = downloadImage(imageUrl);
if (bitmap == null) {
imageView.setImageResource(loadingFailImageId);
return;
}
if (imageUrl.equals(imageView.getTag())) {
imageView.setImageBitmap(bitmap);
}
mImageCache.put(imageUrl, bitmap);
}
});
}
/**
* 下载图片
* @param imageUrl
* @return
*/
private Bitmap downloadImage(String imageUrl) {
Bitmap bitmap = null;
//省略下载部分代码
return bitmap;
}
}
从上面的代码中我们可以看出,每当需要增加一个设置选项的时候,就需要修改ImageLoader的代码,违背了开闭原则,而且ImageLoader中的代码会越来越多,不利于维护
下面我们来看看如何用Builder模式来改造ImageLoader
首先是把ImageLoader的设置都放在单独的配置类里,每个set方法都返回this,从而达到链式调用的目的
public class ImageLoaderConfig {
// 图片缓存,依赖接口
public ImageCache mImageCache = new MemoryCache();
//加载图片时的loading和加载失败的图片配置对象
public DisplayConfig displayConfig = new DisplayConfig();
//线程数量,默认为CPU数量+1;
public int threadCount = Runtime.getRuntime().availableProcessors() + 1;
private ImageLoaderConfig() {
}
/**
* 配置类的Builder
*/
public static class Builder {
// 图片缓存,依赖接口
ImageCache mImageCache = new MemoryCache();
//加载图片时的loading和加载失败的图片配置对象
DisplayConfig displayConfig = new DisplayConfig();
//线程数量,默认为CPU数量+1;
int threadCount = Runtime.getRuntime().availableProcessors() + 1;
/**
* 设置线程数量
* @param count
* @return
*/
public Builder setThreadCount(int count) {
threadCount = Math.max(1, count);
return this;
}
/**
* 设置图片缓存
* @param cache
* @return
*/
public Builder setImageCache(ImageCache cache) {
mImageCache = cache;
return this;
}
/**
* 设置图片加载中显示的图片
* @param resId
* @return
*/
public Builder setLoadingPlaceholder(int resId) {
displayConfig.loadingImageId = resId;
return this;
}
/**
* 设置加载失败显示的图片
* @param resId
* @return
*/
public Builder setLoadingFailPlaceholder(int resId) {
displayConfig.loadingFailImageId = resId;
return this;
}
void applyConfig(ImageLoaderConfig config) {
config.displayConfig = this.displayConfig;
config.mImageCache = this.mImageCache;
config.threadCount = this.threadCount;
}
/**
* 根据已经设置好的属性创建配置对象
* @return
*/
public ImageLoaderConfig create() {
ImageLoaderConfig config = new ImageLoaderConfig();
applyConfig(config);
return config;
}
}
}
ImageLoader的修改
public class ImageLoader {
//图片加载配置
ImageLoaderConfig mConfig;
// 图片缓存,依赖接口
ImageCache mImageCache = new MemoryCache();
// 线程池,线程数量为CPU的数量
ExecutorService mExecutorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
//省略单例模式实现
//初始化ImageLoader
public void init(ImageLoaderConfig config) {
mConfig = config;
mImageCache = mConfig.mImageCache;
}
/**
* 显示图片
* @param imageUrl
* @param imageView
*/
public void displayImage(String imageUrl, ImageView imageView) {
Bitmap bitmap = mImageCache.get(imageUrl);
if (bitmap != null) {
imageView.setImageBitmap(bitmap);
return;
}
// 图片没有缓存,提交到线程池下载
submitLoadRequest(imageUrl, imageView);
}
/**
* 下载图片
* @param imageUrl
* @param imageView
*/
private void submitLoadRequest(final String imageUrl, final ImageView imageView) {
imageView.setImageResource(mConfig.displayConfig.loadingImageId);
imageView.setTag(imageUrl);
mExecutorService.submit(new Runnable() {
@Override
public void run() {
Bitmap bitmap = downloadImage(imageUrl);
if (bitmap == null) {
imageView.setImageResource(mConfig.displayConfig.loadingFailImageId);
return;
}
if (imageUrl.equals(imageView.getTag())) {
imageView.setImageBitmap(bitmap);
}
mImageCache.put(imageUrl, bitmap);
}
});
}
/**
* 下载图片
* @param imageUrl
* @return
*/
private Bitmap downloadImage(String imageUrl) {
Bitmap bitmap = null;
//省略下载部分代码
return bitmap;
}
}
调用形式,是不是很熟悉?
ImageLoaderConfig config = new ImageLoaderConfig.Builder()
.setImageCache(new MemoryCache())
.setThreadCount(2)
.setLoadingFailPlaceholder(R.drawable.loading_fail)
.setLoadingPlaceholder(R.drawable.loading)
.create();
ImageLoader.getInstance().init(config);
总结
在构建的对象需要很多配置的时候可以考虑Builder模式,可以避免过多的set方法,同时把配置过程从目标类里面隔离出来,代码结构更加清晰
Builder模式比较常用的实现形式是通过链式调用实现,这样更简洁直观
源码地址:https://github.com/snowdream1314/ImageLoader
来源:http://blog.csdn.net/myth13141314/article/details/78187781
猜你喜欢
- 导语在使用flutter 自带图片组件的过程中,大家有没有考虑过flutter是如何加载一张网络图片的? 以及对自带的图片组件我们可以做些什
- 带占位符的 URL 是 Spring3.0 新增的功能,该功能在SpringMVC 向 REST 目标挺进发展过程中具有里程碑的意义。通过
- Model与Session区别什么是Session:Session:在计算机中,尤其是在网络应用中,称为“会话”。它具体是指一个终端用户与交
- 在SpringMVC中 我们说到了 * , 它会在映射处理器(HandleMapping)执行时检查我们访问的地址是否配置拦截
- SpringBoot多线程进行异步请求的处理近期在协会博客园中,有人发布了博客,系统进行查重的时候由于机器最低配置进行大量计算时需要十秒左右
- 什么是异常?最简单的,看一个代码示例:public static void main(String[] args) { &nb
- 关闭 IDEA 的自动检查更新(截图idea 2020 2.x)idea 右下角会有这样的更新提示2. 关闭 idea 自动检查更新取消勾选
- 委托:顾名思义,让别人帮你办件事。委托是C#实现回调函数的一种机制。可能有人会问了,回调函数是个啥???举个例子:我现在是一家公司的老板,公
- WORD: import org.apache.lucene.document.Document; import org.apache.lu
- 一、程序的三种结构顺序结构分支结构循环结构二、条件语句if 语句是最有用的控制结构之一。 if … else …语句的语法:if (布尔表达
- MyBatis Xml映射文件字符串替换字符串替换默认情况下,使用 #{} 格式的语法会导致 MyBatis 创建 PreparedStat
- 项目需要用到验证用户手机号码输入是否合法,在网上找了好几处代码,经过测试都是不通过的!最后发现了一段代码可以验证通过。代码好像在一个很多广告
- 在spring Boot中,有些代码是WEB功能,例如API等,但是有些逻辑是非WEB,启动时就要调用并持续运行的,该如何加载自己的非WEB
- Bean:在Spring技术中是基于组件的最基本了是最常用的单元其实实例保存在Spring的容器当中Bean通常被定义在配置文件当中,Bea
- 先上结论RPC请求的效率是HTTP请求的1.6倍左右,性能明显比HTTP请求要高很多。原因分析RESTful是基于HTTP协议进行交互的,H
- 这篇文章主要介绍了spring cloud Ribbon用法及原理解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学
- 记录一下微信第三方实现登录的方法。还是比较简单。一、必要的准备工作1.首先需要注册并被审核通过的微信开放平台帐号,然后创建一个移动应用,也需
- 只要是面向对象的编程语言,基本上都有类Class的用法,只是好不好用,好不好记而已,面向对象是c++开始引入的,但是c++ 关于类的东西,弄
- 影响排序效率的一般从3个方面比较:数据比较的次数,数据移动的次数,内存空间占用的大小。我们就冒泡排序、选择排序、插入排序、快速排序做一个总的
- 性能测试过程中,如果进行大量的并发时,界面容易卡死。通过非GUI(命令行)的方式是个不错的选择。windows环境1.在安装Jmeter的目