1 UML
1.1 UML定义
◆统一建模语言(英语: Unified Modeling Language,编写UML)
◆非专利的第三代建模和规约语言
1.2 UML特点
◆UML是一种开放的方法
◆用于说明、可视化、构建和编写一个正在开发的面向对象的、软件密集系统的制品的开放方法
◆UML展现了一系列最佳工程实践
这些最佳实践在对大规模,复杂系统进行建模方面,特别是在软件架构层次已经被验证有效
1.3 UML22分类
UML22中一共定义了14种图示,分类如下:
◆结构式图形:强调的是系统式的建模
◆行为式图形:强调系统模型中触发的事件
◆交互式图形:属于行为式图形子集合,强调系统模型中资料流程
1.3.1 结构式图形
◆静态图(类图,对象图,包图)
◆实现图(组件图,部署图)
◆剖面图
◆复合结构图
1.3.2 行为式图形
◆活动图
◆状态图
◆用例图
1.3.3 交互式图形
◆通信图
◆交互概述图(UML20)
◆时序图(UML20)
◆时间图(UML20)
1.4 UML类图
Class Diagram:用于表示类、接口、实例等之间相互的静态关系
◆虽然名字叫类图,但类图中并不只有类
1.4.1 记忆技巧
◆UML箭头方向:从子类指向父类
◆提示:可能会认为子类是以父类为基础的,箭头应从父类指向子类
1.4.1.1 记忆技巧箭头方向
◆定义子类时需要通过 extends关键字指定父类
◆子类一定是知道父类定义的,但父类并不知道子类的定义
◆只有知道对方信息时才能指向对方
1.4.1.2 记忆技巧-箭头方向
◆定义子类时需要通过 extends关键字指定父类
◆子类一定是知道父类定义的,但父类并不知道子类的定义
◆只有知道对方信息时才能指向对方
◆所以箭头方向是从子类指向父类
1.4.1.3 记忆技巧-实线-继承虚线-实现
◆空心三角箭头:继承或实现
◆实线-继承,is a关系,扩展目的,不虚,很结实
◆虚线实现,虚线代表”虚”无实体
1.4.1.4 记忆技巧-实线关联虚线依赖
◆虚线-依赖关系:临时用一下,若即若离,虚无缥缈,若有若无
◆表示一种使用关系,一个类需要借助另一个类来实现功能
◆一般是一个类使用另一个类做为参数使用,或作为返回值
1.4.1.5 记忆技巧-实线关联虚线依赖
◆实线关联关系:关系稳定,实打实的关系,铁哥们
◆表示一个类对象和另一个类对象有关联
◆通常是一个类中有另一个类对象做为属性
1.4.1.6 记忆技巧-空心萎形聚合实心菱形组合
◆菱形就是一个盛东西的器皿(例如盘子)
◆聚合:代表空器皿里可以放很多相同东西,聚在一起(箭头方向所指的类
◆组合:代表满器皿里已经有实体结构的存在,生死与共
1.4.1.7 记忆技巧-空心菱形-聚合
◆整体和局部的关系,两者有着独立的生命周期,是has-a的关系
◆弱关系
◆消极的词:弱空
1.4.1.8 记忆技巧-实心萎形-组合
◆整体与局部的关系,和聚合的关系相比,关系更加强烈
◆两者有相同的生命周期, contains-a的关系
◆强关系
◆积极的词:强-满
1.4.1.9 记忆技巧-实心菱形-组合
◆常见数字表达及含义,假设有A类和B类,数字标记在A类侧
◆0…1: 0或1个实例
◆0…*: 0或多个实例
◆ 1…1: 1个实例
◆1 只能有一个实例
◆1…*: 至少有一个实例
1.4.2 UML时序图
◆ Sequence Diagram:是显示对象之间交互的图,这些对象是按时间
顺序排列的。
◆时序图中包括的建模元素主要有:
对象( Actor)、生命线( Lifeline)、
控制焦点〔 Focus of control)、消息( Message)等
2 设计原则
2.1 开闭原则
◆定义:一个软件实体如类、模块和函数应该对扩展开放,对修改关闭
◆用抽象构建框架,用实现扩展细节
◆优点:提高软件系统的可复用性及可维护性
代码实现
- ICourse的接口类和实现类
package com.design.principle.openclose;
public interface ICourse {
Integer getId();
String getName();
Double getPrice();
}
package com.design.principle.openclose;
public class JavaCourse implements ICourse{
private Integer id;
private String name;
private Double price;
public JavaCourse(Integer id, String name, Double price) {
this.id = id;
this.name = name;
this.price = price;
}
@Override
public Integer getId() {
return this.id;
}
@Override
public String getName() {
return this.name;
}
@Override
public Double getPrice() {
return this.price;
}
}
- 遵循开闭原则,新增子类JavaDiscountCourse
package com.design.principle.openclose;
public class JavaDiscountCourse extends JavaCourse{
public JavaDiscountCourse(Integer id, String name, Double price) {
super(id, name, price);
}
// 获取原价格
public Double getOriginPrice() {
return super.getPrice();
}
// 获取打折价格
@Override
public Double getPrice() {
return super.getPrice()*0.8;
}
}
- 测试类
package com.design.principle.openclose;
public class Test {
public static void main(String[] args) {
ICourse iCourse = new JavaDiscountCourse(96,"JAVA",348d);
JavaDiscountCourse javaCourse = (JavaDiscountCourse) iCourse;
System.out.println("课程ID:" + javaCourse.getId() + " 课程名称:" + javaCourse.getName() + " 课程原价格:" + javaCourse.getOriginPrice() + " 课程折扣价格:" + javaCourse.getPrice());
}
}
2.2 依赖倒置原则
◆定义:高层模块不应该依赖低层模块,二者都应该依赖其抽象
◆抽象不应该依赖细节;细节应该依赖抽象
◆针对接口编程,不要针对实现编程
◆优点:可以减少类间的耦合性、提高系统稳定性,提高代码可读性和可维护性,可降低修改程序所造成的风险
- 低层模块类----ICourse
package com.design.principle.dependenceinversion;
public interface ICourse {
void studyCourse();
}
- ICourse的实现类
package com.design.principle.dependenceinversion;
public class JavaCourse implements ICourse{
@Override
public void studyCourse() {
System.out.println("Geely在学习Java课程");
}
}
package com.design.principle.dependenceinversion;
public class FECourse implements ICourse{
@Override
public void studyCourse() {
System.out.println("Geely在学习FE课程");
}
}
- 不动类----Geely
package com.design.principle.dependenceinversion;
public class Geely {
public void setiCourse(ICourse iCourse) {
this.iCourse = iCourse;
}
private ICourse iCourse;
public void learnCourse() {
iCourse.studyCourse();
}
}
- 高层模块类----Test
package com.design.principle.dependenceinversion;
public class Test {
// version_1
// public static void main(String[] args) {
// Geely geely = new Geely();
// geely.studyJavaCourse();
// geely.studyFECourse();
// }
// version_2
// public static void main(String[] args) {
// Geely geely = new Geely();
// geely.learnCourse(new JavaCourse());
// geely.learnCourse(new FECourse());
// }
// version_3
// public static void main(String[] args) {
// Geely geely = new Geely(new JavaCourse());
// geely.learnCourse();
// }
// version_4
public static void main(String[] args) {
Geely geely = new Geely();
geely.setiCourse(new JavaCourse());
geely.learnCourse();
geely.setiCourse(new FECourse());
geely.learnCourse();
}
}
2.3 单一职责原则
◆定义:不要存在多于一个导致类变更的原因
◆一个类/接口/方法只负责一项职责
◆优点:降低类的复杂度、提高类的可读性、提高系统的可维护性、降低变更引起的风险
2.3.1 类----单一职责原则
package com.design.principle.singleresponsibility;
public class Bird {
public void mainMoveMMode(String birdName) {
if ("鸵鸟".equals(birdName)) {
System.out.println(birdName + "用脚走");
} else {
System.out.println(birdName + "用翅膀飞");
}
}
}
- 遵循类的单一职责原则,可能会造成类爆炸
package com.design.principle.singleresponsibility;
public class FlyBird {
public void mainMoveMode(String birdName) {
System.out.println(birdName + "用翅膀飞");
}
}
package com.design.principle.singleresponsibility;
public class WalkBird {
public void mainMoveMode(String birdName) {
System.out.println(birdName + "用脚走");
}
}
2.3.2 接口----单一职责原则(推荐)
package com.design.principle.singleresponsibility;
public interface ICourse {
String getCourseName();
byte[] getCourseVideo();
void studyCourse();
void refundCourse();
}
- 遵循接口的单一职责原则
package com.design.principle.singleresponsibility;
public interface ICourseManager {
void studyCourse();
void refundCourse();
}
package com.design.principle.singleresponsibility;
public interface ICourseContent {
String getCourseName();
byte[] getCourseVideo();
}
2.3.3 方法----单一职责原则(推荐)
package com.design.principle.singleresponsibility;
public class Method {
private void updateUserInfo(String userName, String address) {
userName = "geely";
address = "beijing";
}
// ====================方法----单一职责原则============================
private void updateUserInfo(String userName, String... properties) {
userName = "geely";
}
private void updateUserName(String userName) {
userName = "geely";
}
private void updateUserAddress(String address) {
address = "beijing";
}
private void updateUserInfo(String userName, String address, boolean bool) {
if (bool) {
// todo something1
} else {
// todo something2
}
userName = "geely";
address = "beijing";
}
}
2.4 接口隔离原则
◆定义:用多个专门的接口,而不使用单一的总接口,客户端不应该依赖它不需要的接囗
◆一个类对一个类的依赖应该建立在最小的接口上
◆建立单一接口,不要建立庞大臃肿的接口
◆尽量细化接口,接口中的方法尽量少
◆注意适度原则,一定要适度
◆优点:符合我们常说的高内聚低耦合的设计思想,从而使得类昊有很好的可读性、可扩展性和可维护性。
注:
高内聚:减少对外的交互;接口最少的方法,完成最多的实现
package com.design.principle.interfacesegregation;
public interface IAnimalAction {
void eat();
void fly();
void swim();
}
package com.design.principle.interfacesegregation;
public class Bird implements IAnimalAction{
@Override
public void eat() {
}
@Override
public void fly() {
}
@Override
public void swim() {
}
}
package com.design.principle.interfacesegregation;
public interface ISwimAnimalAction {
void swim();
}
package com.design.principle.interfacesegregation;
public interface IEatAnimalAction {
void eat();
}
package com.design.principle.interfacesegregation;
public interface IFlyAnimalAction {
void fly();
}
package com.design.principle.interfacesegregation;
public class Dog implements ISwimAnimalAction,IEatAnimalAction{
@Override
public void eat() {
}
@Override
public void swim() {
}
}
2.5 迪米特原则
◆定义:一个对象应该对其他对象保持最少的了解。又叫最少知道原则
◆尽量降低类与类之间的耦合
◆优点: 降低类之间的耦合
◆强调只和朋友交流,不和陌生人说话
◆朋友: 出现在成员变量、方法的输入、输出参数中的类称为成员朋友类,而出现在方法体内部的类不属于朋友类。
package com.design.principle.demeter;
import java.util.ArrayList;
import java.util.List;
public class Boss {
public void commandCheckNumber(TeamLeader teamLeader) {
List<Course> courseList = new ArrayList<>();
for (int i = 0; i < 20; i++) {
courseList.add(new Course());
}
teamLeader.checkNumberOfCourse(courseList);
}
}
package com.design.principle.demeter;
import java.util.List;
public class TeamLeader {
public void checkNumberOfCourse(List<Course> courseList) {
System.out.println("在线课程的数量是:" + courseList.size());
}
}
package com.design.principle.demeter;
public class Course {
}
package com.design.principle.demeter;
public class Test {
public static void main(String[] args) {
Boss boss = new Boss();
TeamLeader teamLeader = new TeamLeader();
boss.commandCheckNumber(teamLeader);
}
}
package com.design.principle.demeter;
public class Boss {
public void commandCheckNumber(TeamLeader teamLeader) {
teamLeader.checkNumberOfCourse();
}
}
package com.design.principle.demeter;
import java.util.ArrayList;
import java.util.List;
public class TeamLeader {
public void checkNumberOfCourse() {
List<Course> courseList = new ArrayList<>();
for (int i = 0; i < 20; i++) {
courseList.add(new Course());
}
System.out.println("在线课程的数量是:" + courseList.size());
}
}
package com.design.principle.demeter;
public class Course {
}
package com.design.principle.demeter;
public class Test {
public static void main(String[] args) {
Boss boss = new Boss();
TeamLeader teamLeader = new TeamLeader();
boss.commandCheckNumber(teamLeader);
}
}
2.6 里氏替换原则
◆定义
如果对每一个类型为T1的对象o1,都有类型为T2的对象o2,使得以T1定义的所有程序P在所有的对象o1都替换成o2时,程序P的行为没有发生变化,那么类型T2是类型T1的子类型。
◆定义扩展
一个软件实体如果适用一个父类的话那一定适用于其子类,所有引用父类的地方必须能透明地使用其子类的对象,子类对象能够替换父类对象,而程序逻辑不变。
◆引申意义:子类可以扩展父类的功能,但不能改变父类原有的功能。
◆含义1:子类可以实现父类的抽象方法,但不能覆盖父类的非抽象方法。
◆含义2:子类中可以增加自己特有的方法。
◆含义3:当子类的方法重载父类的方法时,方法的前置条件
(即方法的输入/入参)要比父类方法的输入参数更宽松
◆含义4:当子类的方法实现父类的方法时重写/重载或实现抽象方法),
方法的后置条件(即方法的输出/返回值)要比父类更严格或相等
◆优点1:约束继承泛滥,开闭原则的一种体现
◆优点2:加强程序的健壮性,同时变更时也可以做到非常好的兼容性
提高程序的维护性、扩展性。降低需求变更时引入的风险
2.7 合成(组合)/聚合复用原则----黑箱复用
◆定义:尽量使用对象组合/聚合,而不是继承关系达到软件复用的目的
◆聚合has-A和组合 contains-A
◆聚合has-A、组合contains-A 继承is-A 继承:白箱复用
◆优点:可以使系统更加灵活,降低类与类之间的耦合度,一个类的变化对其他类造成的影响相对较少
public abstract class DBConnection {
public abstract String getConnection();
}
package com.design.compositionaggregation;
public class MySQLConnection extends DBConnection{
@Override
public String getConnection() {
return "MySQl数据库连接";
}
}
package com.design.compositionaggregation;
public class PostgreSQLConnection extends DBConnection{
@Override
public String getConnection() {
return "PostgreSQLConnection数据库连接";
}
}
package com.design.compositionaggregation;
public class ProductDao {
private DBConnection dbConnection;
public void setDbConnection(DBConnection dbConnection) {
this.dbConnection = dbConnection;
}
public void addProduct() {
String conn = dbConnection.getConnection();
System.out.println("使用" + conn + "增加产品");
}
}
package com.design.compositionaggregation;
public class Test {
public static void main(String[] args) {
ProductDao productDao = new ProductDao();
productDao.setDbConnection(new PostgreSQLConnection());
productDao.addProduct();
}
}