单一职责原则
单一职责原则(Single Responsibility Principle 缩写:SRP)
定义:就一个类而言,应该仅有一个引起它变化的原因。
听起来也许很抽象,简单的说就是:一个类(方法)不能担任太多的职责,因为过多的职责意味着复用的可能就越小、职责耦合就越高,当一个职责变化时,可能会影响到其他的职责功能,所以应该把职责进行隔离,将不同的职责封装到不同的类(方法)中,实现类的高内聚、低耦合。合理的分配类的职责是一件复杂而且难运用的任务,需要实战经验和一定的分析能力;
开闭原则
开闭原则(Open-Closed Principle 缩写:OCP)由勃兰特·梅耶在《面向对象软件构造》中提出这一原则。
定义:一人软件实体应对扩展开发、对修改关闭。
在软件的开发过程中,最大的不变就是变化,没有不变的需求,有变化就有修改。但是对原有代码的修改时,可能会带来不可预知的问题破坏原有的系统问题。所有说我们写代码应该遵循开闭原则,当软件需要改变时,尽量通过扩展的方式来实现改变,而不是通过修改原有的代码来实现变化;
里氏替换原则
里氏替换原则(Liskov Substitution Principle 缩写:LSP)由2008年图灵将得主芭芭拉·利斯科夫和周以真提出,
定义:所有引用父类的地方必须能透明的使用其子类的对象。
面向对象语音的三大特性:继承、封装、多态,里氏替换原则就是依赖于继承、多态这两大特性。简单的讲就是,只要父类能出现的地方子类就能出现,而且替换为子类也不会有任何异常,使用者根本不需要知道使用的是父类还是子类。但是反之则不行,子类可以使用的地方,父类并不一定适用;
这个原则可能举个列子比较好理解,列如:我喜欢吃海鲜,那我一定喜欢吃鱿鱼、虾因为他们都是海鲜,但是我喜欢吃鱿鱼并不能判断出我喜欢吃海鲜,因为我有可能不喜欢吃螃蟹。
依赖倒置原则
依赖倒置原则(Dependency Inversion Principle 缩写:DIP)
由Robert C.Martin(Bob大叔)提出。
定义:抽象不应该依赖于细节,细节应当依赖于抽象。
它有几个关键点:
- 高层模块不应该依赖于低层模块,两者都应该依赖其抽象;
- 抽象不应该依赖于细节;
- 细节应该依赖于抽象;
依赖倒置原则说的就是,要针对接口编程,而不是针对实现编程;通俗一点就是:模块间的依赖通过抽象发生,实现类之间不能直接的依赖关系,其依赖关系是通过接口或抽象类产生的;
接口隔离原则
接口隔离原则(Interface Segregation Principle 缩写:ISP)
定义:使用多个专门的接口,而不是一个总接口,即客户端不应该依赖那些它不需要的接口;
遵循接口隔离原则应该做的就是讲臃肿的接口拆分成更小和更合理的接口,客户端只需要知道它感兴趣的接口方法,这样就可以实现系统最小的耦合,从而有利于代码的重构和扩展;
接口隔离原则有点像之前的单一职责原则的意思,都应该做到合理的最小化;
迪米特原则
迪米特原则(Law of Demeter 缩写:LOD)
定义:一个软件实体应当尽可能少的与其他实体发生相互作用;
这个软件实体可能是一个类、一个库、一个模块,如果一个系统(模块)符合迪米特原则,那么当其中一个模块发生修改时,就会尽可能少的影响其他模块,扩展自然也相对容易,实现高内聚低耦合的目的。
迪米特原则又可以解释为“最少知识原则”,一个对象对其他对象有最少的了解,一个对象能了解的有以下几种:
1. 当前对象(this);
2. 以参数形式传入到当前对象中的对象;
3. 当前对象的成员变量;
4. 如果当前对象的成员对象是一个集合,那么也包括集合中的元素;
5. 当前对象创建的对象;
案例理解
上面讲了这么多枯燥无味的理论,我自己都烦了,估计你已经都忘记了上面的各个概念,忘记了说明没有理解,那我们就来个案例加深一下理解。
package com.ailian.designpattern.Principle;
import android.graphics.Bitmap;
import android.util.LruCache;
import android.widget.ImageView;
public class ImageLoader {
ImageCache imageCache = new MemoryCache();
public void setImageCache(ImageCache cache){
imageCache = cache;
}
public void showImage(String url , ImageView imageView){
Bitmap bitmap = imageCache.get(url);
if (bitmap != null){
imageView.setImageBitmap(bitmap);
}else {
//从网络下载图片并且put到ImageCache相应的对象中
}
}
}
interface ImageCache{
Bitmap get(String url);
void put(String url,Bitmap bmp);
}
class MemoryCache implements ImageCache{
private LruCache<String,Bitmap> memoryCache;
@Override
public Bitmap get(String url) {
return memoryCache.get(url);
}
@Override
public void put(String url, Bitmap bmp) {
memoryCache.put(url,bmp);
}
}
class DiskCache implements ImageCache{
@Override
public Bitmap get(String url) {
return null;
}
@Override
public void put(String url, Bitmap bmp) {
}
}
class ChooseCache implements ImageCache{
ImageCache memoryCache = new MemoryCache();
ImageCache diskCache = new DiskCache();
@Override
public Bitmap get(String url) {
Bitmap bitmap = memoryCache.get(url);
if (bitmap == null){
bitmap = diskCache.get(url);
}
return bitmap;
}
@Override
public void put(String url, Bitmap bmp) {
memoryCache.put(url,bmp);
diskCache.put(url,bmp);
}
}
分析:这是一个非常简单的加载图片的例子,但是面向对象六大基本原则都有体现:
单一职责原则:ImageLoader只做一件事,展示图标并且将图片保存到缓存或磁盘中;而MemoryCache、DiskCache、ChooseCatche类则只做具体的保持和读取操作;
开闭原则:而MemoryCache、DiskCache、ChooseCatche类通过setImageCache方式注入到ImageLoader中,当需要添加新的缓存方式时不需要改任何ImageLoader的代码,只需要增加一个xxCatche并实现ImageCache接口,然后通过setImageCache方法注入到ImageLoader就可以了;
里氏替换原则:setImageCache中传入的是ImageCache接口,为什么不是传入MemoryCache、DiskCache、ChooseCatche中的一个呢,因为当传入子类的话,其他子类就无法传入,否则会出现编译时异常;
接口隔离原则:ImageCache接口只定义了put和get方法,其他不相干的方法不应该定义在该接口中;
迪米特原则:ImageLoader只知道ImageCache,它完全不知道MemoryCache、DiskCache、ChooseCatche等子类的存在;
总结
面向对象设计原则为支持可维护性、复用性而产生,这些原则运用于很多设计模式中,每一种设计模式的思想都遵循这些原则,我们使用设计模式的目的就是开发出一个,易于升级、易于维护,在经历多个版本后依然保持代码清晰、灵活、稳定的系统;当然,这时一个比较理想的系统,但是我们应该努力实现这个目标;