开放封闭原则(OCP,Open Closed Principle)是全部面向对象原则的核心。软件设计自己所追求的目标就是封装变化、下降耦合,而开放封闭原则正是对这一目标的最直接体现。其余的设计原则,不少时候是为实现这一目标服务的,例如以Liskov替换原则实现最佳的、正确的继承层次,就能保证不会违反开放封闭原则。 编程
关于开放封闭原则,其核心的思想是:软件实体应该是可扩展,而不可修改的。也就是说,一个软件实体应当对扩展是开放的,而对修改是封闭的。 设计模式
所以,开放封闭原则主要体如今两个方面:对扩展开放,意味着有新的需求或变化时,能够对现有代码进行扩展,以适应新的状况。对修改封闭,意味着类一旦设计完成,就能够独立完成其工做,而不要对类进行任何修改。架构
在设计一个模块时,应当使得这个模块能够在不被修改的前提下被扩展。也就是说,应当能够在没必要修改源代码的状况下修改这个模块的行为。学习
设计的目的便在于面对需求的改变而保持系统的相对稳定,从而使得系统能够很容易的从一个版本升级到另外一个版本。spa
“需求老是变化”、“世界上没有一个软件是不变的”,这些言论是对软件需求最经典的表白。从中透射出一个关键的意思就是,对于软件设计者来讲,必须在不须要对原有的系统进行修改的状况下,实现灵活的系统扩展。而如何能作到这一点呢?设计
只有依赖于抽象。实现开放封闭的核心思想就是对抽象编程,而不对具体编程,由于抽象相对稳定。让类依赖于固定的抽象,因此对修改就是封闭的;而经过面向对象的继承和对多态机制,能够实现对抽象体的继承,经过覆写其方法来改变固有行为,实现新的扩展方法,因此对于扩展就是开放的。这是code
实施开放封闭原则的基本思路,同时这种机制是创建在两个基本的设计原则的基础上,这就是Liskov替换原则和合成/聚合复用原则。对象
对于违反这一原则的类,必须进行重构来改善,经常使用于实现的设计模式主要有Template Method模式和Strategy模式。而封装变化,是实现这一原则的重要手段,将常常发生变化的状态封装为一个类。blog
实际上,绝对封闭的系统是不存在的。不管模块是怎么封闭,到最后,总仍是有一些没法封闭的变化。而咱们的思路就是:既然不能作到彻底封闭,那咱们就应该对那些变化封闭,那些变化隔离作出选择。咱们作出选择,而后将那些没法封闭的变化抽象出来,进行隔离,容许扩展,尽量的减小系统的开发。当系统变化来临时,咱们要及时的作出反应。继承
咱们并不惧怕改变的到来。当变化到来时,咱们首先须要作的不是修改代码,而是尽量的将变化抽象出来进行隔离,而后进行扩展。面对需求的变化,对程序的修改应该是尽量经过添加代码来实现,而不是经过修改代码来实现。
实际上,变化或者可能的变化来的越早,抽象就越容易,相对的,代码的维护也就越容易;而当项目接近于完成而来的需求变化,则会使抽象变得很困难——这个困难,并非抽象自己的困难,抽象自己并无困难,困难在于系统的架构已经完成,修改牵扯的方面太多而使得抽象工做变得很困难。
咱们举个银行业务的例子
银行有四项业务,存款,取款,转帐和买基金
若是用普通的方式来写
1 /* 2 * 银行业务员 3 */ 4 public class BankWorker { 5 //负责存款业务 6 public void saving() { 7 System.out.println("进行存款操做"); 8 } 9 10 //负责取款业务 11 public void drawing() { 12 System.out.println("进行取款操做"); 13 } 14 15 //负责转帐 16 public void zhuanzhang() { 17 System.out.println("进行转帐操做"); 18 } 19 20 //负责基金的申购 21 public void jijin() { 22 System.out.println("进行基金申购操做"); 23 } 24 }
这样等同于一个业务员负责了全部的业务,站在银行窗口焦急等待的用户,在长长的队伍面前显得无奈。因此,将这种无奈迁怒到银行的头上是理所固然的,由于银行业务的管理显然有不当之处。银行的业务人员面对蜂拥而至的客户需求,在排队等待的人们并不是只有一种需求,有人存款、有人转帐,
也有人申购基金,繁忙的业务员来回在不一样的需求中穿梭,手忙脚乱的寻找各类处理单据,电脑系统的功能模块也在不一样的需求要求下来回切换,这就是一个发生在银行窗口内外的无奈场景。而我每次面对统一排队的叫号系统时,都为前面长长的等待人群而叫苦,从梳理银行业务员的职责来看,在管理上
他们负责的业务过于繁多,将其对应为软件设计来实现,这种拙劣的设计就上面例子中的方式。
这样,若是要修改的话,若是银行新添加了一项业务,这个业务员就得新增这项业务,这样扩展行就不好,并且,只要新增业务就要修改源码。
因此下面咱们用符合开放封闭原则的方式来编写代码
把不一样的业务分配给不一样的业务员,因此先编写抽象业务员父类
1 /* 2 * 银行业务员接口,是全部银行业务员的抽象父类 3 */ 4 public interface BankWorker { 5 public void operation(); 6 }
而后,在编写各个负责各个业务的业务员
1 /* 2 * 负责存款业务的业务员 3 */ 4 public class SavingBankWorker implements BankWorker { 5 6 public void operation() { 7 System.out.println("进行存款操做"); 8 } 9 10 }
1 /* 2 * 负责取款业务的业务员 3 */ 4 public class WithdrawalsBankWorker implements BankWorker{ 5 6 public void operation() { 7 System.out.println("进行取款操做"); 8 } 9 10 }
1 /* 2 * 负责转帐业务的业务员 3 */ 4 public class ZhuanZhangBankWorker implements BankWorker { 5 6 public void operation() { 7 System.out.println("进行转帐操做"); 8 } 9 10 }
1 /* 2 * 负责基金业务的业务员 3 */ 4 public class JiJinBankWorker implements BankWorker { 5 6 public void operation() { 7 System.out.println("进行基金申购操做"); 8 } 9 10 }
能够看到,这样的形式就能够作到,增长业务只需新增业务员便可,没必要对原有业务进行任何的修改,也符合了开放封闭原则
1.经过扩展已有的软件系统,能够提供新的行为,以知足对软件的新需求,是变化中的软件有必定的适应性和灵活性。
2.已有的软件模块,特别是最重要的抽象模块不能再修改,这就使变化中的软件系统有必定的稳定性和延续性。
一、开放封闭原则,是最为重要的设计原则,Liskov替换原则和合成/聚合复用原则为开放封闭原则的实现提供保证。
二、能够经过Template Method模式和Strategy模式进行重构,实现对修改封闭、对扩展开放的设计思路。
三、封装变化,是实现开放封闭原则的重要手段,对于常常发生变化的状态通常将其封装为一个抽象,例如银行业务中的IBankProcess接口。
四、拒绝滥用抽象,只将常常变化的部分进行抽象,这种经验能够从设计模式的学习与应用中得到。