设计模式

统一过程(UP)

统一过程(Unified Process,简称UP)是一种软件开发过程框架,它是一种迭代的、增量的开发方法,旨在帮助开发团队更好地管理软件项目。

统一过程的主要特点包括:

  1. 迭代开发:将整个项目分解为多个迭代周期,每个周期完成一定的功能模块,逐步构建完整的软件系统。
  2. 增量交付:在每个迭代周期结束时,交付一个可运行的软件版本,以便客户和用户能够及时了解项目进展并提供反馈。
  3. 风险管理:通过持续评估项目中的风险,并在必要时进行调整,确保项目按计划进行。
  4. 适应性:根据项目需求和团队经验,可以调整统一过程的各个方面,以适应不同的项目场景。
  5. 文档化:在整个开发过程中,保持对软件设计、实现和测试的详细记录,以便团队成员之间共享信息并确保项目的可维护性。

代码质量的评价标准

代码质量:

1、可维护性 :不去破坏原有的代码设计以及不引入新的bug的前提下,修改或者新增代码

2、灵活性 :在添加新代码的时候,不破坏资深的前提下接纳新代码。

3、简洁性:别人理解时间最小化。

4、可复用性:减少重复代码编写,复用已有的代码

5、可测试性 :在单元测试的时候易于测试。

6、可扩展性:对修改关闭,对扩展开放,不修改或者少量修改代码增添新功能。

7、可读性:人类能理解的代码。代码风格符合编程规范。

编程方法论

1、面向对象:是一种编程的思想也是一种编程范式

2、设计原则:一些代码设计的经验总结,尽量根据设计原则来开发。

  1. 单一职责原则
  2. 开闭原则
  3. 里氏代换原则
  4. 依赖倒转原则
  5. 接口隔离原则
  6. 迪米特法则

3、设计模式:已经总结出来的一套解决方案或者设计思路

4、编程规范:解决代码可读性问题,更加偏重代码细节。

5、重构:不改变代码外部行为的情况下修改代码。

设计模式概述

什么是设计模式:就是已经一套已经被反复使用,多人知晓的套路。

好处:能够读懂源码,学习框架事半功倍。

设计模式分类

GoF设计模式有23个,根据用途分为三类:创建型、结构性、行为型

创建型(5种)

提供创建对象的机制,提升已有代码的灵活性和可复用性

常用:单例模式、工厂模式(工厂方法和抽象工厂)、建造者模式

不常用:原型模式

结构性模式(7种)

介绍如何将对象和类祖冲较大的结构,并同时保持结构的灵活和高效

常用:代理、桥接、装饰、适配器

不常用:外观、组合、享元

行为模式(11种)

负责对象间的高效沟通和职责传递的委派

常用:观察者模式、模板模式、策略模式、责任链模式、迭代器模式、状态模式

不常用:访问者模式、备忘录模式、命令模式、解释器模式、中介模式。

UML图概述

统一建模语言,用来设计软件的可视化建模语言

UML分类

静态结构图:类图、对象图、组件图、部署图

动态行为图:状态图、活动图、时序图、协作图、组件图。

类图

反映的是类结构和类之间的关系为目的。

类结构表示法

image-20241126013252784

如果是抽象类,类名和方法名要用斜体来表示

接口类的话

image-20241126013229376

类关系的表示方法

image-20241126013204578

实现关系:

image-20241126013305015

泛化关系:

关联关系:

image-20241126013510485

image-20241126013336512

image-20241126013326335

聚合关系:

image-20241126013353827

组合关系:

image-20241126013554694

依赖关系:

image-20241125163701253

image-20241126013532082

多用组合少用继承。

活动图

image-20241218172841204

交互图(顺序图+通信图)

顺序图

image-20241218173952042

通信图

image-20241218174014873

组件图

image-20241218174238865

包图

image-20241218174335234

状态图

image-20241218174715481

部署图

image-20241218174849795

六大设计原则

简称为SOLID

image-20241126013543552

单一职责原则

一个类或者模块只实现一个功能。不要设计大而全的类。设计功能单一的类。(要根据实际业务场景分析)

image-20241126020106801

开放封闭原则

对扩展开放、对修改关闭

当增添新的需求我们应该是扩展而不是修改

image-20241126021122008

里氏替换原则

什么是替换?

如果方法的参数是一个接口类型,这个方法可以接受所有实现过这个接口的实现类(多态)

与期望行为一致的替换:在不了解派生类的情况下,进通过接口或者基类的方法,即可清楚的知道方法的行为,而不管那种派生类的视线,都与接口或基类方法的期望行为一致。

例子:

image-20241126071714468

image-20241126071641543

Context是调用类

接口隔离原则

一个类对另一个类的依赖应该建立在最小的接口上

通俗解释:要为各个类建立他们需要的专用接口,而不要视图去建立一个很庞大的接口供所有依赖他的类去调用。

例子:

image-20241126072227669

image-20241126072554581

image-20241126072604375

依赖倒置原则

在软件设计中要以抽象为基础搭建起来的架构。

案例:

image-20241126073006365

image-20241126073139612

如果是这样设计,用户就只能用那一种类型的配件了,想要替换很麻烦,所以如果用依赖倒置原则来设计

image-20241126073514276

image-20241126075555447

迪米特法则

不该有直接依赖关系的类之间,不要有依赖,有依赖的类之间,依赖必要的接口。

多使用中间人。

案例:

image-20241126075747294

image-20241126080118392

创建型模式(5种)

这类模式提供创建对象的机制,能够提升已有代码的灵活性和复用性

  • 常用的有:单例模式、工厂模式、建造者模式
  • 不常用的有:原型模式。

单例设计模式

只有一个实例对外提供服务,而这个类被称为单例类。

使用单例模式要做的两件事

1、保证一个类只有一个实例

2、为该实例提供一个全局访问点

单例模式结构

image-20241126104900001

单例模式之饿汉式

在类加载期间初始化私有的静态实例,保证instance实例创建过程是线程安全的。

特点:不支持延时加载,获取实例对象的速度比较快,但是如果对象比较大,而且一致没有使用就会造成内存的浪费。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Singleton_01{
//1、私有的构造方法
private Singleton_01(){

}

//2、在本类中创建私有静态的全局对象
private static Singleton_01 instance = new Singleton_01();

//3、提供一个全局访问点、供外部获取单例对象
public static Singleton_01 getInstance(){
return instance;
}
}

测试类

1
2
3
4
5
6
7
8
public class Test{
@Test
public void test01(){
Singleton_01 instance = Singleton_01.getInstance();
Singleton_01 instance1 = Singleton_01.getInstance();
System.out.println(instance == instance1) // true
}
}

单例模式之懒汉式(线程不安全)

特点:支持延时加载,只有调用getInstance方法时,才会创建对象。
当高并发的时候不能保证只有一个实例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class Singleton_02{
//1、私有的构造方法
private Singleton_02(){

}

//2、在本类中创建私有静态的全局对象
private static Singleton_02 instance;

//3、通过判断对象是否被初始化,来选择是否创建对象。
public static Singleton_02 getInstance(){
if(instance == null){
instance = new Singleton_02();
}
return instance;

}
}

不能保证单例的原因

image-20241126110933362

懒汉式如何保证线程安全

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class Singleton_03{
//1、私有的构造方法
private Singleton_03(){

}

//2、在本类中创建私有静态的全局对象
private static Singleton_03 instance;

//3、通过添加synchronize,保证多线程模式下的单例对象的唯一性
public static synchronized Singleton_03 getInstance(){
if(instance == null){
instance = new Singleton_03();
}
return instance;

}
}

使用synchronzied锁,锁住创建单例对象的方法,防止多个线程同时调用。

缺点:因为对getInstance()方法加了锁,导致并发度很低。

单例模式-双重校验

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
public class Singleton_04{
//1、私有的构造方法
private Singleton_04(){

}

//2、在本类中创建私有静态的全局对象
// 使用volatile保证变量可见性,屏蔽指令重排序
private volatile Singleton_04 instance;

//3、通过添加synchronize,保证多线程模式下的单例对象的唯一性
public static Singleton_04 getInstance(){

//第一次判断,如果不为null,不进入抢锁阶段,直接返回实例
if(instance == null){

synchronized(Singleton_04.class){
//抢到锁之后再次进行判断,判断是否为null
if(instance == null){
instance = new Singleton_04();
/**上面的创建对象的代码,在JVM中分为三步:
1、分配内存空间
2、初始化对象
3、将instance指向分配好的内存空间
JVM可能会对上面的步骤进行指令重排序 1,3,2
*/

}
}
}
return instance;

}
}

image-20241126112646554

单例模式-静态内部类

根据静态内部类的特性,同时解决了 延时加载 现成安全的问题 并且代码更加简洁。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Singleton_05{

private Singleton_05{}

//创建静态内部类
private static class SingletonHandler{
//在静态内部类中创建单例,在装载内部类的时候,才会创建单例对象。
private static Singleton_05 instance = new Singleton_05();
}

public static Singleton_05 getInstance(){
return SingletonHandler.instance;
}
}

反射对于单例的破坏

1
2
3
4
5
6
7
8
9
10
11
12
public class Test_Reflect{
public static void main(String[] args){

Class<Singleton_05> clazz = new Singleton_05.class;

Constructor constructor = clazz.getDeclaredConstructor();
constructor.setAccessible(true);//设置为true后,就可以对类中的私有成员进行操作。
Singleton_05 instance = constructor.newInstance();
Singleton_05 instance2 = constructor.newInstance();
System.out.println(instance == instance2);// false
}
}

如何解决反射对于单例的破坏?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class Singleton_05{

private Singleton_05(){
//在构造方法中添加判断
if(SingletonHandler.instance != null){
throw new RuntimeException("不允许非法访问");
}
}

//创建静态内部类
private static class SingletonHandler{
//在静态内部类中创建单例,在装载内部类的时候,才会创建单例对象。
private static Singleton_05 instance = new Singleton_05();
}

public static Singleton_05 getInstance(){
return SingletonHandler.instance;
}
}

序列化对于单例的破坏

image-20241126124626030

解决方法:在单例类中定义readResolve方法,就可以解决序列化对单例的破坏。

为什么是这样解决?因为通过读源码的过程中得知,序列化和反序列化的过程中,程序会判断是否有readResolve方法,如果有就执行该方法,否则就会创建一个新的对象。

单例模式-枚举方式

1
2
3
4
5
6
7
public enum Singleton_06{
INSTANCE;
private Object data;
public static Singleton_06 getInstance(){
return INSTANCE;
}
}

可以阻止反射的破坏:在反射方法中不允许使用反射创建枚举对象

可以阻止序列化的破坏:在序列化的时候仅仅将枚举对象的name属性输出到了结果中,反序列化的时候,就会通过Enum的valueOf方法,来根据名字取查找对应的枚举对象。

单例模式总结

image-20241126131556726

工厂模式

在工程模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且通过使用一个公共的接口来指向新创建的对象。

简单工厂被分成了三种:简单工厂,工厂方法,抽象工厂。

工厂模式——原始实现方式1

案例:模拟发放奖品业务

image-20241126153157569

首先是最原始的实现方式不适用设计模式的方法进行开发

这些是实体类

image-20241126153639575

image-20241126164035069

简单工厂模式

简单工厂不是一种设计模式,它是通过使用静态方法接受不同的参数来返回不同的实例对象(多态)

实现方式:定义一个工厂类,根据传入的参数不同返回不同的实例,被创建的实例具有共同的父类或接口。

简单工厂包含:

1、抽象产品

2、具体产品

3、具体工厂

image-20241127001437791

这样子设计是有问题的

不符合开闭原则,没有扩展性。

image-20241127003620137

image-20241127005038992

工厂方法模式

概念:定义一个用于创建对象的接口,让子类决定实例化哪个 产品类对象。工厂方法使一个产品类的实例化延迟到其工厂的子类。

image-20241127005229633

但是这个还是有个小问题就是在工厂类中还是用ifelse来判断生产对象。

可以设计一个工厂的工厂,在工厂的工厂类里面专门进行逻辑判断,这样就符合了开闭原则。

image-20241127011641647

抽象工厂模式

image-20241127011811930

image-20241127011839845

image-20241127012117246

image-20241127012651946

抽象工厂模式中,每一个具体工厂都提供了多个工厂方法,用于生产多种不同类型的产品,这些产品构成一个产品族,

image-20241127105726228

建造者模式

定义:将一个复杂对象的构建和表示分离,使得同样的构建过程可以创建不同的表示。

image-20241127110134489

建造者模式原理:

image-20241127110444987

案例:

image-20241127125204484

问题二:当一个方法需要传入多个参数的时候,如何重构。

image-20241127130514399

image-20241127130756045

image-20241127131248902

image-20241127131459946

image-20241127131814889

原型模式

原型模式:用一个已经创建的实例作为原型,通过复制该原型对象来创建一个和原型对象相同的新对象。

image-20241201212522955

原型模式主要解决的问题:

image-20241201212556853

原型模式结构图

image-20241201212846258

浅克隆和深克隆

浅克隆:克隆对象中的所有变量的值与原型对象的值完全相同(引用数据类型变量中存储的地址也是完全一致的)

image-20241201213533741

深克隆:克隆对象的所有基本数据类型变量含有的值与原型对象完全一致(不包含引用数据类型)。

image-20241201213711436

例子:

模拟某银行电子账单系统的广告信发送功能,广告信的发送都是有一个模板的,从数据库查出客户的信息,然后放到模板中生成一份完整的右键,然后交给发送机进行发送处理。

发送广告邮件UML类图

image-20241201221058777

结构型模式(7种)

一共有7种:代理模式、桥接模式、装饰着模式、适配器模式、门面(外观)模式、组合模式、享元模式。

代理模式

让你能够提供对象的代替品或其占位符。代理控制着对于源对象的访问,并允许将请求提交给对象前后进行一些处理。

image-20241201221819613

image-20241201221902894

image-20241201222214700

桥接模式

将抽象部分与实现部分分离,使他们都可以独立的变化。

装饰模式

享元模式

组合模式

适配器模式

image-20241218181550194

image-20241218181606384·

外观模式

image-20241218181712996

SOA模式

image-20241218185455518

MVC模式

image-20241218185529373