1、单一职责原则
简单的来说,一个类中应该是一组相关性很高的函数、数据的封装。对于一个具体的示例图片加载器,可以分为图片加载和图片缓存功能,为了满足单一职责,应该把各个功能独立出来,ImageLoader只负责图片的加载,而ImageCache只负责处理图片缓存的逻辑。而不应该一股脑的都封装在一个类中。正如上面所说,单一职责所表达的用意就是“单一”二字。对于两个完全不一样的功能就不应该放在一个类中。
2、开闭原则
软件中的对象(类、模块、函数等)应该对于扩展是开放的,但是,对于修改是封闭的。因为修改可能会将错误引入原本已经经过测试的旧代码中,破坏原有系统。因此,当软件需要变化时,我们
应该尽量通过扩展的方式来实现变化,而不是通过修改已有的代码来实现。而遵循开闭原则的重要手段就是抽象。
比如ImageCache,一开始我们写了MemoryCache,后来因为需求,我们扩展了一个类DiskCache,最后,我们又扩展了一个双缓存类(如果MemoryCache没有找到,则去DiskCache中查找,如果还没有找到,则网络请求)。为了使扩展性、灵活性更高,MemoryCache、DiskCache、DoubleCache都实现了ImageCache接口,并且ImageLoader提供了一个setImageCache方法支持用户自定义缓存。
public interface ImageCache{
public Bitmap get(String url);
public void put(String url,Bitmap bmp);
}
当然,在实际的开发中,只通过继承的方式来升级、维护原有系统只是一个理想化的愿景,因此,在实际的开发过程中,修改原有的代码、扩展代码往往是同时存在的。
3、里氏替换原则
里氏替换原则,简单的说就是所有引用基类的地方必须能够透明的使用其子类的对象。我们知道,面向对象的三大特点是继承、多态、封装。里氏替换原则就是依赖于继承和多态,只要父类出现的地方子类就可以出现,而且替换为子类也不会产生任何错误或异常,使用者根本不需要知道是父类还是子类。但是,反过来就不行了,有子类出现的地方,父类未必就能适应,其实最终总结就是两个字:抽象。
开闭原则和里氏替换原则往往是生死相依、不弃不离的,通过里氏替换来达到对扩展开放,对修改关闭的效果。然而,两个原则都同时强调了一个OOP的重要特性 - 抽象,因此,在开发过程中运用抽象是走向代码优化的重要一步。
4、依赖倒置原则
依赖倒置原则指代了一种特定的解耦形式,使得高层次的模块不依赖于低层次的模块的实现细节的目的,依赖模块被倒置了(依赖抽象,而不依赖具体实现)。该原则有以下关键点:
- 高层模块不应该依赖于底层模块,两者都应该依赖其抽象;
- 抽象不应该依赖细节;
- 细节应该依赖抽象;
依赖倒置原则在Java语言中的表现就是:模块间的依赖通过抽象发生,实现类之间不发生直接的依赖关系,其依赖关系是通过接口或抽象类产生的。
其实一句话就可以概括:面向接口编程,或者说面向抽象编程。面向接口编程是面向对象精髓之一。
5、接口隔离原则
客户端不应该依赖它不需要的接口。另一种定义就是:类间的依赖关系应该建立在最小接口上。接口隔离原则将非常庞大、臃肿的接口拆分成了更小和更具体的接口,这样客户端只需要知道他们感兴趣的方法。说白了就是让客户端依赖的接口尽可能的小。试想一下,如果只需要关闭一个对象(比如FileOutputStream)时,它却暴露出了其他的接口函数,如write方法,这就使得更多的细节暴露在了客户端代码面前,不仅没有很好的隐藏实现,还增加了接口的使用难度。
以上五个原则作为面向对象的5个基本原则,最终可以化为这几个关键词:抽象、单一职责、最小化。
6、迪米特原则(最少知识原则)
一个对象应该对其他对象有最少的了解。通俗的讲,一个类应该对自己需要耦合或调用的类知道的最少,类的内部如何实现与调用者或者依赖者没关系,调用者或依赖者只需要知道它需要的方法即可,其他的一概不用管。类之间的关系越密切,耦合度越大,当一个类发生变化时,对另一个类的影响也越大。