自从上个世纪40-50年代的计算机的兴起,软件开发行业逐渐的兴起,到达60年代末,随着面向过程的结构化编程语言的出现,可以说软件开发进入到了一个非常鼎盛的时期。
但是在这个时期,随着大量技术人员投入到这个行业,随着软件需求的不断变化,以及需求的复杂度越来越高,就越不可避免的出现各种各样的问题,这些问题,甚至严重到威胁到软件开发这个行业,当时的开发者,他们把这些问题称为是那个年代的“软件危机”。
“软件危机”具体的特征表现: 1、软件开发周期无法确定 2、软件开发成本越来越高,甚至无法控制 3、产品质量不高,产品的功能无法满足客户的需求,经常容易项目失败 4、当时的程序员技术,沟通等相关能力参差不齐,导致软件开发出来产品,产品质量不高 5、软件产品非常难于维护,具体原因:程序员的编码能力,个人习惯 6、当时软件开发,不注重文档,软件缺少适当的文档资料
软件开发,就跟建筑行业一样,也需要有前期各种规划,也需要有前期各种设计,同样需要有农名工(程序猿|攻城狮)来参与,同样需要对软件进行各种测试|验收, 同样也存在交付产品的过程。
如果我们不去向上述的方案进行操作的话,那么就有可能会出问题。所以我们软件开发也是非常严谨的事情,容不得半点马虎!
软件开发,同样也有各种需要管理的东西
软件开发中,比较经典的开发步骤:
瀑布模型:
可行性分析
需求分析
软件设计
编码
测试
交付产品
维护
瀑布模型存在比较严重的问题:需求如果要发生变更,只能在极早期 越往后走,代价越大
改进方案:迭代 + 瀑布模型
1、计划驱动 文档 2、敏捷开发 客户交流
需求分析,软件设计,编程 都有两种方式:面向对象 面向过程
采用面向对象的思维习惯来,分析需求(OOA) 软件设计(OOD) 编程(OOP)
软件开发过程中,怎么样去评价一个软件设计的合不合理,好不好?
评价的标准是:
1、有没有覆盖用户所提的所有的业务功能(覆盖了所有的需求,也不一定就是一个设计非常好的软件)
2、前期设计出来的各种文档或者图纸,可读性高不高,能不能被项目所有的干系人快速的理解。一个可读性非常差文档,或者设计的图纸,会给我们后期开发,测试,维护都会带来巨大的影响
3、软件设计的图纸中,覆盖的类,包,接口,以及其他的各种组件,复用性强不强,能不能被本项目,或者其他项目重复使用
4、软件开发后期,如果业务需求需要发生变化,业务功能或者性能的扩展性高不高
5、后期软件的维护,或者开发过程中代码的维护能力强不强(员工离职了,其他员工能不能看懂,能不能快速的理解我们的软件结构)
上述的5个方向,归纳起来就是一句话,软件设计过程中,是否能做到“高内聚,低耦合”?
高内聚:1个类只做1件事,以及1个方法只做1件事 具体来说:就是1个类只关注1个点,比如:用户类只关注用户信息或者用户的行为 它不会去关注其他类的属性或者行为
如果一个类只关注1件事,并且内部每一个方法也只关注1个事,那么说该类的内聚度非常的高,越高代表职责越清晰,代码的复用性越高,类与类之间的牵连越少
耦合: 就是指类和类之间的关系紧密程度 如果一个类与例外一个类关系的非常紧密,那么当其中的一个类需要发生变化的时候,调用类将也会跟着变化。
比如下面这段代码:
public class StudentServiceImpl implements IStudentService {
private IStudentDao studentDaoImpl = new StudentDaoImpl();
@Override
public void saveStudent(StudentBean stu) {
// TODO Auto-generated method stub
studentDaoImpl.saveStudent(stu);
}
}
其中的StudentServiceImpl 和 StudentDaoImpl 两个类的关系就非常的紧密。如果一旦StudentDaoImpl需要被替代,那么StudentServiceImpl也就需要跟着修改代码。
耦合度这个东西,它是决定了应用软件能否灵活支持需求变化的最大因素。紧密关系越紧,变化度越低,变化的代价越大。 但是我们又不能说就直接取消耦合,所以咱们只能降低“耦合度”,怎么去降低耦合度?
就需要大家去遵循这个行业里面,由前辈给我们总结出来一些设计原则,以及设计模式!
第一个原则:单一原则
一句话描述:“1个类只干1件事”,这是软件设计中高内聚的最重要一点,换句话说:1个类只有1种职责,那么这个职责的确定,需要根据类的对象来确定,看你正在关注对象的哪个方向。比如:学校系统中,学生对象来讲,只需要关注学习,而不应该去关注他或者她怎么当子女。只有1种情况能去改变我这个类。
也就是说在设计类时,类中每一个属性,和每一个方法都必须要根这个类的职责,具有直接关系。如果某一个属性或者某一个行为与本类的职责的无关,那么这个类的设计,就一定违背了“单一原则”
类的职责单一,也是一种降低耦合度的手段,对象的职责越少,耦合关联度越低,代码越容易复用。当然单一职责,同样适用于方法。当方法违背单一原则时,那么方法的代码将会非常冗长,以及可能存在代码的重复,甚至代码的浪费!
第二个原则:里式替换原则
定义:子类可以出现在父类出现的任何地方,并且对于父类之前定义的规则,不出发生任何改变!简单点就是说不要重写父类的方法。
目的:为了维护现有的继承体系,保证龙生龙,凤生凤,老鼠儿子依旧会打洞 这个原则是开闭原则的一种体现,或者是一种重要的保证,保证儿子在新增新的功能的时候,不会去改写父类已经定义好的方法,从而去维护现有的继承体系。
第三个原则:依赖倒置原则
软件设计中,多层次之间相互依赖关系需要导致为抽象类或接口,而不是直接依赖于具体的实现。 具体表现为: 1、上层模块不应该直接依赖下层实现,而应该依赖下层的抽象 2、每一个单独的层次,抽象不应该依赖于细节,而细节应该依赖于抽象
第四个原则:接口隔离原则
软件设计中我们所有的实现类在依赖接口的时候,都不应该去依赖那些用不到的方法,只需要依赖那些自己需要的方法。换句话说:就是我们在设计软件的时候,不要强迫实现类去实现它不需要的方法。
接口隔离原则,具体体现有两种情况:
1、接口的设计应该遵循最小接口原则,但这里的最小化,不是说一个方法,一个接口,而是说一个接口只应当承担1种职责(单一原则的体现),那么接口粒度的划分,什么叫最小化呢?答案是:适度即可。
不要把用户不使用的方法塞进同一个接口里,如果一个接口的方法没有被使用到,则说明接口过胖,应该分割成几个功能专一的接口,使用多个专门的接口比使用单一的总接口要好。说白点,一个接口只服务于一个职责。
2、接口的继承原则:接口原则中的“最小化原则”,同样适用于接口之间的继承,那么由继承多个接口的接口,也同样需要满足“最小化原则”,一个接口只应当承担1种职责,如果某一天,大家发现,某一个接口中,存在多个职责,那么接口就要重新设计。
如果一个接口A继承另一个接口B.则接口A相当于继承了接口B的方法,那么继承了接口B后的接口A也应该循上述原则:不应该包含用户不使用的方法。反之,则说明接口A被B给污染了,应该重新设计它们的关系。一个接口只服务于一个职责。
第五个原则:迪米特法则
又称为“最少知道原则”,它的定义为:一个软件实体应当尽可能少的与其他实体发生相互作用。
只和朋友有关联,和朋友的朋友不要有直接的关联。简单说,有三个类A、B、C,A中需要B类信息,B类需要C类信息,也就是包含关系,现在A要用到C类中的方法,那么就会用到A.B.C的方式,即从A类中找到B类中包含的C类,再去调用C类方法,这就违背了迪米特法则,他们之间就存在了耦合,假设C类的类名进行了修改,那么B类中包含的C类类名也要进行修改,A类中因为直接调用了C类中的方法,那么A类也要修改。所以改进点是,在B类中提供一个调用C类方法的新方法,这样A直接去调用B类的新方法就行了,这样C类改变了,只需要去改变B类关于C的信息,A类则不会受到任何影响。
第六个原则:组合/聚合复用原则
组合聚和原则:主要是一种推荐大家使用面向接口编程的一种原则,它的目的在于和继承来区分“代码”的复用问题,组合聚合是一种比较特殊的关联关系,它同样满足“has”这种单词所对应的场景,只不过它更多的强调部分和整体的关系,比如:车和车的轮胎(聚合),部分和公司(组合),组合的耦合度比聚合的耦合度强。
继承和组合聚合,都可以做到关于代码的复用,但是各有各的优缺点。
组合聚合复用的好处:
1)新对象存取成分对象的唯一方法是通过成分对象的接口。说白了就是采用面向接口编程(依赖倒转原则)来实现类与类之间的松散耦合,那么就说明:在类中访问接口,就成了访问对象的唯一方法。
2)这种对象的复用是黑箱复用,因为接口是抽象的,它没有细节,所以说,成分对象的内部实现细节对于新的对象是看不见的。
3)这种复用所用的依赖更少。面向接口编程比面向细节编程的耦合度低。比如说:U盘细节容易发生变化,但是U盘之间对接的口子,却很少发生变化。
4)新对象可以在运行的时候动态的引用于成分对象类型相同的对象。
继承复用的优点:
1)新的实现较为容易,因为超类的大部分功能可以通过继承关系自动进入子类。
2)修改或者扩展继承而来的实现比较容易。
继承复用的缺点:
1)继承复用破坏包装。将超类的实现细节暴露给子类。超类的内部细节常常对子类是透明的,白箱复用。
2)超类的实现发生了改变,子类的实现也不得不改变。
3)超类继承而来的是静态的,不可能在运行时间内发生改变。因此没有足够的灵活性。我们从来没听说过“子类在运行时可以动态的绑定父类”。
第七个原则:开闭原则
开闭原则:开闭原则是所有原则的核心,其他任何原则最终的目的都是为开闭原则服务。开闭原则是指组成程序的代码(类、方法、模块......),他们应该对功能的扩展是开放的,而对功能的修改是关闭的。
具体表现为:
对功能的扩展是开发的:对于一个系统而言,业务功能可以随时在改变,也可能随时在新增,该原则的意思是:系统支持我们去扩展新的功能,来满足用户的新需求
对功能的修改是关闭的:我们在扩展新功能时候,我们对于原功能的修改是关闭的,比如:我们系统以前只支持新手机,现在客户要求我们还要支持老手机,那么就要求我们:在支持老手机时,不要去修改支持新手机的任何代码