统一建模语言(Unified Modeling language,UML)是一种用于软件系统分析和设计的语言工具,它用于帮助软件开发人员进行思考和记录思路的结果。
UML 本身是一套符号的规定,就像数学符号和化学符号一样,哲学符号用于描述软件模型中的各个元素和它们之间的关系,比如类、接口、实现、泛化、依赖、组合、聚合等;
使用 UML 建模,目前由许多工具,如在线网站 draw.io、processon,客户端工具 Rational Rose,IDE 插件如 IDEA 中的 PlantUML(本文中的类图均是用此插件绘制的),Eclipse 中的 AmaterasUML 等等;
UML 图分类:
在 UML 类图中,类一般有三部分组成:
类名,每个单词首字母大写
属性(Attributes),表示方式:可见性 名称: 类型[ = 默认值]
可见性对应符号:
+
#
-
*
类的操作(Operation),表示方式:可见性 名称([参数列表])[: 返回值类型]
可见性对应的符号与上述属性中相同,构造方法无返回值类型;
⭐内部类,Java 语言中允许出现内部类,所以有时候类图会有第四部分;
关联关系(Association),是类与类之间最常用的一种关系,它是一种结构化关系,用于表示一类对象与另一类对象之间有联系;关联关系有如下六种:
双向关联,可以有两个角色名,在 UML 中用直线表示;
单向关联,只有一个角色名,在 UML 中用直线加带箭头表示;
自关联,类的属性对象类型为该类本身,是单向关联的一种特例;
多重性关联,又称重数性关联关系(Multiplicity),表示一个类的对象与另一个类的对象连接的个数,在 UML 中可以直接在关联直线上增加一个数字表示与之对应的另一个类的对象个数;
多重性表示方法列表(T = 表示另一个类的一个对象)
表示方式 | 多重性说明 |
---|---|
1..1 |
T 只与一个该类对象有关系 |
0..* |
T 与零个或多个该类对象有关系 |
1..* |
T 与一个或多个该类对象有关系 |
0..1 |
T 没有或只与一个该类对象有关系 |
m..n |
T 与最少 m、最多 n 个该类对象有关系(m ≤ n ) |
聚合关系(Aggregation),表示一个整体与部分的关系,在 UML 中用带空心菱形的直线表示;
组合关系(Composition),表示类之间整体和部分的关系,组合关系中部分和整体具有统一的生命周期(生存期),在 UML 中用带实心菱形的直线表示;
依赖关系(Dependency),是一种使用关系,在 UML 中用带箭头的虚线表示;
泛化关系(Generalization),即继承关系,也称为“is-a-kind-of”关系,在 UML 中用带空心三角形的直线表示;
接口与实现关系,接口之间也可以有继承关系和依赖关系,但是接口与类之间还存在实现关系(Realization),在 UML 中用带空心三角形的虚线表示;
与其说 Plant UML 是画出来的,倒不如说它是写出来的,绘制的过程由插件自行绘制,用户只需要写出类的属性、操作等,以及类之间的关系,该插件将会自行绘制出一副可供阅读的 UML 类图;
可以 参考官网,以及 官方指南 阅读使用;
在 Plant UML 中:
--
代表实线,-
的数量代表类图中该类距离另一个关系类的远近;
..
代表虚线,.
的数量代表类图中该类距离另一个关系类的远近;
|>
和<|
代表空心三角形,需与实线或虚线搭配使用表示 泛化关系 或 实现关系;
o
代表空心菱形,需要与实线搭配使用代表 聚合关系
*
代表实心菱形,需要与实线搭配使用代表 组合关系
'
代表注释,注释只有行级注释(单行)
interface
、abstract
、enum
、class
分别代表声明接口、抽象类、枚举、类
note
笔记,类似于注释,用法如下:# 用法一:在一个类(类名为 ClassName 的)的 上、下、左、右 位置写一个注释 note [top | bottom | left | right] of ClassName 这里是笔记部分 end note # 用法二:写好一个笔记,命名为 noteName(不能重复) note as noteName 这里是笔记部分 end note # 然后可以将笔记关联到类 ClassName(也可以不指定) noteName .. ClassName # 用法三:紧跟在类/接口声明之后 class A note left: "这里是笔记部分" # 用法四:属性/方法注释,通过 类名::[属性名|方法名] 来指向类的属性或方法,具体用法同用法一和用法二,当方法名相同时,需要把参数列表也加上 note right of A::counter This member is annotated end note note right of A::"start(int timeoutms)" This method with int end note note right of A::"start(Duration timeout)" This method with Duration end note
""
用在关联线上,表示一些含义,紧跟在类之后,例如多重性关联中用于说明类之间的连接个数;# 例如一个页面中有零个多或个按钮,但是一个按钮只属于一个页面 From "1..1" --> "0..*" Button : "关联线中间说明"
PlantUML 默认自动将方法和属性重新分组,可以使用分隔符
.. | == | -- | __
来对方法或者属性进行重排;class User { .. Simple Getter .. + getName() + getAddress() .. Some setter .. + setName() __ private data __ int age -- encrypted -- String password }
@startuml
'https://plantuml.com/class-diagram
interface D
A --- B: "双向关联"
A --> B: "单向关联"
A "1..1" --> "0..*" B: "多重性关联"
A o-- B: "A 聚合 B"
A *-- B: "A 组合 B"
A ..> B: "A 依赖 B"
A --|> B: "A 继承 B"
C -> C: "自关联"
C ..|> D: "C 实现 D"
note bottom of D
笔记
可换行
可用部分HTML
<u>下划线</u>
<size:15>size字体大小</size>
<color:green>颜色</color>
end note
@enduml
A -- B
A --> B
C --> C
A "1..1" --> "0..*" B
A o-- B
A *-- B
A 依赖 B:A ..> B
A 继承 B:A --|> B
C 实现 D:C ..|> D
设计模式包含了面向对象的精髓:“懂了设计模式,你就懂了面向对象分析和设计(OOA/D)的精要”
编写软件过程中,程序员面临着来自 耦合性、内聚性以及可维护性、可拓展性、重用性、灵活性等多方面的挑战,设计模式是为了让程序(软件)具有更好的:
单一职责原则 def:
- 一个对象应该只包含单一的职责,并且该职责该完整地封装在一个类中;
- 易理解版: 就一个类而言,应该仅有一个引起它变化的原因;
单一职责原则可以:
接口隔离原则 def: 客户端不应该依赖那些它不需要的接口,即一个类对另一个类的依赖应该建立在最小的接口上;
注意:该定义中的接口指的是所定义的方法;
有如下类图:
通过接口隔离原则将上述类图进行重构:
依赖倒转原则 def:
- 高层模块不应该依赖低层模块,他们都应该依赖抽象。抽象不应该依赖于细节,细节应该依赖于抽象;
- 易理解版: 要针对接口编程,不要针对实现编程;
里氏代换原则 def:
- 如果对每一个类型为 S 的对象 O1,都有类型为 T 的对象 O2,使得以 T 类型定义的所有程序 P 在所有对象 O1 都替换 O2 时,程序 P 的行为没有变化,那么类型 S 是类型 T 的子类型;
- 易理解版: 所有引用基类(父类)的地方必须能透明的使用其子类的对象;
开闭原则 def: 一个软件实体应当对扩展开放,对修改关闭;
迪米特法则 def:
- 一个软件实体应当尽可能少的与其他实体发生相互作用;
- 易理解版: 只与直接的朋友通信;其中,直接的朋友指:
- 当前对象本身(this)
- 以参数形式传入到当前对象方法中的对象
- 当前对象的成员对象
- 如果当前对象的成员对象是一个集合,那么集合中的元素也都是朋友
- 当前对象所创建的对象(返回值)
迪米特法则的核心是 降低系统的的耦合度,使类与类之间保持松散的耦合关系;
每个类都减少了不必要的依赖,迪米特法则只是要求降低类间(对象间)耦合关系,并不是要求完全没有依赖关系;
迪米特法则的主要用途在于控制信息的过载;
在将迪米特法则运用到系统设计中时,需要注意:
合成复用原则 def: 尽量使用对象组合(组合关系/聚合关系),而不是继承来达到复用的目的;又称为 组合/聚合服用原则
通过关联关系(包括组合关系和聚合关系)来使用一些已有的对象,使之成为新对象的一部分;通过委派调用已有对象的方法达到复用其已有功能的目的;要尽量使用组合/聚合关系,少用继承;
单例模式(Singleton Pattern) def: 单例模式确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例,这个类称为单例类;
使用场景:需要频繁的进行创建和销毁的对象、创建对象时耗时过多或耗费资源过多(如:重量级对象),但又经常用到的对象、工具类对象、频繁访问数据库或文件的对象(比如数据源、session 工厂等)
实际上有 7 中实现单例模式的方法:饿汉式(静态常量)、饿汉式(静态代码块)、懒汉式(非线程安全)、懒汉式(同步方法)、双重检查(懒汉式)、
// 饿汉式(静态常量)
class Singleton {
// 1.构造器私有化,外部不能 new
private Singleton() {}
// 2. 本类内部创建对象实例
private final static Singleton instance = new Singleton();
// 3.提供一个静态的公有方法,返回实例对象
public static Singleton getInstance() {
return instance;
}
}
// 饿汉式(静态代码块)
class Singleton {
private Singleton() {}
private static Singleton instance = null;
static { // 在静态代码块中创建单例对象
instance = new Singleton();
}
public static Singleton getInstance() {
return instance;
}
}
// 懒汉式(非线程安全)
class Singleton {
private static Singleton instance = null;
private Singleton() {}
public static Singleton getInstance() {
if (instance == null) { // 将对象的创建延迟到调用时
instance = new Singleton();
}
return instance;
}
}
// 懒汉式(线程安全,同步方法)
class Singleton {
private static Singleton instance = null;
private Singleton() {}
// 提供一个静态的公有方法,加入同步处理代码,解决线程安全问题
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
// 懒汉式(线程安全,同步代码块)
class Singleton {
private static Singleton instance = null;
private Singleton() {}
// 提供一个静态的公有方法,加入同步处理代码,解决线程安全问题
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
instance = new Singleton();
}
}
return instance;
}
}
双重检查 Double-Check 概念是多线程开发中经常使用到的;
双重检查锁定背后的理论是完美的。不幸地是,现实完全不同。双重检查锁定的问题是:并不能保证它会在单处理器或多处理器计算机上顺利运行
// 双重检查
class Singleton {
// volatile 禁止指令重排(JVM 做的优化措施)
private static volatile Singleton instance = null;
private Singleton() {}
// 提供一个静态的公有方法,加入双重检查代码,解决线程安全问题
public static Singleton getInstance() {
if (instance == null) {
synchronized(Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
在 Java 中,加入 volatile 关键字是很有必要的!
Java 平台的内存模型允许“无序写入”,
因为 instance = new Singleton() 该语句并非原子操作,实际上分了三个步骤:
- 给 instance 分配内存
- 调用 Singleton 的构造函数来初始化成员变量
- 将给 Singleton 对象指向分配的内存空间(此时singleton才不为null)
由于虚拟机的指令重排序:执行顺序可能是 1、3、2,分配内存并修改指针后未初始化;
第一个线程初始化对象到一半,第二个线程来发现已经不是 null 了就直接返回了 实际上该对象此时还没有完全初始化 可能会出现这个问题
// 静态内部类
class Singleton {
private Singleton() {}
// 静态内部类,有一个静态属性 Singleton
private static class SingletonInstance {
private static final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance() {
return SingletonInstance.INSTANCE;
}
}
静态内部类在 Singleton 类被装载时并不会立即实例化,而是在需要实例化时,调用 getInstance 方法,才会装载 SingletonInstance 类,从而完成 Singleton 的实例化;
// 使用枚举,可以实现单例
enum Singleton {
INSTANCE;
public void method() {
// 方法
System.out.println("Fucking");
}
}
简单工厂模式 def: 又称为静态工厂方法模式,可以根据参数的不同返回不同类的实例;
简单工厂模式专门定义一个类来负责创建其他类的实例,被创建的实例通常都具有共同的父类;其核心是工厂类;
下面类图是一个披萨订购的类图,当需要新增一种口味的披萨时,需要修改较多的代码,且违反了开闭原则(对扩展(提供方)开放,对修改(使用方)关闭)
使用简单工厂模式对上面的结构改进:
把创建 Pizza 对象封装到一个类中,当有新的 Pizza 种类时,只需要修改该类即可,其他原有的代码都不需要改动,改进后的类图如下:
工厂方法模式(Factory Method Factory) def: 又称工厂模式,也叫虚拟构造器模式或多态工厂模式;工厂父类负责定义创建产品对象的公共接口,而工厂子类负责生成具体的产品对象;
目的是 :将产品类的实例化操作延迟到工厂子类中完成,即通过子类工厂来确定究竟应该实例化哪一个具体产品类;
在简单工厂模式中的实例披萨项目中增加新的需求:客户在点披萨时,可以选择不同口味的披萨,比如:北京的奶酪pizza、北京的胡椒pizza或者是伦敦的奶酪pizza、伦敦的呼叫pizza;
抽象工厂模式 def: 提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类,抽象工厂模式又称为 Kit 模式;
抽象工厂模式是工厂方法模式的泛化版,在工厂方法模式中,每一个具体工厂只能生产一种具体产品类,而在抽象工厂方法模式中,每一个具体工厂可以生产多个具体产品类;
工厂方法模针对的是同一个产品等级结构(如都是 Pizza),而抽象工厂模式则需要面对多个产品等级结构(如海尔工厂:海尔电冰箱,海尔电视,海尔空调);
原型模式(Prototype Pattern) def: 用原型实例指定创建对象的种类,并通过复制这些原型创建新的对象;
包含如下角色:
Java 中通过覆盖 Object 类的 clone() 方法可以实现 浅克隆(浅拷贝);
浅拷贝仅复制基本数据类型,深拷贝除了复制基本数据类型外,还复制了引用数据类型;
浅拷贝:
深拷贝
对于基本数据类型,直接进行值传递(即将该属性的值复制一份给新的对象);
为所有引用数据类型的成员变量申请存储空间,并复制每个引用数据类型成员变量所引用的对象,直到该对象可达所有的对象;
深拷贝实现方式:
序列化(Setialization)是将对象写到流的过程,写到流中的对象是原有对象的一个拷贝,而原对象仍然存在于内存中;
public class Sheep implements Cloneable, Serializable {
private String name;
private List list;
// 使用默认的克隆方法实现浅拷贝
protected Object clone() throws CloneNotSupportedException {
Sheep sheep = null;
try {
sheep = (Sheep) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return sheep;
}
// 使用序列化的方式实现深拷贝
protected Object deepClone() {
ByteArrayOutputStream bos = null;
ObjectOutputStream oos = null;
ByteArrayInputStream bis = null;
ObjectInputStream ois = null;
Object object = null;
try {
// 序列化
bos = new ByteArrayOutputStream();
oos = new ObjectOutputStream(bos);
oos.writeObject(this);
// 反序列化
bis = new ByteArrayInputStream(bos.toByteArray());
ois = new ObjectInputStream(bis);
object = ois.readObject();
} catch (Exception e) {
e.printStackTrace();
}
return object;
}
}
public class Client { // 测试类
public static void main(String[] args) throws CloneNotSupportedException {
Sheep sheep = new Sheep("tom");
List<String> list = new ArrayList<>();
list.add("123");
list.add("456");
sheep.setList(list);
System.out.println("--------------- 使用默认的 clone 方法 ---------------");
Sheep sheep1 = (Sheep) sheep.clone();
System.out.println(sheep);
System.out.println(sheep1);
list.add("Test: 浅拷贝"); // 若是浅拷贝,则 sheep1 的 list 与 sheep 相同
System.out.println("----------- 更改引用数据类型后 -----------");
System.out.println("sheep: " + sheep);
System.out.println("sheep1: " + sheep1);
System.out.println("--------------- 序列化实现深拷贝 ---------------");
Sheep sheep2 = (Sheep) sheep.deepClone();
System.out.println("sheep: " + sheep);
System.out.println("sheep2: " + sheep2);
list.add("Test: 深拷贝 or 浅拷贝"); // 若是深拷贝,则 sheep2 的 list 与 sheep 不同
System.out.println("----------- 更改引用数据类型后 -----------");
System.out.println("sheep: " + sheep);
System.out.println("sheep2: " + sheep2);
}
}
建造者模式(Builder Pattern)def: 将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。又称为 生成器模式;
包含如下角色:
buildPartX()
,它们用于创建复杂对象的各个部件;另一类方法是 getResult()
,它们用于返回复杂对象;抽象建造者既可以是抽象类,也可以是接口;construct()
建造方法中调用建造者对象的部件构造与装配方法,完成复杂对象的构造;客户端一般只需要与指挥者进行交互;利用建造者模式描述 KFC 如何创建套餐:套餐是一个复杂对象,它一般包含主食(如汉堡、鸡肉卷等)和饮料(如果汁、可乐等)等组成部分,不同的套餐有不同的组成部分,而 KFC 的服务员可以根据顾客的要求,一步一步装配这些组成部分,构造一份完整的套餐,然后返回给顾客;
适配器模式(Adapter Pattern)def: 将一个接口转换成客户希望的另一个接口,适配器模式使接口不兼容的那些类可以一起工作,其别名为包装器(Wrapper),因而适配器模式也称 包装器模式;
适配器模式既可以作为 类结构型模式,也可以作为 对象结构型模式;
包含如下角色:
生活实例,家用电插座上的电压都是 220V 直流电,手机等电子设备充电所需电压一般都小于 220V,如手机充电器大部分都是 5V 直流电;需要通过电源适配器使用;
根据《合成复用原则》,使用关联关系类代替继承关系;
缺省适配器模式(Default Adapter Pattern)def: 当不需要全部实现接口提供的方法时,可先设计一个抽象类实现该接口,并未接口中的每个方法提供一个默认实现(空方法),那么该抽象类的子类可有选择地覆盖父类的某些方法来实现需求;
桥接模式(Bridge Pattern)def: 将抽象部分与它的实现部分分离,使它们都可以独立地变化;又称为 柄体(Handle and Body)模式 或 接口(Interface)模式; 桥接模式是一种 对象结构型模式;
桥接模式的重点是将抽象化与实现脱藕;
Bridge 模式基于 类的最小设计原则,通过使用封装、聚合及继承等行为让不同的类承担不同的职责;
有如下角色:
装饰模式(Decorator Pattern)def: 动态地给一个对象增加一些额外的职责,就增加对象功能来说,装饰模式比生成子类实现更加灵活;装饰模式是一种 对象结构型模式;
通过关联关系,将一个类的对象 objA 嵌入到另一个新对象 objB 中,由 objB 来决定是否调用嵌入对象 objA 的行为并扩展新的行为,称 objB 为装饰器(Decorator);
有如下角色:
组合模式(Composite Pattern): 组合多个对象形成树形结构以表示 “部分—整体” 的结构层次;又称 “部分—整体” 模式;组合模式是一种 对象结构型模式;
有如下角色:
外观模式(Facade Pattern)def: 为子系统中的一组接口提供一个统一的入口,外观模式又称为 门面模式; 外观模式是一种 对象结构型模式;
有如下角色:
享元模式(Flyweight Pattern) def: 运用共享技术有效地支持大量细粒度对象的复用;享元模式是 对象结构型模式;
代理模式(Proxy Pattern)def: 给某一个对象提供一个代理,并由代理对象控制对原对象的引用。代理模式的英文叫做 Proxy 或 Surrogate,它是一种 对象结构型模式;
代理模式有主要三种不同形式:静态代理、动态代理(JDK 代理、接口代理)和 Cglib 代理(可以在内存中动态的创建对象,而不需要实现接口)
有如下角色:
模板方法模式(Template Method Pattern)def: 定义一个操作中算法的骨架,而将一些步骤延迟到子类中,模板方法模式使得子类可以不改变一个算法结构即可重定义该算法的某些步骤;模板方法模式是一种 类行为型模式;
有如下角色:
一个模板方法是定义在抽象类中的、把基本操作方法组合在一起形成一个总算法或一个总行为的过程;
基本方法是实现算法各个步骤的方法,是模板方法的组成部分:
抽象方法: 由抽象类声明、具体子类实现
具体方法: 由抽象类或子类声明并实现,其子类可以进行覆盖,也可以直接继承
钩子方法: 由一个抽象类或具体类声明并实现,其子类可能会加以拓展。通常在父类中给出的实现是一个空实现,并以该空实现作为方法的默认实现;在模板方法模式中,钩子方法有两类:
与一些具体步骤 “挂钩”,通常返回值是 boolean
类型的,方法名一般为 isXXX()
,用于对某个条件进行判断。一般情况下钩子方法返回值都是 true,如果不希望某方法执行,可以在子类中覆盖钩子方法,将返回值置为 false 即可;
public void template() {
open();
display();
if (isPrint()) {
print();
}
}
public isPrint() {
return true;
}
实现体为空的具体方法,子类可以根据需要覆盖或继承这些钩子方法;
可以将模板方法声明为 final
,防止子类覆盖核心方法;
命令模式(Command Pattern)def: 将一个请求封装为一个对象,从而使我们可用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可撤销的操作;别名为动作(Action)模式或事务(Transaction)模式;命令模式是一种 对象行为型模式;
有如下角色:
execute()
等方法,通过这些方法可以调用请求接收者的相关操作;访问者模式(Visitor Pattern)def: 表示一个作用于某对象结构中各个元素地操作,它使我们可以在不改变个元素地类地前提下定义作用于这些元素地新操作;访问者模式是一种 对象行为型模式;
有如下角色:
accept()
方法,该方法以一个抽象访问者作为参数accept()
方法,在其中调用访问者的访问方法以便完成对一个元素的操作迭代器模式(Iterator Pattern)def: 提供一种方法来访问聚合对象,而不用暴露这个对象的内部表示,其别名为游标(Cursor);迭代器模式是一种 对象行为型模式;
有如下角色:
first()
,用于访问下一个元素的 next()
,用于判断是否还有下一个元素的 hasNext()
,用于获取当前元素的 currentItem()
;createIterator()
方法用于创建一个迭代器对象;createItreator()
方法并返回一个与该具体聚合对相应的具体迭代器 ConcreteIterator
实例;聚合对象职责:
public interface MyCollection<T> {
MyIterator<T> createIterator();
}
public interface MyIterator<T> {
void first();
void next();
boolean isLast();
T currentItem();
}
public class NewCollection<T> implements MyCollection<T> {
private T[] obj;
public NewCollection(T[] obj) {
this.obj = obj;
}
public MyIterator<T> createIterator() {
return new NewIterator<T>();
}
private class NewIterator<T> implements MyIterator<T> {
private int currentIndex = 0;
public void first() {
currentIndex = 0;
}
public void next() {
if (currentIndex < obj.length) {
currentIndex++;
}
}
public boolean isLast() {
return currentIndex >= obj.length;
}
public T currentItem() {
return (T) obj[currentIndex];
}
}
}
public class MyObject {
private String name;
public MyObject(String name) {
this.name = name;
}
@Override
public String toString() {
return "MyObject{" +
"name='" + name + '\'' +
'}';
}
}
public class Client {
public static void process(MyCollection<MyObject> collection) {
MyIterator<MyObject> i = collection.createIterator();
while (!i.isLast()) {
System.out.println(i.currentItem().toString());
i.next();
}
}
public static void main(String[] args) {
MyObject[] objects = new MyObject[10];
for (int i = 0; i < 10; i++) {
objects[i] = new MyObject("我是甜狗 " + i + " 号");
}
MyCollection<MyObject> collection = new NewCollection<>(objects);
process(collection);
}
}
观察者模式(Pattern)def: 定义对象间的一种一对多依赖关系,使得每当一个对象状态发生改变时,其相关依赖对象皆得到通知并自动更新;又称 发布—订阅(Publish/Subscribe)模式、模型—视图(Model/View)模式、源—监听(Source/Listener)模式或从属者(Dependents)模式; 观察者模式是一种 对象行为型模式;
观察者模式在 Java 语言中十分重要,在 JDK 中(java.util
包下)提供了 Observable
类以及 Observer
接口,构成了 Java 语言对观察者模式的支持;
有如下角色:
update()
;update()
方法;利用观察者模式实现:天气更新后各个网站自动更新数据并显示;
MVC 模式就是利用观察者模式实现的 :
中介者模式(Mediator Pattern)def: 用一个中介对象来封装一系列的对象交互,中介者使各对象不需要显示地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互;中介者模式是一种 对象行为型模式;
备忘录模式(Memento Pattern)def: 在不破坏封装的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,这样可以在以后将对象恢复到原先保存的状态;其别名为 Token;备忘录模式是一种 对象行为型模式;
有一下角色:
某系统提供了用户信息操作模块,用户可以修改自己的各项信息,为了使操作过程更加人性化,使用备忘录模式对系统进行改进,使得用户在进行错误操作之后可以恢复到操作之前的状态;
解释器模式(Interpreter Pattern)def: 定义语言的文法,并且建立一个解释器来解释该语言中的句子,“语言” 意思是使用规定格式和语法的代码,它是一种 类行为型模式;
有以下角色:
构造一个于妍姐实习,使得系统可以执行整数间的乘、除和求模运算;如用户输入 3 * 4 / 2 % 4
,输出结果为 2;
抽象表达式类 Node(抽象节点)
public interface Node {
int interpret();
}
终结符表达式类 ValueNode(值节点)
public class ValueNode implements Node {
private int value;
public ValueNode(int value) {
this.value = value;
}
@Override
public int interpret() {
return value;
}
}
抽象非终结符表达式类 SymbolNode(符号节点类)
public abstract class SymbolNode implements Node {
protected Node left;
protected Node right;
public SymbolNode(Node left, Node right) {
this.left = left;
this.right = right;
}
}
非终结符表达式类(乘法、除法、求模节点类)
public class MulNode extends SymbolNode {
public MulNode(Node left, Node right) {
super(left, right);
}
@Override
public int interpret() { // 把这里的符号换成对应的即可
return super.left.interpret() * super.right.interpret();
}
}
解释器封装类 Calculator(计算器类)
public class Calculator {
private String statement;
private Node node;
public void setStatement(String statement) {
this.statement = statement;
}
public void build() {
Node left = null, right = null;
Deque<Node> stack = new ArrayDeque<>();
String[] statementArr = statement.split(" ");
for (int i = 0; i < statementArr.length; i++) {
if (statementArr[i].equalsIgnoreCase("*")) {
left = stack.pop();
int val = Integer.parseInt(statementArr[++i]);
right = new ValueNode(val);
stack.push(new MulNode(left, right));
} else if (statementArr[i].equalsIgnoreCase("/")) {
left = stack.pop();
int val = Integer.parseInt(statementArr[++i]);
right = new ValueNode(val);
stack.push(new DivNode(left, right));
} else if (statementArr[i].equalsIgnoreCase("%")) {
left = stack.pop();
int val = Integer.parseInt(statementArr[++i]);
right = new ValueNode(val);
stack.push(new ModNode(left, right));
} else {
stack.push(new ValueNode(Integer.parseInt(statementArr[i])));
}
}
this.node = stack.pop();
}
public int compute() {
return node.interpret();
}
}
Client 客户类
public class Client {
public static void main(String[] args) {
String statement = "3 * 4 / 2 % 4";
Calculator calculator = new Calculator();
calculator.setStatement(statement);
calculator.build();
int result = calculator.compute();
System.out.println(statement + " = " + result);
}
}
状态模式(State Pattern)def: 允许一个对象在其内部状态改变时改变它的行为,对象看起来似乎修改了它的类;其别名为 状态对象,状态模式是一种 对象行为型模式;
有以下角色:
某 App 有一个抽奖活动,抽一次奖要扣除用户 50 积分,中奖概率为 10%,奖品数量有限,送完抽奖活动截至,活动有四个状态:可以抽奖、不能抽奖、发放奖品和领取奖品;
策略模式(Strategy Pattern)def: 定义一系列算法,将每一个算法封装起来,并让他们可以相互替换,策略模式让算法独立于使用它的客户而变化,也称为政策模式(Policy);策略模式是一种 对象行为型模式;
有如下角色:
某系统提供了一个用于对数组数据进行操作的类,该类封装了对数组的常见操作;以排序操作为例,使用策略模式设计该数组操作类,可以根据需要选择冒泡排序、选择排序、或插入排序,也能够灵活地增加新的排序算法;
职责链模式(Chain of Responsibility Pattern)def: 避免请求发送者与接收者耦合在一起,让多个对象都有可能接受请求,将这些对象连接成一条链,并且沿着这条链传递请求,直到有对象处理它为止;又称为 责任链模式; 是一种 对象行为型模式;
有一个的采购审批 OA 系统,由采购员采购教学器材,如果: