OOAD设计模式
自从上个世纪40-50年代的计算机的兴起,软件开发行业逐渐的兴起,到达60年代末,随着面向过程的结构化高级编程语言的出现,可以说软件开发进入到了一个非常鼎盛的时期。
但是在这个时期,随着大量技术人员投入到这个行业,随着软件需求的不断变化,以及需求的复杂度越来越高,就不可避免的出现了各种各样的问题,这些问题,甚至严重到威胁到软件开发这个行业。当时的开发者,他们把这些问题称是哪个年代的“软件危机”。
"软件危机"的爆发的主要原因:
1、用户需求的不明确
2、软件公司开发软件的时候,并没有一个相对正确的理论(套路)
3、软件开发规模的越来越大
4、软件的开发难度,或者说软件的复杂度越来越大
“软件危机”具体的特征表现:
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个方法,就是1个接口。而是说1个接口只应该承担1种责任(单一原则的体现),那么接口粒度的划分,什么才叫最小化呢?答案是:适度即可
2、接口隔离原则中的“最小化原则”,同样适用于接口之间的继承,那么由继承多个接口的接口,也同样需要满足“最小化原则”。一个接口只应该承担1中责任,如果某一天,大家发现,某一个接口中,存在多个职责,那么这个接口一定需要重新设计。
第五个原则:迪米特法则
迪米特法则,又被称之为“最少知道原则” 它要求我们在定义类的时候,或者实现类的方法的时候,尽量做到只与类的朋友进行交互,不与朋友之外的任何类有直接交互,因为一旦交互,就存在耦合,甚至有的类与类之间耦合度非常的高,那么换句话说: 非朋友类如果需要修改,可以能导致你也跟着需要修改,这就违背了"开闭原则" ,也就是常说的:我妈说: 不要和陌生人说话
第六个原则:组合聚合原则
组合聚合原则:主要一种推荐大家使用面向接口编程的一种原则,它的目的在于和继承来区分“代码”的复用问题,组合聚合是一种比较特殊的关联关系,它同样满足“has”这种单词所对应的场景,只不过它更多强调的部分和整体的关系,比如:车和车的轮胎(聚合),部分和公司(组合),组合的耦合度比聚合的耦合度强。
继承和组合聚合,都可以做到关于代码的复用,但是各有各的优缺点
继承的优缺点:
优点是:
1、父类定义的所有属性和行为,子类都自动继承了,相当于子类不需要再重复定义!
2、子类如果需要重写或者扩展新的方法,非常的方便,并且对父类不会产生任何影响!(定义上来讲,但是替代上却不是)
缺点是:
1、继承容易破坏包装,就继承而言父类对于子类是完全透明的,所有的子类,以及子孙类都可以知道超类的方法。(违背了:迪米特法则)
2、父类如果发生改变,子类或者子孙类也会跟着发生改变
3、父子关系,在JAVA中采用extends实现,这种实现关系在编译期间就可以确定下来,所有我们把这关系的建立,称之为“静态关系”,因为它不具备灵活性,
我们从来没听说过:“子类在运行时,可以动态的绑定父类”
组合聚合的优点:
1、组合聚合里面,我们更多的采用面向接口编程(依赖倒转原则)来实现类与类之间的松散耦合,那么就说明:在类中访问接口,就成了访问对象的唯一方法
2、接口是抽象的,它没有细节,那么换句话说:在类中访问接口中定义的方法,我们是不知道细节的,这种代码复用更近乎于“黑箱操作”。
3、面向接口编程永远比面向细节编程,耦合度低。比如:U盘细节极容易发生变化,但是U盘与电脑之间对接的口子,却很少发生变化
4、面向接口编程中,接口的实例是在程序运行过程中,动态绑定的,那换句话说:我可以按照自己的需求,来进行灵活的改变
第七个原则:开闭原则
开闭原则:开闭原则是所有原则的核心,其他任何原则最终的目的都是为开闭原则服务。开闭原则是指组成程序的代码(类,方法,模块……),他们应该对功能的扩展是开放的,而对功能的修改是关闭的。
具体表现为:
对功能的扩展是开放的:对于一个系统而言,业务功能可以随时在改变,也可能随时在新增,该原则的意思是:系统支持我们可以去扩展新的功能,来满足用户的新需求
对功能的修改是关闭的:我们在扩展新功能的时候, 我们对于原功能的修改是关闭的。比如:我们系统以前只支持新手机,现在客户要求我们还要支持老手机,那么就要求我们:在支持老手机时,不要去修改支持新手机的任何代码。