设计原则之旅(二):开闭原则
来源:互联网 发布:unity3d工程师简历 编辑:程序博客网 时间:2024/06/05 16:54
定义:
Softeware entities like classes,modules and functions should be open for extension but closed for modifications.
(一个软件实体如类、模块和函数应该对扩展开放,对修改关闭。)
问题由来:
在产品更新迭代阶段,随着需求的不断变化,原有的代码不能满足新的需求,需要对原有代码进行修改,在修改的过程中,可能会对原有代码引入新的错误,从而增加系统风险与维护成本。
解决方案:
遵循开闭原则:在实体类的设计阶段,对可能变化的需求行为提取抽象接口,实体类只依赖于抽象接口,具体行为方式的实现对外开放,由调用方去实现。当需求变化的时候,只需扩展新的功能,而不需要修改原有的代码,做到对 扩展开放,对修改关闭。
举例说明:
需求: 实现一个图片加载器,用于加载网络图片显示在 ImageView 中,并且具备缓存功能。
示例代码:
图片加载器:
/** * 图片加载器 * Created by Speedy on 2017/4/10. */public class ImageLoader { private static final String TAG = "ImageLoader"; private ExecutorService mExecutorService; //图片缓存 private LruCache<String,Bitmap> mImageCache; private static ImageLoader mImageLoader; public static synchronized ImageLoader getImageLoader(){ if(mImageLoader == null){ mImageLoader = new ImageLoader(); } return mImageLoader; } private ImageLoader(){ initThreadPool(); initCache(); } //初始化线程池 private void initThreadPool() { //线程池,线程数目为 CPU 的数目 mExecutorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors()); } //初始化缓存 private void initCache() { //计算可使用最大的内存 final int maxMenory = (int) (Runtime.getRuntime().maxMemory()/1024); //取四分之一作为缓存 final int cacheSize = maxMenory / 4; mImageCache = new LruCache<String,Bitmap>(cacheSize){ @Override protected int sizeOf(String key, Bitmap value) { return value.getRowBytes()* value.getHeight() / 1024; } }; } //加入缓存 public void put(String url,Bitmap bitmap){ mImageCache.put(url,bitmap); } //获取缓存图片 public Bitmap get(String url){ return mImageCache.get(url); } //显示图片 public void diaplayImage(@NonNull final String url,@NonNull final ImageView imageView){ imageView.setTag(url); mExecutorService.submit(new Runnable() { @Override public void run() { Bitmap bitmap = get(url);//获取缓存图片 if(bitmap == null){ bitmap = downloadImage(url); if(bitmap == null){ Log.e(TAG, "图片下载失败: url = "+ url ); return;//图片下载失败, } } if(url.equals(imageView.getTag())){ bindToImageView(bitmap,imageView);//显示在控件上 } //缓存图片 put(url,bitmap); } }); } //下载网络图片 private Bitmap downloadImage(String imageUrl) { Bitmap bitmap = null; try{ URL url = new URL(imageUrl); final HttpURLConnection conn = (HttpURLConnection) url.openConnection(); bitmap = BitmapFactory.decodeStream(conn.getInputStream()); conn.disconnect(); }catch (Exception e){ e.printStackTrace(); } return bitmap; } //将 Bitmap 显示在 ImageView 中 private void bindToImageView(final Bitmap bitmap,final ImageView imageView){ imageView.post(new Runnable() { @Override public void run() { imageView.setImageBitmap(bitmap); } }); }}
测试 Activity
public class ImageTestActivity extends Activity { private ImageView imageView; private Button button; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_image_test); imageView = (ImageView) findViewById(R.id.imageView); button = (Button) findViewById(R.id.button); button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { String url = "http://avatar.csdn.net/6/B/9/1_easyer2012.jpg"; ImageLoader loader = ImageLoader.getImageLoader(); loader.diaplayImage(url,imageView); } }); }}
布局文件
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:gravity="center" > <ImageView android:id="@+id/imageView" android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/ic_launcher" /> <Button android:id="@+id/button" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="50dp" android:text="开始加载网络图片"/></LinearLayout>
示例代码分析
ImageLoader 类的设计,初看起来,好像没什么太多问题,“ 完全 ”满足需求。 但是如果需求变化,就会发现,ImageLoader 类的设计的扩展性非常差,假如用户需要修改缓存策略,则 ImageLoader 类就需要修改源代码,或者,用户需要对加载后的图片做圆角处理,同样需要修改 ImageLoader 类。
问题分析
ImageLoader 类扩展性差的主要原因是,ImageLoader类的设计违背的两大设计原则:
违背 单一职责原则
ImageLoader 类既负责了图片的加载功能,又负责图片缓存功能的具体实现。违背 开闭原则
ImageLoader 类的图片缓存功能功能是属于变化的行为,不应该将具体实现写在ImageLoader 类, 应该提取抽象行为,ImageLoader 类只依赖抽象接口,具体实现交给外界实现,便于扩展,做到对扩展开放,对修改关闭。
优化后代码:
说明:ImageLoader 类的设计存在许多需要待优化的地方,本文讲解重点是开闭原则,所以,只是以缓存的优化作为一个切入点作为例子讲解。
1 , 提取图片缓存接口
public interface ImageCache { //加入缓存 public void put(String url,Bitmap bitmap); //获取缓存图片 public Bitmap get(String url);}
2 , 实现具体缓存方式
/** * 内存缓存图片 * Created by Speedy on 2017/4/10. */public class MenoryCache implements ImageCache { private LruCache<String,Bitmap> mImageCache; public MenoryCache(){ //计算可使用最大的内存 final int maxMenory = (int) (Runtime.getRuntime().maxMemory()/1024); //取四分之一作为缓存 final int cacheSize = maxMenory / 4; mImageCache = new LruCache<String,Bitmap>(cacheSize){ @Override protected int sizeOf(String key, Bitmap value) { return value.getRowBytes()* value.getHeight() / 1024; } }; } //加入缓存 public void put(String url,Bitmap bitmap){ mImageCache.put(url,bitmap); } //获取缓存图片 public Bitmap get(String url){ return mImageCache.get(url); }}
3 , ImageLoader 与 ImageCache 建立依赖关系
/** * 图片加载器 * Created by Speedy on 2017/4/10. */public class ImageLoader { private static final String TAG = "ImageLoader"; private ExecutorService mExecutorService; //缓存接口 private ImageCache mImageCache; private static ImageLoader mImageLoader; public static synchronized ImageLoader getImageLoader(){ if(mImageLoader == null){ mImageLoader = new ImageLoader(); } return mImageLoader; } private ImageLoader(){ initThreadPool(); initCache(); } //初始化线程池 private void initThreadPool() { //线程池,线程数目为 CPU 的数目 mExecutorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors()); } //初始化缓存 private void initCache() { mImageCache = new MenoryCache(); } //提供扩展 public void setImageCache(ImageCache imageCache) { this.mImageCache = imageCache; } //显示图片 public void diaplayImage(@NonNull final String url,@NonNull final ImageView imageView){ imageView.setTag(url); mExecutorService.submit(new Runnable() { @Override public void run() { Bitmap bitmap = mImageCache.get(url);//获取缓存图片 if(bitmap == null){ bitmap = downloadImage(url); if(bitmap == null){ Log.e(TAG, "图片下载失败: url = "+ url ); return;//图片下载失败, } } if(url.equals(imageView.getTag())){ bindToImageView(bitmap,imageView);//显示在控件上 } //缓存图片 mImageCache.put(url,bitmap); } }); } //下载网络图片 private Bitmap downloadImage(String imageUrl) { Bitmap bitmap = null; try{ URL url = new URL(imageUrl); final HttpURLConnection conn = (HttpURLConnection) url.openConnection(); bitmap = BitmapFactory.decodeStream(conn.getInputStream()); conn.disconnect(); }catch (Exception e){ e.printStackTrace(); } return bitmap; } //将 Bitmap 显示在 ImageView 中 private void bindToImageView(final Bitmap bitmap,final ImageView imageView){ imageView.post(new Runnable() { @Override public void run() { imageView.setImageBitmap(bitmap); } }); }}
4,后期如果客户缓存策略有变,只需要实现 ImageCache 接口扩展功能,而 ImageLoader 这无须做任何修改,从而做到了对扩展开放,对修改关闭,及满足了开闭原则。例如:加入后期客户需要实现 SD 卡缓存功能 。
/** * Created by Speedy on 2017/4/10. */public class DiskCache implements ImageCache { ... @Override public void put(String url, Bitmap bitmap) { //讲图片保持只SD 卡中 } @Override public Bitmap get(String url) { //从SD卡中获取缓存图片 return null; }}
5,动态修改缓存策略
ImageLoader loader = ImageLoader.getImageLoader(); loader.setImageCache(new DiskCache());
欢迎关注微信公众号:“ Android 之旅 ”,一起学习 、交流 。
- 设计原则之旅(二):开闭原则
- 设计模式原则之二:开闭原则
- 设计原则之开闭原则
- 设计原则之开闭原则
- [OOD设计原则]二. 开闭原则(OCP)
- 设计原则 - 开闭原则
- 设计原则-开闭原则
- 设计原则之开闭原则(一)
- 设计模式之王者原则 开闭原则
- 设计原则之——开闭原则
- 面向对象设计原则之开闭原则
- 面向对象设计原则之开闭原则
- 面向对象设计原则之开闭原则
- 面向对象设计原则之开闭原则
- 面向对象设计原则之开闭原则
- 设计模式六大原则之开闭原则
- java web 设计原则之开闭原则
- 面向对象设计原则之开闭原则
- 接口设计实现不同线程之间数据传递
- centos7安装putty
- 获取父级元素
- HTTP协议中POST、GET、HEAD、PUT等请求方法以及一些常见错误
- 堆、栈
- 设计原则之旅(二):开闭原则
- libevent实现多线程
- Ubuntu14.04 安装git
- 省市三级联动(数据库)
- web开发 如何防止重复提交
- RabbitMQ 高级指南:从配置、使用到高可用集群搭建
- ACK flood攻击的影响
- 【持续更新】python与centos7使用过程中的问题解决方案
- 推荐算法简述