Skip to content

Commit 89ea566

Browse files
🍉 observer pattern
1 parent a9009a1 commit 89ea566

File tree

4 files changed

+176
-11
lines changed

4 files changed

+176
-11
lines changed

web/.DS_Store

-6 KB
Binary file not shown.
18.9 KB
Binary file not shown.

前端框架/.DS_Store

-6 KB
Binary file not shown.

设计模式/设计模式.md

Lines changed: 176 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3243,8 +3243,171 @@ Comparator<Integer> comparator= (o1, o2) -> {
32433243
>
32443244
>观察者模式又被称为`发布-订阅(Publish/Subscribe)`,当对象间存在一对多关系时,则使用观察者模式(Observer Pattern)。比如,当一个对象被修改时,则会自动通知依赖它的对象。观察者模式属于行为型模式。
32453245
>
3246+
3247+
访问者模式
3248+
3249+
```java
3250+
package com.fx.behaviorPattern.observer.d;
3251+
3252+
3253+
import lombok.Data;
3254+
3255+
import java.util.ArrayList;
3256+
import java.util.List;
3257+
3258+
public class Test {
3259+
public static void main(String[] args) {
3260+
Role role = new Role();
3261+
role.setName("角色");
3262+
role.setHp(100);
3263+
3264+
Panel panel = new Panel(role);
3265+
role.addObserver(panel);
3266+
3267+
BallPanel ballPanel = new BallPanel(role);
3268+
role.addObserver(ballPanel);
3269+
3270+
HeadPanel headPanel = new HeadPanel(role);
3271+
role.addObserver(headPanel);
3272+
3273+
Monster monster = new Monster();
3274+
monster.attack(role);
3275+
monster.attack(role);
3276+
monster.attack(role);
3277+
}
3278+
}
3279+
3280+
@Data
3281+
class Role {
3282+
private String name;
3283+
/**
3284+
* 血量
3285+
**/
3286+
private int hp;
3287+
/**
3288+
* 魔法值(蓝)
3289+
**/
3290+
private int mp;
3291+
/**
3292+
* 所有观察者
3293+
**/
3294+
private List<Observer> observers = new ArrayList<>();
3295+
3296+
public void addObserver(Observer obj) {
3297+
observers.add(obj);
3298+
}
3299+
3300+
public void removeObserver(Observer obj) {
3301+
observers.remove(obj);
3302+
}
3303+
3304+
public void notifyObservers() {
3305+
observers.forEach(Observer::notifyObserver);
3306+
}
3307+
3308+
public void setHp(int hp) {
3309+
// 更新血条的时候,一定要通知三个地方:1. 血条、球、面板
3310+
this.hp = hp;
3311+
System.out.println("血条更新为:" + hp);
3312+
3313+
System.out.println("球形更新为:" + hp);
3314+
System.out.println("面板更新为:" + hp);
3315+
}
3316+
3317+
@Override
3318+
public String toString() {
3319+
return "Role{" +
3320+
"name='" + name + '\'' +
3321+
", hp=" + hp +
3322+
", mp=" + mp +
3323+
'}';
3324+
}
3325+
}
3326+
3327+
class Monster {
3328+
public void attack(Role r) {
3329+
r.setHp(r.getHp() - 10);
3330+
r.notifyObservers();
3331+
}
3332+
}
3333+
3334+
/**
3335+
* 观察者们需要一个方法来接收数据
3336+
*/
3337+
3338+
interface Observer {
3339+
void notifyObserver();
3340+
}
3341+
3342+
/**
3343+
* 观察者模式拥有很多变体
3344+
*/
3345+
class Panel implements Observer {
3346+
/**
3347+
* 访问者模式其中一种实现就是要持有数据的引用 也就是推拉的形式
3348+
* 推,让数据源推消息给观察者
3349+
* 拉,观察者拉取自己关心的数据
3350+
*/
3351+
private final Role r;
3352+
3353+
public Panel(Role r) {
3354+
this.r = r;
3355+
}
3356+
3357+
@Override
3358+
public void notifyObserver() {
3359+
System.out.printf("Panel 更新血条了值为:%d\n", r.getHp());
3360+
}
3361+
}
3362+
3363+
class BallPanel implements Observer {
3364+
private final Role r;
3365+
3366+
public BallPanel(Role r) {
3367+
this.r = r;
3368+
}
3369+
3370+
@Override
3371+
public void notifyObserver() {
3372+
System.out.printf("Panel 更新血条了值为:%d\n", r.getHp());
3373+
}
3374+
}
3375+
3376+
class HeadPanel implements Observer {
3377+
private final Role r;
3378+
3379+
public HeadPanel(Role r) {
3380+
this.r = r;
3381+
}
3382+
3383+
@Override
3384+
public void notifyObserver() {
3385+
System.out.printf("Panel 更新血条了值为:%d\n", r.getHp());
3386+
}
3387+
}
3388+
3389+
```
3390+
3391+
#### 4.2.3 观察者模式总结
3392+
3393+
![image-20231105234455568](https://cdn.fengxianhub.top/resources-master/image-20231105234455568.png)
3394+
3395+
3396+
3397+
### 4.3 访问者模式
3398+
3399+
>**访问者(Visitor)模式**:封装一些作用于某种数据结构中的各元素的操作,它可以在不改变这个数据结构的前提下定义作用于这些元素的新的操作
3400+
>
32463401
>🧨观察者模式的本质是想实现`多重派发 (multiple dispatch)`,由于Java中只有函数参数重载实现的单派发(single dispatch),所以需要这个模式去模拟,如果用支持多派发的语言如clojue或者common lisp、js,就不需要观察者模式了,在golang中也可以通过接口和类型断言来模拟实现多派发。
32473402

3403+
![img](https://cdn.fengxianhub.top/resources-master/e4a5535e-29c6-3aa2-8b0c-3f51cc49d980.png)
3404+
3405+
**抽象访问者(Visitor)角色**:定义接口,声明一个或多个访问操作。
3406+
**具体访问者(ConcreteVisitor)角色**:实现抽象访问者所声明的接口,也就是抽象访问者所声明的各个访问操作。
3407+
**抽象元素(Visitable)角色**:声明一个接受操作,接受一个访问者对象作为一个参数。
3408+
**具体元素结点(ConcreteElement)角色**:实现抽象结点所规定的接受操作。
3409+
**数据结构对象(ObjectStructure)角色**:可以遍历结构中的所有元素,提供一个接口让访问者对象都可以访问每一个元素。
3410+
32483411
下面用一个经典的墨子骑马🏇的故事来描述java遇到的多派发的问题
32493412

32503413
```java
@@ -3325,7 +3488,7 @@ class BlackHorse implements Horse {}
33253488

33263489
如果解决呢?一般有两种做法
33273490

3328-
#### 4.2.1 类型判断解决多派分发问题
3491+
#### 4.3.1 类型判断解决多派分发问题
33293492

33303493
在方法里使用`instanceof`判断真实类型,比如(java.awt.Component的源码)
33313494

@@ -3394,7 +3557,7 @@ protected void processEvent(AWTEvent e) {
33943557

33953558
这种方法实现的双重分派都格外的冗长、复杂和容易出错,也不符合`开闭原则`
33963559

3397-
#### 4.2.2 反转球实现动态分发
3560+
#### 4.3.2 反转球实现动态分发
33983561

33993562
通过两次的调用来实现,比如下面剪刀石头布的游戏:
34003563

@@ -3509,7 +3672,7 @@ RoshamBol.match()有2个item参数,通关过Item.compete()方法开始2路分
35093672

35103673
这种实现也就是“访问者模式”的精华。
35113674

3512-
#### 4.2.4 golang实现多派发
3675+
#### 4.3.4 golang实现多派发
35133676

35143677
```go
35153678
package main
@@ -3553,7 +3716,7 @@ func main() {
35533716
}
35543717
```
35553718

3556-
#### 4.2.5 js实现多派发
3719+
#### 4.3.5 js实现多派发
35573720

35583721
```javascript
35593722
class Shape {
@@ -3597,7 +3760,7 @@ calculateArea(circle); // 输出:Area: 78.54
35973760
calculateArea(rectangle); // 输出:Area: 12.00
35983761
```
35993762

3600-
#### 4.2.6 java需要借助观察者模式或者访问者模式实现
3763+
#### 4.3.6 java需要借助观察者模式或者访问者模式实现
36013764

36023765
```java
36033766
interface Shape {
@@ -3671,9 +3834,11 @@ public class Main {
36713834
}
36723835
```
36733836

3837+
###
3838+
36743839

36753840

3676-
### 4.3 状态机模式
3841+
### 4.4 状态机模式
36773842

36783843
>**状态机模式**
36793844
>
@@ -3691,7 +3856,7 @@ public class Main {
36913856

36923857
打个比方,当订单的状态由待支付变成已支付时,我们需要它能够自动完成例如订单持久化等操作
36933858

3694-
#### 4.3.1 问题引入
3859+
#### 4.4.1 问题引入
36953860

36963861
在没有状态机模式之前,我们大概率是直接搞个字段`status`放到表里面,然后在业务代码里面去修改里面的状态,然后在需要用到状态的地方再去查一下db,我们想想这样有哪些弊端?
36973862

@@ -3726,7 +3891,7 @@ public class Main {
37263891

37273892

37283893

3729-
#### 4.3.2 状态模式解决问题
3894+
#### 4.4.3 状态模式解决问题
37303895

37313896

37323897

@@ -3742,7 +3907,7 @@ public class Main {
37423907

37433908

37443909

3745-
#### 4.3.3 市面上的状态机
3910+
#### 4.4.3 市面上的状态机
37463911

37473912
目前开源的状态机实现方案有:
37483913

@@ -3781,13 +3946,13 @@ public class Main {
37813946

37823947

37833948

3784-
#### 4.3.4 spring状态机
3949+
#### 4.4.4 spring状态机
37853950

37863951
https://www.cnblogs.com/Zero-Jo/p/13891075.html
37873952

37883953

37893954

3790-
#### 4.3.5阿里状态机-cola stateMachine
3955+
#### 4.4.5阿里状态机-cola stateMachine
37913956

37923957
https://www.cnblogs.com/Zero-Jo/p/14622937.html
37933958

0 commit comments

Comments
 (0)