Compare commits
2 Commits
58e0b5f4d1
...
4b7d878e4e
Author | SHA1 | Date |
---|---|---|
|
4b7d878e4e | |
|
0e59f47f48 |
|
@ -9,6 +9,7 @@
|
|||
<file url="file://$PROJECT_DIR$/multithreading1/src/main/resources" charset="UTF-8" />
|
||||
<file url="file://$PROJECT_DIR$/multithreading1/src/main/resources-filtered" charset="UTF-8" />
|
||||
<file url="file://$PROJECT_DIR$/mvc/src/main/java" charset="UTF-8" />
|
||||
<file url="file://$PROJECT_DIR$/pattern/src/main/java" charset="UTF-8" />
|
||||
<file url="file://$PROJECT_DIR$/spring-demo/src/main/java" charset="UTF-8" />
|
||||
<file url="file://$PROJECT_DIR$/src/main/java" charset="UTF-8" />
|
||||
<file url="file://$PROJECT_DIR$/src/main/resources" charset="UTF-8" />
|
||||
|
|
|
@ -231,6 +231,51 @@ class HomeControllerTest {
|
|||
</html>
|
||||
```
|
||||
|
||||
> 静态资源问题,如果用户是访问静态资源,如果直接写在`template`方式引入资源会找不到,因为默认资源都放在`static`目录下载的。
|
||||
|
||||
#### 什么时候该用这种测试
|
||||
|
||||
##### 1. **验证控制器的业务逻辑**
|
||||
|
||||
- `@WebMvcTest` 本质上是用来验证 **控制器层的行为**,而不是渲染出来的页面。它帮助你测试控制器是否能正确处理 HTTP 请求、是否返回正确的视图名称或正确的响应状态码等。
|
||||
- 比如,你可以测试一个 `GET /home` 请求是否返回了预期的视图名称 `"index"`,或者返回的状态码是否是 `200 OK`,而这些正是你在控制器中希望验证的部分。**这与页面渲染无关**,所以它不会显示页面内容,也不会出现渲染时的误差。
|
||||
|
||||
##### 2. **关注点分离**
|
||||
|
||||
- 使用 `@WebMvcTest` 进行单元测试能够将 **Web 层** 与 **业务逻辑层**(Service、Repository)进行解耦。通过这种方式,你可以专注于测试控制器层的 HTTP 请求和响应,而不需要担心 Service 层的逻辑是否正确,或者数据库的连接是否正常。
|
||||
- 控制器层和页面渲染是两个不同的关注点。**控制器层的目标是处理请求、确定视图名称、返回模型数据**等,而页面渲染本身通常由前端技术或模板引擎处理,更多是浏览器中的前端渲染过程。
|
||||
|
||||
##### 3. **通过 Mock 数据模拟请求和响应**
|
||||
|
||||
- 在 `@WebMvcTest` 中,`MockMvc` 会模拟请求并验证响应,它不会执行实际的页面渲染或显示,只会检查你定义的 **控制器输出** 是否符合预期。因此,它能有效避免浏览器渲染过程中的误差。
|
||||
- 比如,`MockMvc` 可以验证你的控制器是否正确返回了某个 HTML 页面的视图名称,而不用关注 HTML 的具体内容。如果你的目标是确保控制器的逻辑没有错误,这种方式是非常高效的。
|
||||
|
||||
##### 4. **测试时更快速且更简洁**
|
||||
|
||||
- 通过 `@WebMvcTest` 测试时,你只加载 Web 层的相关组件,不需要启动整个应用的上下文,因此测试速度通常更快。
|
||||
- 它让你能够快速定位问题。例如,如果测试失败,你可以很清楚地知道是控制器中的视图名称、请求参数的处理,还是其他的 HTTP 相关操作出了问题。反而如果进行实际的浏览器渲染测试,可能涉及到浏览器兼容性、前端细节等问题,会增加复杂度。
|
||||
|
||||
##### 5. **避免不必要的页面渲染**
|
||||
|
||||
- 事实上,控制器的核心功能并不依赖于页面渲染。控制器应该保证的是:
|
||||
- 正确处理请求
|
||||
- 返回适当的视图名称和模型数据
|
||||
- 设置正确的响应状态码和其他 HTTP 相关的参数
|
||||
- 页面渲染通常是通过模板引擎(如 Thymeleaf、JSP)完成的,而 `@WebMvcTest` 仅仅验证的是 **控制器的行为**,例如它返回的是正确的视图名称(而不是真的生成 HTML 页面)。
|
||||
|
||||
##### 6. **不同类型的测试互为补充**
|
||||
|
||||
- 你可以把
|
||||
|
||||
```
|
||||
@WebMvcTest
|
||||
```
|
||||
|
||||
和 端到端测试(如集成测试或 UI 测试)结合起来使用:
|
||||
|
||||
- **`@WebMvcTest`** 用于测试控制器的行为、请求路径、视图名称等。
|
||||
- **端到端测试** 可以通过实际浏览器或工具(如 Selenium)来模拟用户操作,查看页面是否正确渲染,验证 HTML 是否按预期显示。
|
||||
|
||||
## 访问控制
|
||||
|
||||
请求地址时返回对应的网页文件
|
||||
|
@ -1743,4 +1788,41 @@ public class SecurityConfiguration {
|
|||
.build();
|
||||
}
|
||||
}
|
||||
```
|
||||
```
|
||||
|
||||
### CSRF 伪造
|
||||
|
||||
通常在自定义登录页面中加入
|
||||
|
||||
```html
|
||||
<label>
|
||||
<input name="_csrf" placeholder="_csrf" th:value="${_csrf.token}" type="hidden"/>
|
||||
</label>
|
||||
```
|
||||
|
||||
如果需要禁用
|
||||
|
||||
```java
|
||||
.csrf(AbstractHttpConfigurer::disable)// 前后端分离可以禁用
|
||||
```
|
||||
|
||||
### 开发授权服务器
|
||||
|
||||
```xml
|
||||
<!--资源服务器-->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-oauth2-resource server</artifactId>
|
||||
</dependency>
|
||||
<!-- 客户应用 -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-oauth2-client</artifactId>
|
||||
</dependency>
|
||||
<!-- 授权服务器 -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-oauth2-authorization-server</artifactId>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 18 KiB |
Binary file not shown.
After Width: | Height: | Size: 24 KiB |
Binary file not shown.
After Width: | Height: | Size: 21 KiB |
Binary file not shown.
After Width: | Height: | Size: 33 KiB |
Binary file not shown.
After Width: | Height: | Size: 16 KiB |
Binary file not shown.
After Width: | Height: | Size: 9.8 KiB |
|
@ -0,0 +1,737 @@
|
|||
# 设计模式
|
||||
|
||||
## 单例模式
|
||||
|
||||
### 懒汉模式
|
||||
|
||||
#### 双重检查锁定(DCL)
|
||||
|
||||
```java
|
||||
public class Singleton {
|
||||
private static volatile Singleton instance;
|
||||
|
||||
private Singleton() {
|
||||
// 构造函数私有化
|
||||
}
|
||||
|
||||
public static Singleton getInstance() {
|
||||
if (instance == null) { // 第一次检查
|
||||
synchronized (Singleton.class) {
|
||||
if (instance == null) { // 第二次检查
|
||||
instance = new Singleton();
|
||||
}
|
||||
}
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 静态内部类方式
|
||||
|
||||
利用 **静态内部类**(饿汉式)的特性来实现懒汉模式,并且可以保证线程安全。这种方式只有在调用 `getInstance()` 时才会加载内部类,从而实现懒加载。
|
||||
|
||||
```java
|
||||
public class Singleton {
|
||||
private Singleton() {
|
||||
// 构造函数私有化
|
||||
}
|
||||
|
||||
private static class SingletonHolder {
|
||||
private static final Singleton INSTANCE = new Singleton();
|
||||
}
|
||||
|
||||
public static Singleton getInstance() {
|
||||
return SingletonHolder.INSTANCE;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 适用场景
|
||||
|
||||
- 如果单例对象比较重,且只在程序中某些特定情况下才使用,懒汉模式可以带来性能优势。
|
||||
- 在低并发环境或单线程环境下,懒汉模式使用较为简单,不会有多线程安全问题。
|
||||
- 对于高并发且需要频繁访问单例的场景,推荐使用**静态内部类**或**饿汉模式**来避免懒汉模式可能带来的性能问题。
|
||||
|
||||
#### 总结
|
||||
|
||||
懒汉模式有时为了延迟初始化而牺牲了性能,特别是在多线程环境下。为了保证线程安全,可以采用双重检查锁定(DCL)或静态内部类的方式,而**静态内部类方式是最推荐的方式**
|
||||
,它避免了同步开销,同时也能确保线程安全。
|
||||
|
||||
- **优点**:
|
||||
1. 延迟加载,节省资源。
|
||||
2. 内存优化,只有在需要时才实例化。
|
||||
3. 减少不必要的初始化开销。
|
||||
- **缺点**:
|
||||
1. 传统懒汉模式线程不安全(多个线程可能会创建多个实例)。
|
||||
2. 使用同步机制会有性能开销,特别是在多线程环境下。
|
||||
3. 代码复杂性较高,容易引入错误。
|
||||
4. 不适合高并发场景,特别是频繁访问时会影响性能。
|
||||
|
||||
### 饿汉模式
|
||||
|
||||
**饿汉模式**(Eager Initialization)是单例模式的一种实现方式,在该方式中,单例实例在类加载时就已经创建完成,而不是在首次使用时才创建。换句话说,**饿汉模式**
|
||||
的单例实例在类被加载时就会立即初始化,确保了单例模式的唯一性。
|
||||
|
||||
```java
|
||||
public class Singleton {
|
||||
|
||||
// 在类加载时就初始化单例对象
|
||||
private static final Singleton INSTANCE = new Singleton();
|
||||
|
||||
// 私有构造函数,防止外部通过 new 创建实例
|
||||
private Singleton() {
|
||||
}
|
||||
|
||||
// 提供全局访问点
|
||||
public static Singleton getInstance() {
|
||||
return INSTANCE;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 适用场景
|
||||
|
||||
- **简单应用**:适合没有延迟初始化需求的小型应用,或者单例对象的创建非常轻量,不会影响系统性能的场景。
|
||||
- **实例化不耗费太多资源**:如果单例对象的初始化过程非常轻量级且无须延迟初始化,可以使用饿汉模式。
|
||||
- **类加载顺序固定**:如果不在乎单例对象在启动时是否初始化,且单例对象的初始化过程没有严重性能损失,可以选择饿汉模式。
|
||||
|
||||
#### 总结
|
||||
|
||||
- **优点**:简单、线程安全、性能高(没有锁机制)、实现方便。
|
||||
- **缺点**:不支持延迟加载、可能会浪费资源(如果单例对象不常用时)。
|
||||
|
||||
饿汉模式是单例模式中最简单的实现方式,适用于大多数情况下单例对象的创建开销较小,且不需要延迟初始化的场景。如果单例对象的创建非常轻量且不依赖于后期加载时的资源,饿汉模式是一个不错的选择。
|
||||
|
||||
### 枚举类型实现
|
||||
|
||||
**枚举类型实现单例模式**(Enum Singleton)是实现单例模式的另一种方式,优点是更加简洁、可靠,并且避免了许多常见的单例实现方式(如懒汉模式和饿汉模式)可能出现的问题。使用枚举类型的单例实现方式自
|
||||
**Java 5** 引入以来,已经成为了最推荐的单例模式实现方式之一。
|
||||
|
||||
**当你不需要懒加载或延迟初始化时,使用枚举实现单例模式是最理想的选择。**
|
||||
|
||||
```java
|
||||
public enum Singleton {
|
||||
|
||||
// 唯一实例
|
||||
INSTANCE;
|
||||
|
||||
// 可以添加其他的方法
|
||||
public void someMethod() {
|
||||
System.out.println("This is a singleton instance method.");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 使用示例
|
||||
|
||||
```java
|
||||
public class Main {
|
||||
public static void main(String[] args) {
|
||||
// 获取单例对象
|
||||
Singleton singleton = Singleton.INSTANCE;
|
||||
|
||||
// 调用单例方法
|
||||
singleton.someMethod();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 总结
|
||||
|
||||
枚举类型实现单例模式是非常推荐的做法,尤其是在 Java 5 及以后的版本。它简单、线程安全、避免了反射和序列化带来的问题,且保证了单例模式的正确性。对于绝大多数的单例模式实现需求,枚举类型是最安全、最简洁、最优雅的解决方案。
|
||||
|
||||
## 工厂模式
|
||||
|
||||
工厂模式(Factory Pattern)是创建型设计模式的一种,它提供了一种创建对象的方式,而不需要暴露具体的类实例化过程。工厂模式有三种常见的实现方式:
|
||||
|
||||
### 1. **简单工厂模式(Simple Factory Pattern)**
|
||||
|
||||
简单工厂模式是工厂模式的一种简单形式,**它并不属于设计模式中的结构**,它是通过一个工厂类来决定创建哪一个产品类的实例。
|
||||
|
||||
- 特点
|
||||
- 由工厂类来决定实例化哪个产品类,不暴露具体的产品创建过程。
|
||||
- 客户端不需要知道产品类的具体信息,减少了与产品类的耦合。
|
||||
- 优缺点
|
||||
- 优点:客户端不需要关心对象的创建细节,降低了代码的耦合度。
|
||||
- 缺点:工厂类需要维护一个 `if` 或 `switch` 来判断创建哪种产品,如果产品种类增加,工厂类会变得非常庞大且难以维护。
|
||||
|
||||
**示例**:
|
||||
|
||||
```java
|
||||
class Product {
|
||||
void use() {
|
||||
System.out.println("Using the product");
|
||||
}
|
||||
}
|
||||
|
||||
class ConcreteProductA extends Product {
|
||||
@Override
|
||||
void use() {
|
||||
System.out.println("Using Product A");
|
||||
}
|
||||
}
|
||||
|
||||
class ConcreteProductB extends Product {
|
||||
@Override
|
||||
void use() {
|
||||
System.out.println("Using Product B");
|
||||
}
|
||||
}
|
||||
|
||||
class SimpleFactory {
|
||||
public Product createProduct(String type) {
|
||||
if (type.equals("A")) {
|
||||
return new ConcreteProductA();
|
||||
} else if (type.equals("B")) {
|
||||
return new ConcreteProductB();
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class Client {
|
||||
public static void main(String[] args) {
|
||||
SimpleFactory factory = new SimpleFactory();
|
||||
Product product = factory.createProduct("A");
|
||||
product.use();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 2. **工厂方法模式(Factory Method Pattern)**
|
||||
|
||||
工厂方法模式是工厂模式的核心。它通过定义一个接口来创建对象,由子类来决定实例化哪一个类。工厂方法模式将简单工厂中的创建产品的逻辑分散到了各个具体的工厂类中。
|
||||
|
||||
- 特点
|
||||
- 定义一个创建对象的接口,但让子类来决定具体实例化哪一个产品。
|
||||
- 通过继承和多态来解决简单工厂的缺点,避免了在一个类中维护大量的 `if` 或 `switch` 语句。
|
||||
- 优缺点
|
||||
- 优点:符合开闭原则,增加新产品时只需要添加新的工厂类,不需要修改现有代码。
|
||||
- 缺点:增加了类的数量,系统更为复杂。
|
||||
|
||||
**示例**:
|
||||
|
||||
```java
|
||||
interface Product {
|
||||
void use();
|
||||
}
|
||||
|
||||
class ConcreteProductA implements Product {
|
||||
@Override
|
||||
public void use() {
|
||||
System.out.println("Using Product A");
|
||||
}
|
||||
}
|
||||
|
||||
class ConcreteProductB implements Product {
|
||||
@Override
|
||||
public void use() {
|
||||
System.out.println("Using Product B");
|
||||
}
|
||||
}
|
||||
|
||||
abstract class Creator {
|
||||
public abstract Product factoryMethod();
|
||||
}
|
||||
|
||||
class ConcreteCreatorA extends Creator {
|
||||
@Override
|
||||
public Product factoryMethod() {
|
||||
return new ConcreteProductA();
|
||||
}
|
||||
}
|
||||
|
||||
class ConcreteCreatorB extends Creator {
|
||||
@Override
|
||||
public Product factoryMethod() {
|
||||
return new ConcreteProductB();
|
||||
}
|
||||
}
|
||||
|
||||
public class Client {
|
||||
public static void main(String[] args) {
|
||||
Creator creatorA = new ConcreteCreatorA();
|
||||
Product productA = creatorA.factoryMethod();
|
||||
productA.use();
|
||||
|
||||
Creator creatorB = new ConcreteCreatorB();
|
||||
Product productB = creatorB.factoryMethod();
|
||||
productB.use();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 3. **抽象工厂模式(Abstract Factory Pattern)**
|
||||
|
||||
抽象工厂模式是工厂模式的进一步扩展,它提供了一个接口,用于创建一组相关或相互依赖的对象,而无需指定具体的类。与工厂方法模式不同,抽象工厂模式更侧重于一组产品的创建,而不是单个产品的创建。
|
||||
|
||||
- 特点
|
||||
- 提供一个接口,用来创建相关或依赖的多个产品。
|
||||
- 客户端通过抽象工厂来获取一系列的产品对象,而无需知道具体的实现。
|
||||
- 优缺点
|
||||
- 优点:可以方便地更换产品系列(产品的族),符合开闭原则。
|
||||
- 缺点:增加了系统的复杂性,如果新增产品族或产品种类时需要修改现有的代码。
|
||||
|
||||
**示例**:
|
||||
|
||||
```java
|
||||
interface ProductA {
|
||||
void useA();
|
||||
}
|
||||
|
||||
interface ProductB {
|
||||
void useB();
|
||||
}
|
||||
|
||||
class ConcreteProductA1 implements ProductA {
|
||||
@Override
|
||||
public void useA() {
|
||||
System.out.println("Using Product A1");
|
||||
}
|
||||
}
|
||||
|
||||
class ConcreteProductB1 implements ProductB {
|
||||
@Override
|
||||
public void useB() {
|
||||
System.out.println("Using Product B1");
|
||||
}
|
||||
}
|
||||
|
||||
class ConcreteProductA2 implements ProductA {
|
||||
@Override
|
||||
public void useA() {
|
||||
System.out.println("Using Product A2");
|
||||
}
|
||||
}
|
||||
|
||||
class ConcreteProductB2 implements ProductB {
|
||||
@Override
|
||||
public void useB() {
|
||||
System.out.println("Using Product B2");
|
||||
}
|
||||
}
|
||||
|
||||
interface AbstractFactory {
|
||||
ProductA createProductA();
|
||||
|
||||
ProductB createProductB();
|
||||
}
|
||||
|
||||
class ConcreteFactory1 implements AbstractFactory {
|
||||
@Override
|
||||
public ProductA createProductA() {
|
||||
return new ConcreteProductA1();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProductB createProductB() {
|
||||
return new ConcreteProductB1();
|
||||
}
|
||||
}
|
||||
|
||||
class ConcreteFactory2 implements AbstractFactory {
|
||||
@Override
|
||||
public ProductA createProductA() {
|
||||
return new ConcreteProductA2();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProductB createProductB() {
|
||||
return new ConcreteProductB2();
|
||||
}
|
||||
}
|
||||
|
||||
public class Client {
|
||||
public static void main(String[] args) {
|
||||
AbstractFactory factory1 = new ConcreteFactory1();
|
||||
ProductA productA1 = factory1.createProductA();
|
||||
ProductB productB1 = factory1.createProductB();
|
||||
productA1.useA();
|
||||
productB1.useB();
|
||||
|
||||
AbstractFactory factory2 = new ConcreteFactory2();
|
||||
ProductA productA2 = factory2.createProductA();
|
||||
ProductB productB2 = factory2.createProductB();
|
||||
productA2.useA();
|
||||
productB2.useB();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 总结:
|
||||
|
||||
- **简单工厂模式**:由工厂类根据不同的条件决定创建不同的对象,适合产品种类少且变化不大时。
|
||||
- **工厂方法模式**:通过子类来决定产品的创建,适合产品种类较多时,且产品可能会变化。
|
||||
- **抽象工厂模式**:提供多个相关的产品的创建接口,适合需要创建一系列相关产品的场景。
|
||||
|
||||
## 策略模式
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
我们通过策略模式来实现鸭子的飞行和叫声行为,使得它们可以在运行时灵活替换。
|
||||
|
||||
### 飞行行为
|
||||
|
||||
#### 飞行接口
|
||||
|
||||
```java
|
||||
public interface FlyBehavior {
|
||||
/**
|
||||
* 飞的样子
|
||||
*/
|
||||
void fly();
|
||||
}
|
||||
```
|
||||
|
||||
#### 飞行实现
|
||||
|
||||
```java
|
||||
public class FlyNoWay implements FlyBehavior {
|
||||
/**
|
||||
* 飞的样子
|
||||
*/
|
||||
@Override
|
||||
public void fly() {
|
||||
System.out.println("不能飞。。。");
|
||||
}
|
||||
}
|
||||
|
||||
public class FlyWithWings implements FlyBehavior {
|
||||
/**
|
||||
* 飞的样子
|
||||
*/
|
||||
@Override
|
||||
public void fly() {
|
||||
System.out.println("可以飞。。。");
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
### 叫的行为
|
||||
|
||||
#### 叫的接口
|
||||
|
||||
```java
|
||||
public interface QuackBehavior {
|
||||
/**
|
||||
* 怎么叫的
|
||||
*/
|
||||
void quack();
|
||||
}
|
||||
```
|
||||
|
||||
#### 叫的实现
|
||||
|
||||
```java
|
||||
public class MuteQuack implements QuackBehavior {
|
||||
/**
|
||||
* 怎么叫的
|
||||
*/
|
||||
@Override
|
||||
public void quack() {
|
||||
System.out.println("不会叫...");
|
||||
}
|
||||
}
|
||||
|
||||
public class Quack implements QuackBehavior {
|
||||
/**
|
||||
* 怎么叫的
|
||||
*/
|
||||
@Override
|
||||
public void quack() {
|
||||
System.out.println("鸭子叫。。。");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 鸭子
|
||||
|
||||
```java
|
||||
@Setter
|
||||
public abstract class AbstractDuck {
|
||||
// 设置新的叫声行为
|
||||
QuackBehavior quackBehavior;
|
||||
// 设置新的飞行行为
|
||||
FlyBehavior flyBehavior;
|
||||
|
||||
public AbstractDuck() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 展示什么类型的鸭子
|
||||
*/
|
||||
public abstract void display();
|
||||
|
||||
public void performFly() {
|
||||
flyBehavior.fly();
|
||||
}
|
||||
|
||||
public void performQuack() {
|
||||
quackBehavior.quack();
|
||||
}
|
||||
|
||||
public void swim() {
|
||||
System.out.println("鸭子游泳");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 鸭子继承
|
||||
|
||||
```java
|
||||
public class ModelDuck extends AbstractDuck {
|
||||
|
||||
private final String modelDuck = "橡皮🦆";
|
||||
|
||||
public ModelDuck() {
|
||||
super.setFlyBehavior(new FlyNoWay());
|
||||
super.quackBehavior = new MuteQuack();
|
||||
}
|
||||
|
||||
/**
|
||||
* 展示什么类型的鸭子
|
||||
*/
|
||||
@Override
|
||||
public void display() {
|
||||
System.out.println(modelDuck + "...");
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
@Override
|
||||
public void swim() {
|
||||
System.out.println(modelDuck + "不会游泳。。。");
|
||||
}
|
||||
}
|
||||
|
||||
public class WoodDuck extends AbstractDuck {
|
||||
|
||||
private final String modelDuck = "木头🦆";
|
||||
|
||||
public WoodDuck() {
|
||||
super.flyBehavior = new FlyNoWay();
|
||||
super.quackBehavior = new MuteQuack();
|
||||
}
|
||||
|
||||
/**
|
||||
* 展示什么类型的鸭子
|
||||
*/
|
||||
@Override
|
||||
public void display() {
|
||||
System.out.println(modelDuck + "...");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 运行
|
||||
|
||||
```java
|
||||
public class Main {
|
||||
public static void main(String[] args) {
|
||||
ModelDuck modelDuck = new ModelDuck();
|
||||
modelDuck.display();
|
||||
modelDuck.performFly();
|
||||
modelDuck.performQuack();
|
||||
modelDuck.swim();
|
||||
|
||||
System.out.println("--------------------------------");
|
||||
|
||||
AbstractDuck woodDuck = new WoodDuck();
|
||||
woodDuck.display();
|
||||
woodDuck.performFly();
|
||||
woodDuck.performQuack();
|
||||
woodDuck.swim();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||

|
||||
|
||||
## 观察者模式
|
||||
|
||||
观察者模式是一种行为设计模式,它允许一个对象(被观察者)在其状态发生变化时,自动通知所有依赖于它的对象(观察者),而不需要知道这些观察者的具体细节。其主要思想是实现“低耦合”,即对象之间的关系通过接口进行松散连接。
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
为了避免使用过时的类,我们可以手动实现观察者模式。我们需要定义一个“观察者”接口和一个“被观察者”接口,并在被观察者中维护一个观察者的列表,通知观察者更新状态。
|
||||
|
||||
### 观察者
|
||||
|
||||
#### 观察者接口
|
||||
|
||||
```java
|
||||
/**
|
||||
* 观察者接口
|
||||
*/
|
||||
public interface Observer {
|
||||
|
||||
/**
|
||||
* 更新数据
|
||||
*
|
||||
* @param temperature 温度
|
||||
* @param humidity 湿度
|
||||
* @param pressure 气压
|
||||
*/
|
||||
void update(float temperature, float humidity, float pressure);
|
||||
}
|
||||
```
|
||||
|
||||
#### 观察者的实现
|
||||
|
||||
```java
|
||||
public class Observer1 implements Observer {
|
||||
|
||||
/**
|
||||
* 更新数据
|
||||
*
|
||||
* @param temperature 温度
|
||||
* @param humidity 湿度
|
||||
* @param pressure 气压
|
||||
*/
|
||||
@Override
|
||||
public void update(float temperature, float humidity, float pressure) {
|
||||
System.out.println("Observer1 显示数据:temperature = " + temperature + ";humidity = " + humidity);
|
||||
}
|
||||
}
|
||||
|
||||
public class Observer2 implements Observer {
|
||||
|
||||
/**
|
||||
* 更新数据
|
||||
*
|
||||
* @param temperature 温度
|
||||
* @param humidity 湿度
|
||||
* @param pressure 气压
|
||||
*/
|
||||
@Override
|
||||
public void update(float temperature, float humidity, float pressure) {
|
||||
System.out.println("Observer2 显示数据:temperature = " + temperature + ";humidity = " + humidity);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public class Observer3 implements Observer {
|
||||
|
||||
/**
|
||||
* 更新数据
|
||||
*
|
||||
* @param temperature 温度
|
||||
* @param humidity 湿度
|
||||
* @param pressure 气压
|
||||
*/
|
||||
@Override
|
||||
public void update(float temperature, float humidity, float pressure) {
|
||||
System.out.println("Observer3 显示数据:temperature = " + temperature + ";humidity = " + humidity);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 主题
|
||||
|
||||
#### 主体接口
|
||||
|
||||
```java
|
||||
/**
|
||||
* 被观察者接口
|
||||
*/
|
||||
public interface Subject {
|
||||
void registerObserver(Observer observer);
|
||||
|
||||
void removeObserver(Observer observer);
|
||||
|
||||
void notifyObservers();
|
||||
}
|
||||
```
|
||||
|
||||
#### 主体实现
|
||||
|
||||
```java
|
||||
import cn.bunny.pattern3.observer.Observer;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 被观察者类
|
||||
*/
|
||||
public class WeatherStationSubject implements Subject {
|
||||
private final List<Observer> observers = new ArrayList<>();
|
||||
private float temperature;
|
||||
private float humidity;
|
||||
private float pressure;
|
||||
|
||||
@Override
|
||||
public void registerObserver(Observer observer) {
|
||||
observers.add(observer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeObserver(Observer observer) {
|
||||
observers.remove(observer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifyObservers() {
|
||||
for (Observer observer : observers) {
|
||||
observer.update(temperature, humidity, pressure);
|
||||
}
|
||||
}
|
||||
|
||||
public void setTemperature(float temperature, float humidity, float pressure) {
|
||||
this.temperature = temperature;
|
||||
this.humidity = humidity;
|
||||
this.pressure = pressure;
|
||||
notifyObservers();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 运行
|
||||
|
||||
```java
|
||||
import cn.bunny.pattern3.observer.Observer;
|
||||
import cn.bunny.pattern3.observer.Observer1;
|
||||
import cn.bunny.pattern3.observer.Observer2;
|
||||
import cn.bunny.pattern3.observer.Observer3;
|
||||
import cn.bunny.pattern3.subject.WeatherStationSubject;
|
||||
|
||||
public class Main {
|
||||
public static void main(String[] args) {
|
||||
WeatherStationSubject weatherStationSubject = new WeatherStationSubject();
|
||||
Observer observer1 = new Observer1();
|
||||
Observer observer2 = new Observer2();
|
||||
Observer observer3 = new Observer3();
|
||||
|
||||
// 注册观察者
|
||||
weatherStationSubject.registerObserver(observer1);
|
||||
weatherStationSubject.registerObserver(observer2);
|
||||
weatherStationSubject.registerObserver(observer3);
|
||||
|
||||
// 更新天气温度,观察者会收到通知
|
||||
weatherStationSubject.setTemperature(25.0f, 25.0f, 25.0f);
|
||||
weatherStationSubject.setTemperature(30.0f, 30.0f, 30.0f);
|
||||
|
||||
// 移出观察者1
|
||||
weatherStationSubject.removeObserver(observer1);
|
||||
|
||||
// 更新天气温度,观察者会收到通知
|
||||
System.out.println("--------------------------");
|
||||
weatherStationSubject.setTemperature(44.0f, 44.0f, 44.0f);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||

|
||||
|
||||
## 装饰者模式
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>cn.bunny</groupId>
|
||||
<artifactId>MultiThread</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>pattern</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>pattern</name>
|
||||
<url>http://maven.apache.org</url>
|
||||
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<version>3.8.1</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
|
@ -0,0 +1,20 @@
|
|||
package cn.bunny.pattern1;
|
||||
|
||||
public class Singleton1 {
|
||||
private static volatile Singleton1 instance;
|
||||
|
||||
private Singleton1() {
|
||||
// 构造函数私有化
|
||||
}
|
||||
|
||||
public static Singleton1 getInstance() {
|
||||
if (instance == null) { // 第一次检查
|
||||
synchronized (Singleton1.class) {
|
||||
if (instance == null) { // 第二次检查
|
||||
instance = new Singleton1();
|
||||
}
|
||||
}
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
package cn.bunny.pattern1;
|
||||
|
||||
public class Singleton2 {
|
||||
|
||||
// 私有构造函数,防止外部直接实例化
|
||||
private Singleton2() {
|
||||
}
|
||||
|
||||
// 公共的获取单例实例的方法
|
||||
public static Singleton2 getInstance() {
|
||||
return SingletonHolder.INSTANCE;
|
||||
}
|
||||
|
||||
// 静态内部类,该类只在第一次调用 getInstance() 时加载
|
||||
private static class SingletonHolder {
|
||||
// JVM 会确保这行代码只会被执行一次
|
||||
private static final Singleton2 INSTANCE = new Singleton2();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
package cn.bunny.pattern1;
|
||||
|
||||
public enum Singleton3 {
|
||||
INSTANCE,
|
||||
;
|
||||
|
||||
public void method() {
|
||||
System.out.println("Singleton3");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
package cn.bunny.pattern2;
|
||||
|
||||
import cn.bunny.pattern2.duck.AbstractDuck;
|
||||
import cn.bunny.pattern2.duck.ModelDuck;
|
||||
import cn.bunny.pattern2.duck.WoodDuck;
|
||||
|
||||
public class Main {
|
||||
public static void main(String[] args) {
|
||||
ModelDuck modelDuck = new ModelDuck();
|
||||
modelDuck.display();
|
||||
modelDuck.performFly();
|
||||
modelDuck.performQuack();
|
||||
modelDuck.swim();
|
||||
|
||||
System.out.println("--------------------------------");
|
||||
|
||||
AbstractDuck woodDuck = new WoodDuck();
|
||||
woodDuck.display();
|
||||
woodDuck.performFly();
|
||||
woodDuck.performQuack();
|
||||
woodDuck.swim();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
package cn.bunny.pattern2.behavior.fly;
|
||||
|
||||
public interface FlyBehavior {
|
||||
/**
|
||||
* 飞的样子
|
||||
*/
|
||||
void fly();
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
package cn.bunny.pattern2.behavior.fly;
|
||||
|
||||
public class FlyNoWay implements FlyBehavior {
|
||||
/**
|
||||
* 飞的样子
|
||||
*/
|
||||
@Override
|
||||
public void fly() {
|
||||
System.out.println("不能飞。。。");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
package cn.bunny.pattern2.behavior.fly;
|
||||
|
||||
public class FlyWithWings implements FlyBehavior {
|
||||
/**
|
||||
* 飞的样子
|
||||
*/
|
||||
@Override
|
||||
public void fly() {
|
||||
System.out.println("可以飞。。。");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
package cn.bunny.pattern2.behavior.quack;
|
||||
|
||||
public class MuteQuack implements QuackBehavior {
|
||||
/**
|
||||
* 怎么叫的
|
||||
*/
|
||||
@Override
|
||||
public void quack() {
|
||||
System.out.println("不会叫...");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
package cn.bunny.pattern2.behavior.quack;
|
||||
|
||||
public class Quack implements QuackBehavior {
|
||||
/**
|
||||
* 怎么叫的
|
||||
*/
|
||||
@Override
|
||||
public void quack() {
|
||||
System.out.println("鸭子叫。。。");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
package cn.bunny.pattern2.behavior.quack;
|
||||
|
||||
public interface QuackBehavior {
|
||||
/**
|
||||
* 怎么叫的
|
||||
*/
|
||||
void quack();
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
package cn.bunny.pattern2.duck;
|
||||
|
||||
import cn.bunny.pattern2.behavior.fly.FlyBehavior;
|
||||
import cn.bunny.pattern2.behavior.quack.QuackBehavior;
|
||||
import lombok.Setter;
|
||||
|
||||
@Setter
|
||||
public abstract class AbstractDuck {
|
||||
// 设置新的叫声行为
|
||||
QuackBehavior quackBehavior;
|
||||
// 设置新的飞行行为
|
||||
FlyBehavior flyBehavior;
|
||||
|
||||
public AbstractDuck() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 展示什么类型的鸭子
|
||||
*/
|
||||
public abstract void display();
|
||||
|
||||
public void performFly() {
|
||||
flyBehavior.fly();
|
||||
}
|
||||
|
||||
public void performQuack() {
|
||||
quackBehavior.quack();
|
||||
}
|
||||
|
||||
public void swim() {
|
||||
System.out.println("鸭子游泳");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
package cn.bunny.pattern2.duck;
|
||||
|
||||
import cn.bunny.pattern2.behavior.fly.FlyNoWay;
|
||||
import cn.bunny.pattern2.behavior.quack.MuteQuack;
|
||||
|
||||
public class ModelDuck extends AbstractDuck {
|
||||
|
||||
private final String modelDuck = "橡皮🦆";
|
||||
|
||||
public ModelDuck() {
|
||||
super.setFlyBehavior(new FlyNoWay());
|
||||
super.quackBehavior = new MuteQuack();
|
||||
}
|
||||
|
||||
/**
|
||||
* 展示什么类型的鸭子
|
||||
*/
|
||||
@Override
|
||||
public void display() {
|
||||
System.out.println(modelDuck + "...");
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
@Override
|
||||
public void swim() {
|
||||
System.out.println(modelDuck + "不会游泳。。。");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
package cn.bunny.pattern2.duck;
|
||||
|
||||
import cn.bunny.pattern2.behavior.fly.FlyNoWay;
|
||||
import cn.bunny.pattern2.behavior.quack.MuteQuack;
|
||||
|
||||
public class WoodDuck extends AbstractDuck {
|
||||
|
||||
private final String modelDuck = "木头🦆";
|
||||
|
||||
public WoodDuck() {
|
||||
super.flyBehavior = new FlyNoWay();
|
||||
super.quackBehavior = new MuteQuack();
|
||||
}
|
||||
|
||||
/**
|
||||
* 展示什么类型的鸭子
|
||||
*/
|
||||
@Override
|
||||
public void display() {
|
||||
System.out.println(modelDuck + "...");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
package cn.bunny.pattern3;
|
||||
|
||||
|
||||
import cn.bunny.pattern3.observer.Observer;
|
||||
import cn.bunny.pattern3.observer.Observer1;
|
||||
import cn.bunny.pattern3.observer.Observer2;
|
||||
import cn.bunny.pattern3.observer.Observer3;
|
||||
import cn.bunny.pattern3.subject.WeatherStationSubject;
|
||||
|
||||
public class Main {
|
||||
public static void main(String[] args) {
|
||||
WeatherStationSubject weatherStationSubject = new WeatherStationSubject();
|
||||
Observer observer1 = new Observer1();
|
||||
Observer observer2 = new Observer2();
|
||||
Observer observer3 = new Observer3();
|
||||
|
||||
// 注册观察者
|
||||
weatherStationSubject.registerObserver(observer1);
|
||||
weatherStationSubject.registerObserver(observer2);
|
||||
weatherStationSubject.registerObserver(observer3);
|
||||
|
||||
// 更新天气温度,观察者会收到通知
|
||||
weatherStationSubject.setTemperature(25.0f, 25.0f, 25.0f);
|
||||
weatherStationSubject.setTemperature(30.0f, 30.0f, 30.0f);
|
||||
|
||||
// 移出观察者1
|
||||
weatherStationSubject.removeObserver(observer1);
|
||||
|
||||
// 更新天气温度,观察者会收到通知
|
||||
System.out.println("--------------------------");
|
||||
weatherStationSubject.setTemperature(44.0f, 44.0f, 44.0f);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
package cn.bunny.pattern3.observer;
|
||||
|
||||
/**
|
||||
* 观察者接口
|
||||
*/
|
||||
public interface Observer {
|
||||
|
||||
/**
|
||||
* 更新数据
|
||||
*
|
||||
* @param temperature 温度
|
||||
* @param humidity 湿度
|
||||
* @param pressure 气压
|
||||
*/
|
||||
void update(float temperature, float humidity, float pressure);
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
package cn.bunny.pattern3.observer;
|
||||
|
||||
public class Observer1 implements Observer {
|
||||
|
||||
/**
|
||||
* 更新数据
|
||||
*
|
||||
* @param temperature 温度
|
||||
* @param humidity 湿度
|
||||
* @param pressure 气压
|
||||
*/
|
||||
@Override
|
||||
public void update(float temperature, float humidity, float pressure) {
|
||||
System.out.println("Observer1 显示数据:temperature = " + temperature + ";humidity = " + humidity);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
package cn.bunny.pattern3.observer;
|
||||
|
||||
public class Observer2 implements Observer {
|
||||
|
||||
/**
|
||||
* 更新数据
|
||||
*
|
||||
* @param temperature 温度
|
||||
* @param humidity 湿度
|
||||
* @param pressure 气压
|
||||
*/
|
||||
@Override
|
||||
public void update(float temperature, float humidity, float pressure) {
|
||||
System.out.println("Observer2 显示数据:temperature = " + temperature + ";humidity = " + humidity);
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
package cn.bunny.pattern3.observer;
|
||||
|
||||
public class Observer3 implements Observer {
|
||||
|
||||
/**
|
||||
* 更新数据
|
||||
*
|
||||
* @param temperature 温度
|
||||
* @param humidity 湿度
|
||||
* @param pressure 气压
|
||||
*/
|
||||
@Override
|
||||
public void update(float temperature, float humidity, float pressure) {
|
||||
System.out.println("Observer3 显示数据:temperature = " + temperature + ";humidity = " + humidity);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
package cn.bunny.pattern3.subject;
|
||||
|
||||
import cn.bunny.pattern3.observer.Observer;
|
||||
|
||||
/**
|
||||
* 被观察者接口
|
||||
*/
|
||||
public interface Subject {
|
||||
void registerObserver(Observer observer);
|
||||
|
||||
void removeObserver(Observer observer);
|
||||
|
||||
void notifyObservers();
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
package cn.bunny.pattern3.subject;
|
||||
|
||||
|
||||
import cn.bunny.pattern3.observer.Observer;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 被观察者类
|
||||
*/
|
||||
public class WeatherStationSubject implements Subject {
|
||||
private final List<Observer> observers = new ArrayList<>();
|
||||
private float temperature;
|
||||
private float humidity;
|
||||
private float pressure;
|
||||
|
||||
@Override
|
||||
public void registerObserver(Observer observer) {
|
||||
observers.add(observer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeObserver(Observer observer) {
|
||||
observers.remove(observer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifyObservers() {
|
||||
for (Observer observer : observers) {
|
||||
observer.update(temperature, humidity, pressure);
|
||||
}
|
||||
}
|
||||
|
||||
public void setTemperature(float temperature, float humidity, float pressure) {
|
||||
this.temperature = temperature;
|
||||
this.humidity = humidity;
|
||||
this.pressure = pressure;
|
||||
notifyObservers();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
package cn.bunny;
|
||||
|
||||
import junit.framework.Test;
|
||||
import junit.framework.TestCase;
|
||||
import junit.framework.TestSuite;
|
||||
|
||||
/**
|
||||
* Unit test for simple App.
|
||||
*/
|
||||
public class AppTest
|
||||
extends TestCase
|
||||
{
|
||||
/**
|
||||
* Create the test case
|
||||
*
|
||||
* @param testName name of the test case
|
||||
*/
|
||||
public AppTest( String testName )
|
||||
{
|
||||
super( testName );
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the suite of tests being tested
|
||||
*/
|
||||
public static Test suite()
|
||||
{
|
||||
return new TestSuite( AppTest.class );
|
||||
}
|
||||
|
||||
/**
|
||||
* Rigourous Test :-)
|
||||
*/
|
||||
public void testApp()
|
||||
{
|
||||
assertTrue( true );
|
||||
}
|
||||
}
|
1
pom.xml
1
pom.xml
|
@ -23,6 +23,7 @@
|
|||
<module>multithreading</module>
|
||||
<module>local-tools</module>
|
||||
<module>mvc</module>
|
||||
<module>pattern</module>
|
||||
</modules>
|
||||
<scm>
|
||||
<connection/>
|
||||
|
|
|
@ -71,6 +71,7 @@ public class SecurityConfiguration {
|
|||
.defaultSuccessUrl("/index")// 登录成功后默认跳转页面
|
||||
// .defaultSuccessUrl("/index", true)// 登录成功后默认跳转页面,如果用户之前访问页面也需要强制跳转到 /index 可以传递第二个参数
|
||||
)
|
||||
// .csrf(AbstractHttpConfigurer::disable)// 前后端分离可以禁用
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -247,6 +247,9 @@
|
|||
<label>
|
||||
<input class="input" name="password" placeholder="密码" type="password" value="admin"/>
|
||||
</label>
|
||||
<label>
|
||||
<input name="_csrf" placeholder="_csrf" th:value="${_csrf.token}" type="hidden"/>
|
||||
</label>
|
||||
<button class="btn">登录</button>
|
||||
</div>
|
||||
</div>
|
||||
|
|
Loading…
Reference in New Issue