主要内容
- UML
- 类与类的关系
- 多态
- instanceof关键字
- 简单工厂设计模式
- 面向对象设计原则
一、 String 类中常用方法
1 join
把数组中的值使用指定分隔符组成一个新的字符串
使用示例:
String result = String.join(“,”,数组对象);
2 split
把字符串值使用指定分隔符进行拆分,形成字符串数组
使用示例:
String [] arr =字符串对象.split(“,”);
3 subString
截取字符串中部分内容并返回新的字符串,原字符串内容不变。
示例:
截取时从0开始算起,包前不包后。
示例二:
如果只有一个参数,从指定索引截取到最后
二、 UML
1 UML简介
UML(Unified Modeling Language)统一建模语言。是一种图形化语言。
在UML 2.5 中共包含14种图形:类图、用例图、活动图、对象图、时序图、交互概述图、包图、配置文件图、部署图、组件图、组合结构图、状态机图、通信图、序列图
2 UML工具
既然想要画UML图,肯定不是使用Windows的画板完成。市场上有很多流行工具都可以画UML图。例如:PowerDesigner、ROSE、VISIO、EA、StarUML、亿图等。
我们课上以PowerDesigner给大家讲解类图的画法。
3 类图
类图(class diagram)是用图形的方式来展示类的名称、类中包含内容、类和类的关系。
4 使用PowerDesigner画两个类
4.1 新建类图
4.2 添加类
先点击类,然后在左侧空白位置添加类。
然后点击箭头
会在左侧生成类
4.3 修改类名
双击类,在弹出框中设置类名
4.4 设置属性
在弹出框中店家Attributes,设置属性。
属性只需要修改Name和Data Type列
4.5 设置方法
选择Operations,表示设置方法。
方法需要设置Name列和Return Type列
设置完成后先点击应用按钮
4.6 设置方法参数
给哪个方法设置参数,鼠标左键就双击前面
在弹出框中选择Parameter,设置Name列和 Data Type 列。
三、 类与类的关系
在Java中类与类的关系分为:继承、实现、依赖、关联、聚合、组合六种关系。
1 继承关系
1.1 介绍
继承关系:Generalization(泛化)
继承关系是一个类型属于另一个类型,即is - a 关系
当类继承另一个类或接口继承另一个接口时都属于这种关系。
1.2 代码示例
1.3 UML类图
Generalization使用空心三角箭头 + 实线。由子类指向父类。
2 实现关系
2.1 简介
实现关系:Realization。
当一个类实现了一个或多个接口时,之间的关系就是实现关系。
2.2 代码示例
2.3 UML类图
UML中使用虚线+空心三角箭头。由实现类指向接口
3 依赖关系
3.1 简介
依赖关系:Dependency
依赖关系就是一个类用到了另外一个类。这种关系是偶然的,临时的。不具备长期关系。
3.2 代码示例
例如:学生去办公室问了老师一个问题。
依赖关系在代码中多体现为一个类作为另一个类的方法参数。
3.3 UML类图
Student中方法使用了Teacher类。就说Student依赖Teacher。
使用虚线箭头由Student指向Teacher。
4 关联关系
4.1 简介
关联关系:Association
关联关系是一种强语义关系,这种关系不是临时的、不是偶然的,是一种长期关系。
关联关系可以是单向的,也可以是双向的。而且具备一对一、一对多、多对多、多对一关系。
关联关系一般都是平等两个类。
4.2 代码示例
例如:老师和学生就是关联关系。
在代码中老师可以作为学生的全局属性,学生也可以作为老师的全局属性。
老师可以教很多学生。所以在代码中老师类可以包含多个学生对象
以我们尚学堂模式举例,学生只听一个老师讲课。
4.3 UML类图
关联关系为实线箭头。Student中包含Teacher,箭头就由Student指向老师。老师中包含学生,箭头就由老师指向学生。
箭头两侧的值表示对应关系。
上面箭头表示Student和Teacher的关系是
5 聚合关系
5.1 简介
聚合关系:Aggregation
聚合关系属于关联关系中一种特例。表示整体和部分的关系即has - a。聚合是整体和部分的关系。这种关系整体和部分是可以分离的。例如:公司和员工、班级和学生。部分单独存在也是允许的。
5.2 代码示例
既然聚合属于关联关系中一种特例。体现在代码层面依然是一个类作为另一个类的属性。
例如:公司和员工。代码体现除了员工在公司上班。
5.3 URM类图
空心菱形 + 实线箭头。
6 组合关系
6.1 简介
组合关系:Composition
组合关系属于聚合关系的特例。也表示整体和部分的关系,。但是整体和部分是不可分离的,即contains - a。例如:人和大脑。
6.2 代码示例
既然组合关系属于聚合关系的特例。在代码层面上还是一个类作为另一个类的全局属性
以人和大脑举例。人包含一个大脑。
6.3 UML类图
UML中使用实心菱形 + 实线箭头。谁里面包含另一个类,就由谁指向另一个类。
四、 向上转型和向下转型
1 向上转型
在Java中允许把子类对象赋值给父类声明,或把实现类对象赋值给接口声明,这种情况称为向上转型。
当使用向上转型时,对象只能调用父类中的方法或子类重写父类的方法。不能调用子类独有的方法。把向上转型理解清楚了,后面的多态案例就很好学习了。
2 向下转型
向下转型是把父类对象或接口对象赋值给子类或实现类对象,向下转型必须添加所转换类型的语法。
可以把实现类对象赋值给接口声明。
向下转型后,对象就可以调用子类独有的方法了。其实向下转型就相当于直接实例化实现类对象,然后赋值给实现类声明。
2.1 错误示例
向下转型时,要求父类对象指向的必须是转换后类型或转换后类型的子类。
所以下面代码运行会报错。
3 instanceof 关键字
3.1 简介
Java中允许父类对象引用接收子类对象, 接口接收实现类.
instanceof用于判断某个对象是否属于某个类或者接口,若是返回true,不是返回false
3.2 语法
对象 instanceof 类型
返回结果为boolean类型。
因为instanceof是判断是用在继承关系|实现关系,所以直接和final修饰的类做判断,直接编译错误。
3.3 代码示例
存在下面的关系
测试结果。其中Abc为使用final修饰的类。
五、 多态
1 简介
多态:把子类(实现类)实例化对象赋值给父类(接口)对象声明,实例化时可以是不同的子类,不同的子类重写后的方法实现都可能是不一样的。就形成了同一个对象,调用同一个方法,产生不同的结果。
多态的前提是:
继承 -> 重写 -> 子类对象赋值给父类。
实现 -> 实现方法 -> 子类对象赋值给父接口
多态主要用在方法返回值方法参数和中。
因为有了多态,才让代码更加灵活,更加方便。
注意:
多态是方法的多态,不是属性的多态。
六、 简单工厂设计模式
1 设计模式简介
设计模式就是前人总结出的一套代码,这套代码作为某些特定问题的最佳解决方案。
我们常说有23种设计模式。
面向对象代码的衡量标准就是面向对象设计原则。而设计模式是面向对象最经典的应用。
简单工厂是工厂方法模式的简化版、工厂方法模式又属于抽象工厂模式的简化版。
不用着急我们一点一点学习,今天给大家讲解第一个设计模式,这个设计模式也是多态的一种体现。
2 简单工厂设计模式
简单工厂是设计模式中比较简单的一种。就是根据参数的值判断需要实例化哪个子类对象。
以小汽车举例:
具有如下继承层次关系
我们可以编写一个工厂类,该类根据用户的选择实例化对应的对象。一般工厂类都是以Factory结尾的。
下图代码方法的返回值是父类类型,而实际接收的是子类对象。这也是标准的多态写法。
七、 面向对象设计原则
面向对象设计原则是面向对象设计的基石,面向对象设计质量的依据和保障。一共有7个设计原则。设计模式就是面向对象设计原则的经典应用。
1 单一职责原则
1.1 概念
单一职责原则 SRP --- Single Responsibility Principle
There should never be more than one reason for a class to change。
应该有且仅有一个原因引起类的变更
系统中的每个类都应只有一个职责,而所有类所关注的就是自身职责的完成
职责是指为“变化的原因”
如果能想到多个原因去改变一个类,这个类就具有多个职责
并不是单一功能原则,并不是每个类只能有一个方法,而是单一“变化的原因”原则
如果一个类有多个职责,这些职责就耦合在了一起,当一个职责发生变化时,可能会影响其它的职责
多个职责耦合在一起,会影响复用性(可能只需要复用该类某一个职责,但该职责跟其它职责耦合在一起,很难分离出来)
1.2 好处
单一职责原则的意思就是经常说的“高内聚、低耦合”
1.3 举例
饭店不同员工职责不同,我们不需要老板一肩扛起所有工作,可以安排不同岗位的员工。即使某个员工离职了,也比较好招聘到新员工。
JavaEE中的分层框架模式实际上体现了单一职责原则
对于用户操作,可以分解存储数据的实体类User,完成数据库操作的DAO类UserDao、完成业务操作的业务类UserService、显示用户信息的页面userList.jsp,甚至增加负责调度的UserServlet。
1.4 注意
单一职能原则是所有原则中最简单的、最难应用的一个;要注意过犹不及
“变化的原因”,只有实际发生时才有意义。可能预测到会有多个原因引起这个类的变化,但这仅仅是预测,并没有真的发生,这个类仍可看做具有单一职责,不需要分离
2 开闭原则OCP
2.1 概念
开闭原则OCP--Open Closed Principle
Software entities should be open for extension,but closed for modification。
软件实体应当对扩展开放,对修改关闭。更通俗翻译:软件系统中的各种组件,如模块(Modules)、类(Classes)以及功能(Functions)等,应该在不修改现有代码的基础上,引入新功能。
实现开闭原则的关键是抽象
定义一个抽象层,只规定功能而不提供实现,实现通过定义具体类来完成
需求变化时不是通过修改抽象层来完成,而是通过定义抽象层新实现完成
通过抽象类及接口,规定了具体类的特征作为抽象层,相对稳定,不需修改,从而满足“对修改关闭”;从抽象类导出的具体类可以改变系统的行为,从而满足“对扩展开放”
2.2 好处
通过扩展已有软件系统,可提供新的行为,以满足对软件的新需求,提高了软件系统的适应性和灵活性
已有的软件模块,特别是最重要的抽象层模块不能再修改,提高了软件系统的一定的稳定性和延续性
这样的设计同时也满足了可复用性与可维护性
2.3 举例
定义飞行接口Flyable,可以有多个实现类。showFly()使用Flyable作为参数,可以传递所有实现类的对象。有了新选手,增加实现类即可。不需要修改showFly()所在的类。
JavaEE多层模式下,可以定义业务、DAO接口,是和其他层对接的规范。可以增加不同的实现类,而不是去修改原来的实现类。
开发一个计算器类。可以定义一个运算接口,提供基本的加减乘除运算的实现类,如果需要增加取模、开平方、幂等运算,增加新的实现类即可。
2.4 总结
实现开闭原则的关键是抽象
抽象层相对稳定不需修改,需求变化后通过重新定义抽象层的新实现来完成
即使无法百分之百的做到开闭原则,但朝这个方向努力,可以显著改善一个系统的结构
对系统每个部分都肆意地进行抽象也不是一个好主意,应该仅仅对程序中需求频繁变化部分进行抽象。拒绝不成熟的抽象和抽象本身一样重要
开闭原则具有理想主义的色彩,它是面向对象设计的终极目标。其他设计原则都可以看作是开闭原则的实现手段或方法
3 里氏替代原则
3.1 概念
里氏替代原则LSP--Liskov Substitution Principle
functions that use pointers or references to base classes must be able to use objects of derived classes without knowing it.
所有引用基类的地方必须能透明地使用其子类的对象。
通俗点讲只要父类能出现的地方子类就可以出现,而且调用子类还不产生任何的错误或异常,调用者可能根本就不需要知道是父类还是子类。但是反过来就不成了,有子类出现的地方,父类未必就能适应。
里氏替换法则包含了四层意思:
1) 子类必须完全实现父类的方法--重写
2) 子类可以有自己的个性----子类中可以定义特有的方法
3) 覆盖和实现父类的方法时,输出结果(返回值)可以被缩小,但不能被放大
3.2 好处
里氏代换原则对如何良好继承提出了衡量依据
3.3 举例
1、企鹅/鸵鸟是鸟吗?
2、玩具手枪是手枪吗?
建议:如果子类不能完整实现父类的方法,或者是父类的某些方法在子类中已经发生“畸变”,那么建议断开父子继承关系,采用依赖、聚集、组合等关系代替继承。
3.4 总结
采用开闭原则必然用到抽象和多态,而这离不开继承。而里氏代换原则对如何良好继承提出了衡量依据。里氏代换原则是使代码符合开闭原则的一个重要保证。
4 依赖倒置原则DIP(接口)
4.1 概念
依赖倒置原则DIP --Dependence Inversion Principle
High level modules should not depend upon low level modules. Both should depend upon abstractions. Abstractions should not depend upon details. Details should depend upon abstractions。翻译过来,包含三层含义:高层模块不应该依赖低层模块,两者都应该依赖其抽象;抽象不应该依赖细节;细节应该依赖抽象。
抽象:即抽象类或接口,两者是不能够实例化的。
细节:即具体的实现类,实现接口或者继承抽象类的类,可通过关键字new直接被实例化。
依赖正置就是类间的依赖是实实在在的实现类间的依赖,也就是面向实现编程
依赖倒置原则的本质其实就是通过抽象(抽象类或接口)使各个类或模块的实现彼此独立,不相互影响,实现模块间的松耦合 面向要求进行依赖
4.2 好处
采用依赖倒置原则可以减少类间的耦合性,提高系统的稳定性,减少并行开发引起的风险,提高代码的可读性和可维护性。
4.3 举例
早期电脑所有硬件整合在一起,一个模块坏全部坏,现在的电脑依赖于插槽(规范),更换cpu、内存、卡等方便。
司机驾校培训出来不是只会开宝马、或者只会开奔驰,而是可以开所有汽车。
4.4 总结
依赖倒置原则核心就是要面向接口编程,理解了面向接口编程,也就理解了依赖倒置
如果没有实现依赖倒置原则,那么也就意味着开闭原则也无法实现。
结合实际情况使用此原则,要考虑生产和成本,不能生搬硬套
5 接口分离原则ISP
5.1 概念
接口分离原则ISP-- Interface Segregation Principle
有两种定义
第一种:Clients should not be forced to depend upon interfaces that they don't use.客户端不应该强行依赖它不需要的接口
第二种:The dependency of one class to another one should depend on the smallest possible interface.类间的依赖关系应该建立在最小的接口上
接口隔离原则的含义:
建立单一接口,不要建立庞大臃肿的接口,尽量细化接口,接口中的方法尽量少。也就是说,要为各个类建立专用接口,而不要试图去建立一个很庞大的接口供所有依赖它的类去调用。
5.2 好处
防止庞大、臃肿的接口,避免“接口污染”,提高灵活性和可维护性
5.3 举例
在车站售票窗口排队的人有买票的,有查开车信息,有退票的,不必排在同一窗口中。多开几窗口,每个窗不同功能,让不同需求的人排在不同窗口,可以节约时间和人力。
5.4 总结
注意控制接口的粒度,接口不能太小,如果太小会导致系统中接口泛滥,不利于维护;
接口也不能太大,太大的接口将违背接口隔离原则,灵活性较差,使用起来很不方便。
一般而言,接口中仅包含为某一类用户定制的方法即可,不应该强迫客户依赖于那些它们不用的方法。
6 迪米特法则LOD
6.1 概念
迪米特法则LOD-- Law of Demeter
talk only to your immediate friends
只与你直接的朋友通信(不跟陌生人说话,朋友越少越好)
如果两个类不必彼此直接通信,那么这两个类就不应当发生直接的相互作用.如果其中一个类需要调用另一个类的方法的话,可以通过第三者转发这个调用.
迪米特法则的初衷在于降低类之间的耦合。由于每个类尽量减少对其他类的依赖,因此,很容易使得系统的功能模块功能独立,相互之间不存在(或很少有)依赖关系。
符合LOD 不符合LOD
6.2 好处
尽量降低类与类之间的耦合
6.3 举例
买房找一个中介即可,不用是认识很多卖房人。去医院看病不用自己知道所有科室的位置,找一个导医即可。
6.4 总结
过分使用迪米特原则会产生大量这样的中介和传递类,导致系统复杂度变大。所以在采用迪米特法则时要反复权衡,既做到结构清晰,又要高内聚低耦合。
外观模式和中介者模式都是迪米特法则的应用
7 合成/聚合复用原则CARP
7.1 概念
合成/聚合复用原则CARP-- Composite Aggregate Reuse Principle
Favor object composition over class inheritance.
优先使用对象组合,而不是类继承.(要尽量使用合成和聚合,尽量不要使用继承)
合成聚合复用原则是指在一个新对象中通过关联关系(组合和聚合关系)使用原来已经存在的一些对象,是之成为新对象的一部分,新的对象通过向这些原来已经具有的对象委派相应的动作或者命令达到复用已有功能的目的。
为何“要尽量使用合成和聚合,尽量不要使用继承”呢?这是因为:
第一, 继承复用破坏包装,它把超类的实现细节直接暴露给子类,这违背了信息隐藏的原则;
第二, 如果超类发生了改变,那么子类也要发生相应的改变,这就直接导致了类与类之间的高耦合,不利于类的扩展、复用、维护等,也带来了系统僵硬和脆弱的设计。
第三, 从超类继承而来的实现是静态的,不可能再运行时间内发生改变,因此没有足够的灵活性。
而是用合成和聚合的时候新对象和已有对象的交互往往是通过接口或者抽象类进行的,就可以很好的避免上面的不足,而且这也可以让每一个新的类专注于实现自己的任务,符合单一职责原则。
7.2 好处
非常有利于构建可维护、可复用、可扩展和灵活性好的软件系统。
7.3 举例
7.4 总结
组合与继承都是重要的复用方法。组合称为黑箱复用,继承称为白箱复用。
在OO开发的早期,继承被过度地使用;随着时间发展,人们发现优先使用组合可以获得复用性与简单性更佳的设计
并非不要使用继承,并非继承一无是处,而是不要滥用继承。合成/聚合也有自己的缺点
单一职责原则 ★★★★
开闭原则 ★★★★★
里氏代换原则 ★★★
依赖倒置原则 ★★★★★
接口分离原则 ★★★
迪米特原则 ★★
组合/聚合复用原则 ★★★★