面向对象的六大原则
来源:互联网 发布:c 11 并行编程 编辑:程序博客网 时间:2024/05/22 06:19
在开发过程中,面向对象的六大原则非常重要,所以本节给大家带来了这六大原则的讲解,全文会以一个简单的ImageLoader为例进行讲解,为以后学习设计模式做铺垫。
一、单一职责原则(Single Responsibility Principle)
定义:就一个类而言,应该仅有一个引起它变化的原因,简单来说,一个类中应该是一组相关性很高的函数、数据的封装。
一般情况下,我们会这样写一个ImageLoader:
/** * 图片加载类 */public class ImageLoader { //图片缓存 private LruCache<String,Bitmap> mImageCache; //线程池,数量为CPU数量 private ExecutorService mExecutorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors()); public ImageLoader() { initCache(); } private void initCache() { //可使用的最大内存 final int maxMemory= (int) (Runtime.getRuntime().maxMemory()/1024); //取四分之一的可用内存做为缓存 final int cacheSize=maxMemory/4; mImageCache=new LruCache<String,Bitmap>(cacheSize){ @Override protected int sizeOf(String key, Bitmap value) { return value.getRowBytes()*value.getHeight()/1024; } }; } public void displayImage(final String url, final ImageView imageView){ //设置标签,防止加载错图片 imageView.setTag(url); mExecutorService.submit(new Runnable() { @Override public void run() { Bitmap bitmap=downloadImage(url); if (bitmap == null){ return; } if (imageView.getTag().equals(url)){ imageView.setImageBitmap(bitmap); } mImageCache.put(url,bitmap); } }); } private Bitmap downloadImage(String imageUrl) { Bitmap bitmap=null; try { URL url=new URL(imageUrl); HttpURLConnection conn= (HttpURLConnection) url.openConnection(); bitmap= BitmapFactory.decodeStream(conn.getInputStream()); } catch (Exception e) { e.printStackTrace(); } return bitmap; }}
但是这个类的耦合性太严重,所有的功能都写在一个类中了,如果功能增加了,ImageLoader类就会越来越大,代码越来越差,也就是可维护性很差,就会导致图片加载会越来越差。为了符合单一原则,我们应该做如下修改:
/** * 图片加载类 */public class ImageLoader { //图片缓存 private ImageCache mImageCache; //线程池,数量为CPU数量 private ExecutorService mExecutorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors()); public ImageLoader() { mImageCache=new ImageCache(); } public void displayImage(final String url, final ImageView imageView) { Bitmap bmp = mImageCache.get(url); if (bmp != null) { imageView.setImageBitmap(bmp); return; } //设置标签,防止加载错图片 imageView.setTag(url); mExecutorService.submit(new Runnable() { @Override public void run() { Bitmap bitmap = downloadImage(url); if (bitmap == null) { return; } if (imageView.getTag().equals(url)) { imageView.setImageBitmap(bitmap); } mImageCache.put(url, bitmap); } }); } private Bitmap downloadImage(String imageUrl) { Bitmap bitmap = null; try { URL url = new URL(imageUrl); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); bitmap = BitmapFactory.decodeStream(conn.getInputStream()); } catch (Exception e) { e.printStackTrace(); } return bitmap; }}/** * 图片缓存类 */public class ImageCache { //图片缓存 private LruCache<String, Bitmap> mImageCache; public ImageCache(){ initCache(); } private void initCache() { //可使用的最大内存 final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024); //取四分之一的可用内存做为缓存 final int cacheSize = maxMemory / 4; mImageCache = new LruCache<String, Bitmap>(cacheSize) { @Override protected int sizeOf(String key, Bitmap value) { return value.getRowBytes() * value.getHeight() / 1024; } }; } public Bitmap get(String url) { return mImageCache.get(url); } public void put(String url,Bitmap bmp) { mImageCache.put(url,bmp); }}
此时ImageLoader只是负责加载图片的逻辑,ImageCache只是负责处理图片缓存的逻辑,这样职责也就非常的清晰了;当缓存相关的逻辑需要改变时,不需要修改ImageLoader类了,而图片加载的逻辑需要修改时也不会影响到缓存处理逻辑。
二、开闭原则(Open Close Principle)
定义:软件中的对象(类、模块、函数等)应该对于扩展是开放的,但是对于修改是封闭的。
因此,当软件需要变化时,我们应该尽量通过扩展的方式来实现变化,而不是通过修改已有的代码来实现。
我们仍然以ImageLoader为例,假想一下,如果现在要求把这个ImageLoader再加一个SD卡缓存,你会怎么加呢?
我们一般会这样写:刚刚咱们学习了“单一职责原则”,所以我们知道肯定不能在ImageLoader里面加一个方法进行SD卡缓存,也不会在ImageCache中加SD卡缓存,因为每一个类都应该是单一的功能,仅有一个引起它变化的原因嘛。那自然会想到我再建一个DisCache类进行SD的缓存操作,在ImageLoader中加入一个DisCahce引用,手动调用里面的功能不就可以了吗?那问题来了,万一你把ImageLoader改错了呢?特别是在开发一款上市软件的时候,那将会出大问题呀?就也是违背了“开闭原则”。
那我们应该怎么做呢?思路应该是这样的:使用接口,因为接口是一种规范,进行缓存功能的实现无非就是put与get方法,存入与取出。所以先定义一个接口ImageCache,再定义一个MemoryCache与DiskCache分别实现ImageCache接口,在ImageLoader中利用多态性持有ImageCahce的引用,只在用户传入哪个ImageCache就会执行哪个缓存。代码如下:
/** * 图片缓存接口 */public interface ImageCache { public Bitmap get(String imageUrl); public void put(String imageUrl,Bitmap bmp);}/** * 内存缓存 */public class MemoryCache implements ImageCache{ //图片缓存 private LruCache<String, Bitmap> mImageCache; public MemoryCache(){ initCache(); } private void initCache() { //可使用的最大内存 final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024); //取四分之一的可用内存做为缓存 final int cacheSize = maxMemory / 4; mImageCache = new LruCache<String, Bitmap>(cacheSize) { @Override protected int sizeOf(String key, Bitmap value) { return value.getRowBytes() * value.getHeight() / 1024; } }; } public Bitmap get(String url) { return mImageCache.get(url); } public void put(String url,Bitmap bmp) { mImageCache.put(url,bmp); }}/** * SD卡缓存 */public class DiskCache implements ImageCache {private String cacheDir = "sdcard/cache/";@Overridepublic Bitmap get(String imageUrl) { return BitmapFactory.decodeFile(imageUrl);}@Overridepublic void put(String imageUrl, Bitmap bmp) { FileOutputStream fos=null; try { fos=new FileOutputStream(cacheDir+imageUrl); bmp.compress(Bitmap.CompressFormat.JPEG,100,fos); } catch (FileNotFoundException e) { e.printStackTrace(); }finally { if (fos!=null){ try { fos.close(); } catch (IOException e) { e.printStackTrace(); } } } }}/** * 图片加载类 */public class ImageLoader { //图片缓存 private ImageCache mImageCache= new MemoryCache(); //线程池,数量为CPU数量 private ExecutorService mExecutorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors()); public void setmImageCache(ImageCache cache){ mImageCache=cache; } public void displayImage(final String url, final ImageView imageView) { Bitmap bmp = mImageCache.get(url); if (bmp != null) { imageView.setImageBitmap(bmp); return; } //设置标签,防止加载错图片 imageView.setTag(url); mExecutorService.submit(new Runnable() { @Override public void run() { Bitmap bitmap = downloadImage(url); if (bitmap == null) { return; } if (imageView.getTag().equals(url)) { imageView.setImageBitmap(bitmap); } mImageCache.put(url, bitmap); } }); } private Bitmap downloadImage(String imageUrl) { Bitmap bitmap = null; try { URL url = new URL(imageUrl); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); bitmap = BitmapFactory.decodeStream(conn.getInputStream()); } catch (Exception e) { e.printStackTrace(); } return bitmap; }}
通过setImageCache(ImageCache cache)方法注入不同的缓存实现,这样不仅能够使ImageLoader更简单、健壮,也使得ImageLoader的可扩展性、灵活性更高,用户可以随便设置自己的缓存。如果现在需要加入一个双缓存机制是不是也很容易实现了,在此就不再重复了,读者可以自己尝试尝试。
三、里氏替换原则(Liskov Substitution Principle)
定义1:如果对每一个类型为 T1的对象 o1,都有类型为 T2 的对象o2,使得以 T1定义的所有程序 P 在所有的对象 o1 都代换成 o2 时,程序 P 的行为没有发生变化,那么类型 T2 是类型 T1的子类型。
定义2:所有引用基类的地方必须能透明地使用其子类的对象。
通俗的说:只要父类能出现的地方子类就可以出现,而且替换为子类也不会产生任何错误或异常,使用者可能根本就不需要知道是父类还是子类。但是,反过来就不行了,有子类出现的地方,父类未必就能适应。
比如现在我要定义一个继承自ImageView的CircleImageView,可以将CircleImageView设置给ImageLoader.displayImage(),这就是所说的里氏替换原。通过里氏替换原,就可以自定义各式各样、千变万化的View。
里氏替换原原则的核心原理是抽象,抽象又依赖于继承这个特性,在OOP(面向对象编程)当中,继承的优缺点都相当明显。
优点如下:代码重用,减少创建类的成本,每个子类都拥有父类的方法和属性;子类与父类基本相似,但又与类有所区别;提高代码的可扩展性。缺点:继承是侵入性的,只要继承就必须拥有父类的所有属性和方法;可能造成子类代码冗余、灵活性降低,因为子类必须拥有父类拥有父类的属性和方法。
下面给出一个CircleImageView的实现地址:http://www.tuicool.com/articles/mQNFJ3,感兴趣的可以自己去实现一样。
四、依赖倒置原则(Dependence Inversion Principle)
定义:高层模块不应该依赖低层模块,二者都应该依赖其抽象;抽象不应该依赖细节;细节应该依赖抽象。
高层模块就是调用端,低层模块就是具体实现类。依赖倒置原则在java语言中的表现就是:模块间的依赖通过抽象发生,实现类之间不发生直接的依赖关系,其依赖关系是通过接口或抽象类产生。
例如:上面的ImageLoader是一个很好有例子,它是依赖于ImageCache而不是某个具体的缓存机制,当用户需要实现其他缓存时,可以自己实现ImageCache接口来定义一个自己的缓存,也不需要改变ImageLoader中的任何代码。
依赖倒置原则有以下几个关键点:高层模块不应该依赖细节抽象不应该依赖细节细节应该依赖抽象
五、接口隔离原则(Interface Segregation Principle)
定义:客户端不应该依赖它不需要的接口;一个类对另一个类的依赖应该建立在最小的接口上。
接口隔离原则将非常庞大、臃肿的接口拆分成更小的和更具体的接口,这样客户将会只需要知道他们感兴趣的方法。接口隔离原则的目的是系统解耦,从而容易重构、更改和重新部署。
例如:
public void put(String imageUrl, Bitmap bmp) { FileOutputStream fos=null; try { fos=new FileOutputStream(cacheDir+imageUrl); bmp.compress(Bitmap.CompressFormat.JPEG,100,fos); } catch (FileNotFoundException e) { e.printStackTrace(); }finally { if (fos!=null){ try { fos.close(); } catch (IOException e) { e.printStackTrace(); } } }}
在我们使用OutputStream或其他可关闭的对象时,我们必须保证它们最终被关闭,而各种try….catch嵌套都会使得代码的可读性很差,而且还容易发生错误,所以我们可以写一个统一的类来关闭这此对象。
public final class CloseUtils{ private CloseUtils(){} /** * 关闭Closeable对象 */ public static void closeQuietly(Closeable closeable){ if (closeable != null){ try { closeable.close(); } catch (IOException e) { e.printStackTrace(); } } }}
现在可以将上面的put方法改写一下了
public void put(String imageUrl, Bitmap bmp) { FileOutputStream fos=null; try { fos=new FileOutputStream(cacheDir+imageUrl); bmp.compress(Bitmap.CompressFormat.JPEG,100,fos); } catch (FileNotFoundException e) { e.printStackTrace(); }finally { CloseUtils.closeQuietly(fos); }}
代码简洁了很多,而且这个closeQuietly方法可以运用到各类可关闭的对象中,保证了代码的重用性。CloseUtils的closeQuietly方法的基本原理就是依赖于Closeable抽象而不是具体实现,并且建立在最小化依赖原则的基础之上,它只需要知道对象是可关闭的就可以了,这就是接口隔离原则。
再比如说,我们需要将缓存到sd卡中的图片进行其他处理,此时我们应该再写一个单独的接口来处理,而不是将这些处理方法写入到ImageCache中去,这样才会显得结构清晰,不冗余。
六、迪米特法则(Law Of Demeter)
定义:一个对象应该对其他对象保持最少的了解。
通俗的说:一个类应该对自己需要耦合或调用的类知道得最少,类的内部如何实现与调用者或者依赖者没有关系,调用者或者依赖者只需要知道它需要的方法即可,其他的可一概不管。
例如:
public void put(String imageUrl, Bitmap bmp) { FileOutputStream fos=null; try { fos=new FileOutputStream(cacheDir+imageUrl); bmp.compress(Bitmap.CompressFormat.JPEG,100,fos); } catch (FileNotFoundException e) { e.printStackTrace(); }finally { CloseUtils.closeQuietly(fos); }}
我们可以将上面的代码改为如下的代码:
public void put(String imageUrl, Bitmap bmp) { DiskLruCache.Editor editor=null; OutputStream os=null; try { editor=mDiskLruCache.edit(imageUrl); if (editor != null){ os=editor.newOutputStream(0); if (writeBitmapToDisk(bmp,os)){ editor.commit(); }else { editor.abort(); } } } catch (Exception e) { e.printStackTrace(); }finally { CloseUtils.closeQuietly(os); }}
此时SD卡缓存的具体实现被替换了,但是对用户而言一点影响也没有,因为用户根本不知道DiskLruCache的存在,他们没有与DiskLruCache进行通信,他们只认识ImageCache,这使得系统具有更低的耦合性和更好的扩展性。
总结:
在应用开发过程中,这几个原则相当重要,你的程序里否健康,与这六大原则是息息相关的,我们不要刻板的去遵守这六大原则,而是要灵活的运用它们,这样才可以使你的程序的可维护性,可扩展性更好,更健康。
- 面向对象的六大原则
- 面向对象的六大原则
- 面向对象的六大原则
- 面向对象的六大原则
- 面向对象的六大原则
- 面向对象的六大原则
- 面向对象的六大原则
- 面向对象的六大原则
- 面向对象的六大原则
- 面向对象的六大原则
- 面向对象的六大原则
- 面向对象的六大原则
- 面向对象的六大原则
- 面向对象的六大原则
- 面向对象的六大原则
- 面向对象的六大原则
- 面向对象的六大原则
- 面向对象的六大原则
- 百度地图 baiduMap(二) 定位
- Dart入门—基础类型与正则
- 最最实用的30个Linux命令
- Linux 中安装JDK 环境
- VMware中三种网络连接的区别
- 面向对象的六大原则
- jmeter-正则表达式提取器
- Android 应用有哪些常见,常被利用的安全漏洞?
- Android 在从全屏切换到非全屏的时候闪动问题
- ipad版Qzone(纯swift编写)
- php的tips(json发送中文是出现null的问题)
- 移动开发人员学习书单和路线(持续更新...)
- LruCache详解之Android内存优化
- php的tips(数据库mysql中文写入问题)