program-pattern
编程语言目标:
通用(抽象-可读;隔离-扩展)+ 重用(测试维护,代码结构好,文档全,测试用例全)
程序=数据结构 + 控制 + 逻辑
=泛型 + 函数式 + 多态
数据类型
- NULL:空,表达缺失。Java中用Optional 明确表达∃缺失状态
函数式=filter + map + reduce;= 回调 = 代理,用于切面
面向对象= 单例/上下文+(工厂)+模板+策略/责任链+IOC;桥接+装饰;发布订阅
其中,最重要的是业务逻辑,即方法,或者说,方法入参
- 参数是类,注入
- 参数是方法,回调
解耦:减少组件联系
- 定义清晰的接口
- 依赖注入:组件不自己创建依赖的对象,而是通过传参接收依赖的对象
- 事件驱动:发布订阅,发布者无需知道订阅者是谁,订阅者也不关心事件来自何处;引入中间对象,如果是消息队列,不必同时在线
- 微服务,多个项目,http交互
面向对象
面向对象(设计思路顺序):模块,类(创建,组合,交互;接口、抽象类),字段方法
- 编码规范:
- 命名,注释,函数长度,参数个数;
- 接口定义类实现,避免类膨胀;接口要定义的通用;将功能定义成接口(会飞,会叫);
面向对象的作用:业务建模
面向对象特性
- 封装:隐藏信息,保护数据 Enum,减少出错 Page
- 访问控制
- 抽象 1. 接口、抽象类、函数 2. 宽泛命令、注释
- 继承:复用 Template -> 组合
- 多态:隔离 Override
和面向过程的区别:面向对象适合处理状态;如果算法为主,数据为辅,则适合使用面向过程(脚本)
UML
用例图
关系:
- include 包含(重用,两个及以上的用例提取公共行为;用一个构建来实现用例的某部分);
- expand 扩展(分离出不同的行为,主用例和辅助用例)
设计原则
- 开闭原则:
- 基于已有的代码,最小代价实现新功能
- 如果预留扩展点,需要足够了解业务;但成本太高,并且没有什么业务是不可能的,不必要为遥远的不一定发生的需求过度设计;可以需求驱动时,重构代码
- 里式替换:按协议实现,如函数声明的功能,输入输出异常的约定,注释中的特殊说明
- 验证:用父类的单元测试验证子类,运行成功
- 接口隔离原则:如果调用者只调用接口的部分功能,那接口不够单一,应把部分功能移到一个新接口中
类的关系
//泛化Generalization-- 继承
public class A { ... }
public class B extends A { ... }
// 实现Realization-- 接口和实现类
public interface A {...}
public class B implements A { ... }
//组合:内部属性 contains-a; 没有B则没有A
public class A {
private B b;
public A() {
this.b = new B();
}
}
//聚合Aggregation, 属性+传参;has-a;整体-局部;A:B=1:n;B是A的一部分, B可有可无
public class A {
private B b;
// 注入
public A(B b) {
this.b = b;
}
}
//关联:外部属性;类彼此知道(或不知道)
public class A { public void func(B b) { ... } }
//依赖: 内部使用外部;B的定义发生变化,它将改变A,但不会反过来(取决于)
public class A {
private B b;
public A(C c) {
B.add(c);
}
}
复杂业务的MVC设计
- controller,Query 查询参数可校验, VO 展示参数 View
- service,DTO Data Transfer
- Repository DO Data
设计模式
代码隔离。创建型 隔离 创建和使用;结构型 隔离 对象间的关系;行为型 隔离 对象间的交互
目标:提升代码质量
时机:有痛点
方式:持续重构
注意:先根据业务术语划分业务范畴,再使用设计模式,如 贴息对账和佣金对账
此外,也要有 产品意识,服务意识
概念
发通知(主体 A),用邮件(方式B)。叫 A 调用/依赖/创建-控制 B。
- 控制反转、依赖注入
- 回调
# 控制反转:A创建B -> (外部/主程序/框架)Main创建B。
# 效果:解耦A和B
Main:
# 依赖注入是(控制反转的)实现方式
# 依赖注入:Main 将(A的依赖)B 注入到 A 中
A(B)
A:
# 回调:A 调用B,然后 B调用 (A⭐ 中声明的 )callback。A 统一处理{B1,B2}的结果
# 效果:异步操作,解耦调用方和被调用方。
B(new callback)
sequenceDiagram
participant Main
participant NotificationManager
participant NotificationService
participant EmailNotificationService
participant NotificationCallback
Main->>NotificationManager: new NotificationManager(emailService)
Note over Main: 传入 EmailNotificationService 作为依赖
Main->>NotificationManager: sendNotification("user@example.com", "邮件内容")
NotificationManager->>NotificationService: sendNotification(target, message, callback)
Note over NotificationManager: 传递 NotificationCallback 声明
NotificationService->>EmailNotificationService: sendNotification()
Note over NotificationService: EmailNotificationService 实现了 NotificationService
alt 邮件发送成功
EmailNotificationService->>NotificationCallback: onSuccess(target, message)
NotificationCallback->>NotificationManager: 调用NotificationCallback 声明,打印成功日志
else 邮件发送失败
EmailNotificationService->>NotificationCallback: onFailure(target, message, Exception)
NotificationCallback->>NotificationManager: 调用NotificationCallback 声明,打印失败日志
NotificationManager->>NotificationManager: handleFailure() 处理失败逻辑
end// 通知回调
public interface NotificationCallback {
void onSuccess(String target, String message);
void onFailure(String target, String message, Exception e);
}
// 邮件通知服务
public class EmailNotificationService implements NotificationService {
@Override
// 设邮件操作耗时,改成异步发送
@Async("EmailSendExecutor")
public boolean sendNotification(String target, String message, NotificationCallback callback) {
// 模拟邮件发送
boolean success = new Random().nextBoolean(); // 随机模拟成功或失败
if (success) {
callback.onSuccess(target, message);
} else {
callback.onFailure(target, message, new Exception("邮件发送失败"));
}
return success;
}
}
// 通知管理
public class NotificationManager {
private final NotificationService notificationService;
// 构造函数注入
public NotificationManager(NotificationService notificationService) {
this.notificationService = notificationService;
}
public void sendNotification(String target, String message) {
// NotificationCallback:
notificationService.sendNotification(target, message, new NotificationCallback() {
@Override
public void onSuccess(String target, String message) {
System.out.println("通知发送成功: " + message + " 给 " + target);
}
@Override
public void onFailure(String target, String message, Exception e) {
System.out.println("通知发送失败: " + message + " 给 " + target + " 错误: " + e.getMessage());
// 后续处理逻辑,如 记录失败日志,重试,发送告警通知
handleFailure(target, message, e);
}
});
}
public class Main {
public static void main(String[] args) {
// 创建通知服务实例,还可能是smsService
// 控制反转:A创建B -> Main创建B
NotificationService emailService = new EmailNotificationService();
// 依赖注入:Main 将(A的依赖)B 注入到 A 中
NotificationManager emailManager = new NotificationManager(emailService);
// 发送通知
emailManager.sendNotification("user@example.com", "这是一条邮件通知");
}
}
例
报警,发送通知,用邮件发送。
- 单例模式-唯一实例:确保 ApplicationContext 的唯一性。
- 组合模式-接口的一个实现类是list<object>,另一个实现类是object:通过 Alert 管理多个 AlertHandler。
- 依赖注入-传递类:使得 AlertHandler 的依赖关系更加清晰。
- 模板模式(例子没有)-抽象类如AlertHandler中定义代码骨架
- 策略模式-策略接口/动态切换策略:通过 AlertHandler 提供不同的检查策略。
- 发布订阅/观察者模式/责任链-for:Alert 通知所有注册的 AlertHandler。
- 工厂模式-集合:通过 NotificationLevel 的 valueOf 方法创建枚举实例。
- 桥接模式-抽象无实现+实现:将通知逻辑与消息发送实现分离。
sequenceDiagram
participant User Code
participant ApplicationContextHolder
participant ApplicationContext
participant Alert
participant ErrorAlertHandler
participant Notification
participant DingTalkSender
User Code->>ApplicationContextHolder: 调用 getInstance()
ApplicationContextHolder->>ApplicationContext: 创建 ApplicationContext 实例
ApplicationContext->>Alert: 创建 Alert 实例
Alert->>Alert: 初始化 alertHandlers 列表
User Code->>Alert: 调用 check(ApiStatDTO), 获取duration 和 requestCount
Alert->>Alert: 遍历 alertHandlers 列表
Alert->>ErrorAlertHandler: 调用 AlertHandler.check(ApiStatDTO)
ErrorAlertHandler->>ErrorAlertHandler: 检查条件是否满足(AlertRule alertRule, Notification notification)
ErrorAlertHandler->>Notification: 调用 notify(NotificationLevel, String)
Notification->>DingTalkSender: 发送消息// ApplicationContextHolder:创建ApplicationContext单例
// getAlert()
Notification notification=new Notification(new DingTalkSender());
alert.addHandler(new TpsAlertHandler(alertRule,notification));
// Notification 是抽象,MessageSender 是实现,组合在一起
ApiStatDTO apiStatDTO=new ApiStatDTO()
.setRequestCount(2)
.setDuration(1);
ApplicationContext.getInstance()
.getAlert()
.check(apiStatDTO);
创建-类创建
单例
一个类只创建一个对象,这种设计就是单例模式
优势:全局类,不需要在类间传递
劣势:不支持有参构造函数,不利于代码扩展;硬编码隐藏类间依赖关系(Logger.getInstance() 改成 字段注入)
替代:工厂模式,IOC 容器
适用场景:全局唯一,没有扩展需求,不依赖外部系统。如 Spring容器管理的对象,默认是单例
实现
懒汉式
//懒汉式-- 实例方法 调用时,类进行实例化。 synchronized,不支持高并发
private static IdGenerator lazyInstance;
public static synchronized IdGenerator getLazyInstance() {
if (lazyInstance == null) {
lazyInstance = new IdGenerator();
}
return lazyInstance;
}
// 调用
IdGenerator.getLazyInstance();
双重检测
//高版本的JDK 把[对象 new 操作和初始化操作]设计为原子操作,能禁止指令重排
// volatile 关键字则保证了指令的有序性(禁止指令重排)和对象初始化的正确性(指令重排引起)。
private volatile static IdGenerator doubleCheckInstance; //DCL(双重检测锁)
public static IdGenerator getDoubleCheckInstance() {
// 加锁前检查一次,避免不必要的加锁。锁是一种影响性能的资源
// 实例化后不会再进入加锁逻辑
if (doubleCheckInstance == null) {
// 加锁保证变量的可见性和线程安全性
// 可见性:一个线程修改了共享变量的值,这个修改应该对其他线程可见
// 线程安全性:只有一个线程可以持有锁并访问受保护的代码段或变量。当一个线程持有锁时,其他试图获取同一锁的线程将被阻塞,直到锁被释放。
// synchronized 的有序,是保证代码块中的修改有序。。而不知保证
synchronized (IdGenerator.class) {
// 1. 防止多个实例创建:设两个线程A,B同时调用getDoubleCheckInstance(),此时A B 都可以通过doubleCheckInstance == null的判断。若A获取到synchronized锁,B等待A执行完。则A执行完毕后,B不只实例已经被创建,若B继续创建对象,会导致多例。因此需要在创建对象前,再次执行doubleCheckInstance == null判断,显然为false,则B知道实例已被创建,跳过创建单例类的语句,结束执行。
if (doubleCheckInstance == null) {
/**
* synchronized 确保有序性(避免指令重排)是有条件的,只确保代码执行顺序有序,和同步块顺序(执行完代码块,其他线程才能进入代码块),但 synchronized 不能避免创建对象的指令重排
* 创建对象的指令:
* 1. 分配内存空间
* 2. 初始化对象:1. 成员变量默认值 2. 字段特定值 3. 非静态(实例)初始化块执行,多个按声明顺序 4. 构造函数执行,父类先于子类 5. 设置对象头信息,如对象的类指针、哈希码、锁状态信息
* 3. 将`instance`指向分配的内存地址
* 指令重排,132,则其他线程看到 实例非null,但实际是半初始化状态
* volatile 确保对象是完全初始化,即按顺序分配到内存空间后是一个有效的对象,才被其他线程取到。
*/
doubleCheckInstance = new IdGenerator();
}
}
}
return doubleCheckInstance;
}
// 调用
IdGenerator.getDoubleCheckInstance();
静态内部类
// IdGenerator 类内
/**
* 静态内部类,insance 的唯一性、创建过程的线程安全性,都由 JVM 来保证
* @return
*/
public static IdGenerator getStaticInstance() {
return SingletonHolder.instance;
}
/**
* 内部类的方式,实现单例懒加载
* 因为需要方法到 内部类的 static 变量,所以才声明内部类为 static 类
* 为了避免滥用内部类,内部类 用 private 修饰
*/
private static class SingletonHolder {
/**
* 类的 static变量(必须是 非基本/字符串类型 或者 非final修饰)在初始化时赋值,JVM确保初始化时的线程安全性
*
* 避免this引用逃逸:
* 1. 在构造函数中避免启动新线程或将"this"引用传递给其他对象。
* 2. 避免在构造函数中调用外部类或静态方法,并将"this"引用传递给它们。
* 3. 将构造函数声明为私有,并使用工厂方法来创建对象,确保对象完全构造完成后再对外暴露。
* 如果能避免this引用逃逸,那final修饰的变量可以确保只被赋值一次
*/
public static final IdGenerator instance = new IdGenerator();
}
// 调用
IdGenerator.getStaticInstance();
枚举
public enum IdGeneratorEnum {
INSTANCE;
private AtomicInteger id = new AtomicInteger(0);
public long getId(){
return id.incrementAndGet();
}
}
示例
- 资源访问冲突:多线程下,两个日志写到一个文件中
保证写入正确,日志不被覆盖
- Logger 唯一,则文件只被打开一次
- 写入类 BufferedWriter 可以保证对象级的线程安全(对象锁)
OrderController orderController=new OrderController();
UserController userController =new UserController();
// 日志打印
orderController.create(new Order());
userController.login("userA","userP");
Logger.getInstance().close();
private Logger() {
String filePath = Paths.get(System.getProperty("user.dir")).getParent().resolve("var/logs/append.log").toString();
try {
// 默认缓冲区大小为 8192 个字符,基于性能和经验
writer = new BufferedWriter(new FileWriter(filePath, true));
} catch (IOException e) {
throw new RuntimeException(e);
}
}
其他次解决方案 1. 多个对象,写入时 Logger 加类锁 2. 分布式锁 3. 并发队列 BlockingQueue:多个线程将日志写到队列,一个线程记到日志文件
- 全局唯一类:配置信息,连接池,防重ID生成器
替代
- 工厂
- IOC 容器
工厂√
对象创建复杂,要隔离/控制复杂
- 动态 if-else 创建不同的对象,简单工厂
- 对象创建时进行复杂初始化操作,工厂方法
简单工厂:根据配置文件后缀,选择解析器,将配置加载到RuleConfig中
RuleConfig ruleConfig = new RuleConfigSource().load("");
工厂方法:一个工厂只创建一个对象,工厂用Map缓存
IOC容器√
启动时根据配置创建对象(注解+反射),程序需要某个类时直接从容器中取,涉及
- 配置解析
- 反射创建对象-工厂模式
- 对象生命周期管理
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
RateLimiter rateLimiter = (RateLimiter)context.getBean("rateLimiter");
rateLimiter.test();
建造者
@Accessors(chain=true)
原型
对象创建成本大
- 申请内存,成员变量赋值
- 数据经过复杂计算得到,入排序,HASH计算,或者 IO(RPC,网络,数据库,文件系统) 中读取、
db has 10w key-words (key_word, search_count, update_time ),
系统B分析搜索日志 batch-update / 10 min ( and version+1)
系统A在启动时加载使用,并定时更新内存
要求:任何时刻,系统A中的所有数据必须是同一版本,即保证原子性
解:
把正在使用的数据库版本定义成“服务版本”,更新内存数据时,不是在服务版本A上更新,而是新建版本B,等B建好后,一次性将A切到B上。
/**
* 更新数据库数据到内存
* 浅拷贝创建 newKeyWords;节省时间和空间,curKeyWords 中都是老版本的数据
* 深拷贝创建 需更新的 newKeyWords
*/public class CacheVersion {
private Map<String, KeyWord> curKeyWords = new HashMap<>();
private long lastUpdateTime = -1;
public void refresh() {
// 浅 copy(换引用) 创建对象
Map<String, KeyWord> newKeyWords = curKeyWords;
// select * from key_word where update_time > ${lastUpdateTime}
List<KeyWord> toUpdateKeyWords = getKeyWords(lastUpdateTime);
long maxNewUpdateTime = lastUpdateTime;
for (KeyWord key : toUpdateKeyWords) {
// 更新 maxNewUpdateTime if (key.getLastUpdateTime() > maxNewUpdateTime) {
maxNewUpdateTime = key.getLastUpdateTime();
}
// 需更新的数据,深 copy(换引用,换数据) 替换老对象
newKeyWords.put(key.getKeyWord(), key);
}
lastUpdateTime = maxNewUpdateTime;
curKeyWords = newKeyWords;
}
/**
* 从数据库查询
*
* @param lastUpdateTime
* @return
*/
private List<KeyWord> getKeyWords(long lastUpdateTime) {
return null;
}
}
结构-类组合
代理√
- 主程序 ->代理对象 impl 接口A(目标对象 impl 接口A)
应用
- 监控,统计,鉴权,限流,事务,幂等,日志
- RPC,隐藏 网络通信,数据编解码,像使用本地函数一样进行远程调用
- 缓存:@Cacheable
不改变原始类,给原始类附加功能
装饰器-叠加满减
装饰器,与目标类型相关功能的增强;代理,与目标类型无关功能的增强
Java IO
字节字符流,输入输出
- InputStream, Reader
- FileInputStream/PipedInputStream/, BufferedInputStream
- OutputStream, Writer
叠加业务:折扣+红包促销
桥接
- Main -> classA-概念抽象 ~~extends abstract ~~(new classB impl interfaceB-具体):抽象和具体的组合,便于两个独立变化
解耦 抽象和实现
JDBC 驱动查数据库:
DriverManager 是抽象部分,它不依赖于具体的数据库驱动实现,而是依赖于 java.sql.Driver 接口。
具体的数据库驱动(如 MySQL 的 com.mysql.cj.jdbc.Driver)实现了 Driver 接口。
这种设计使得 DriverManager 和具体的驱动实现可以独立变化
sequenceDiagram
participant Main
participant DriverManager
participant Driver
participant MySQLDriver
Main->>Main: Class.forName("com.mysql.cj.jdbc.Driver")
Main->>MySQLDriver: 加载 MySQL 驱动类
MySQLDriver->>DriverManager: DriverManager.registerDriver(this)
DriverManager->>DriverManager: 将 MySQLDriver 注册到 registeredDrivers 列表
Main->>Main: String url = "jdbc:mysql://localhost:3306/db?user=root&password=pass"
Main->>DriverManager: DriverManager.getConnection(url)
DriverManager->>DriverManager: 遍历 registeredDrivers 列表
DriverManager->>MySQLDriver: 调用 acceptsURL(url)
MySQLDriver->>DriverManager: 返回 true(可以处理 URL)
DriverManager->>MySQLDriver: 调用 connect(url, properties)
MySQLDriver->>DriverManager: 返回 Connection 对象
DriverManager->>Main: 返回 Connection 对象
Main->>Main: 使用 Connection 对象执行查询public class com.mysql.cj.jdbc.Driver extends NonRegisteringDriver implements java.sql.Driver {
static {
try {
java.sql.DriverManager.registerDriver(new Driver());
} catch (SQLException e) {
throw new RuntimeException("Failed to register MySQL driver", e);
}
}
}
Class.forName("com.mysql.jdbc.Driver"); // 调用 static,将Driver注入到 DriverManager
String url="jsbc:mysql://localhost:3306/db?user=root&password=pass"
// for Driver,解析数据库 URL 并建立连接。
Connection con=DriverManager.getConnection(url);
// 查SQL
Statement stat= con.createStatement(url);
String query = "select * from test*";
ResultSet rs= stat.executeQuery(query);
while (rs.next){
rs.getString(1);
}
适配器-多数据源
接口兼容
分为继承和组合
// 我的接口
public interface ITarget{
void tar();
}
// 三方接口
public class Adaptee{
void ade();
}
// 继承实现适配器,适用于接口定义相同很多
public class Adaptor extends Adaptee implements ITarget{
public void tar(){
super.ade();
}
}
//我的实现调用三方接口实现
// 组合实现适配器,适用于接口定义很不同
public class Adaptor implements ITarget{
private Adaptee adaptee;
public void tar(){
adaptee.ade();
}
}
门面
避免远程性能问题,将3个方法封装成一个方法
组合-文件夹和文件统一处理
树形结构数据,同一单个对象(文件)和组合对象(目录)的处理(递归遍历)
组织架构,部门员工
HumanTree humanTree=new HumanTree();
HumanResource humanResource = humanTree.buildOrganization();
System.out.println(humanResource.calculateSalary());
享元
不可变对象,Map 缓存,实现复用。如文字样式,Integer
如果 Integer的值在 -128 ~ 127 间。直接返回 IntegerCache 中的常用的值
// 创建新对象,不用缓存
Integer a =new Integer(123);
// 使用缓存
Integer a =123;
// 使用缓存
Integer a = Integer.valueOf(123);
//
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
// 预先创建好需要共享的对象,对垃圾收集不友好,缓存一直持有对象的引用,不会被JVM自动回收,少用
private static class IntegerCache {
static final int low = -128;
static final int high;
static final Integer[] cache;
}
行为-类交互
- 消息传递
- 职责分配
模板-流程固定√
- 复用:InputStream#read, AbstractList#add
- 扩展:extends HttpServlet get/post ; Junit TestCase setUp/tearDown
回调:一个函数在特定条件下被调用。模版模式的继承回调,是对子类行为的调用约定,在将来某个时刻(执行模板方法时)回调了子类的方法。回调更多是指指函数接收一个函数引用并保存下来,在合适的时机调用它。
同步回调是模板模式,异步回调是观察者模式,
模板模式中子类通过重写父类方法来实现回调;发布订阅模式中订阅者通过注册回调函数(或匿名函数)来响应发布的事件。
模板基于继承实现,回调基于组合实现,回调更灵活
- JDBCTemplate, RedisTemplate, RestTemplate, 只编写业务代码,由 template 完成连接和请求
// org.springframework.jdbc.core.JdbcTemplate#execute(org.springframework.jdbc.core.ConnectionCallback<T>)
public Object doInStatement(Statement stmt) throws SQLException {
stmt.execute(sql);
return null;
}
stmt = con.createStatement();
this.applyStatementSettings(stmt);
T result = action.doInStatement(stmt);
- 控件注册事件监听器
// 观察者模式,事先注册,事件发生时执行
button.setOnClick(new OnClickListener(){
public void onClick(View v){
System.out.println("I am clicked");
}
})
- Tomcat, JVM shutdown hook
// ApplicationShutDownHooks 应用程序关闭时,遍历执行 hooks
Runtime.getRuntime().addShutdownHook((Thread)()=>{System.out.println("I am called when shutting down.")});
策略-多方法√
运行时动态选择实现类,隔离 策略的 定义,创建和使用
- 缓存淘汰策略:LRU,FIFO
-
订单打折策略:普通订单,团购订单,促销订单
-
文件只含数字,相邻数字用, 分割,对数字排序
- [0,6G) QuickSort; [6G,10G) ExternalSort; [10G,100G) ConcurrentExternalSort; 100G+ MapReduceSort
责任链/链式-for同方法-依次校验√
隔离请求的发送和接收,1:N
实现方式 1. 数组 2. 链表
敏感词过滤
- 如果敏感词过滤框架是三方维护的,不可能直接修改框架的源码,使用责任链模式可以实现功能扩展
- 责任链配置过滤算法更加灵活,可以只选择其中几个算法
// SensitiveWordFilter SexyWordFilter
// SensitiveWordFilterChain
- 过滤器:校验(鉴权,业务校验),限流,日志记录,
- 拦截器:
- AOP MethodInterceptor,
- mybatis MybatisPlusInterceptor
PaginationInnerInterceptor - spring web,登录验证,可以具体到接口:HandlerInterceptor
// org.springframework.web.servlet.HandlerExecutionChain
// 路由 dispatcher - handlerMapping 路径匹配;new HandlerExecutionChain 添加 HandlerInterceptor
public class HandlerExecutionChain {
private static final Log logger = LogFactory.getLog(HandlerExecutionChain.class);
// class org.springframework.boot.actuate.endpoint.web.servlet.WebMvcEndpointHandlerMapping$WebMvcLinksHandler
private final Object handler;
// SkipPathExtensionContentNegotiation
private final List<HandlerInterceptor> interceptorList = new ArrayList<>();
}
pipeline
需求:mock linux pipeline 功能,连接线程间的 command
/**
* Command 定义执行命令的方法
* GenericCommand类实现Command接口,使用Function<String, String>进行数据处理
* Pipeline类负责创建和管理管道内的队列和命令
* ExecutorServiceManager类负责管理线程池和任务的生命周期
*/
int numCommands = 2;
Pipeline pipeline = new Pipeline(numCommands);
// 添加命令到管道
pipeline.addCommandtoUpperCase; // 处理函数1: 转大写
pipeline.addCommand((String s) -> new StringBuilder(s).reverse().toString()); // 处理函数2: 反转字符串
// 创建线程池管理器
ExecutorServiceManager executorServiceManager = new ExecutorServiceManager(numCommands);
// 启动管道处理
pipeline.start(executorServiceManager.executorService);
// 向第一个队列中添加数据
BlockingQueue<String> inputQueue = pipeline.getInputQueue();
inputQueue.put("hello");
inputQueue.put("world");
inputQueue.put("EOF");
// 关闭线程池并等待任务完成
executorServiceManager.shutdownAndAwaitTermination();
// 从最后一个队列中获取处理结果
BlockingQueue<String> outputQueue = pipeline.getOutputQueue();
String result;
while (!(result = outputQueue.take()).equals("EOF")) {
System.out.println(result);
}
public void execute() throws InterruptedException {
while (!Thread.interrupted()) {
String data = inputQueue.take();
if ("EOF".equals(data)) {
outputQueue.put(data);
break;
}
String result = processingFunction.apply(data);
outputQueue.put(result);
}
}
观察者-通知√
优势:
- 事件回调
- 异步,解耦
- 逐个触发监听器,减少内存占用
异步非阻塞 EventBus
对象A o-- List<对象B> ,A变化,B能收到通知
投资理财系统,用户注册成功后,给用户发放体验金/ 或者发欢迎消息
应用:邮件订阅,RSS Feeds
实现方式
- 同步阻塞:一个线程中执行,直到所有 Observer 观察者 代码执行完成后,继续执行被观察者
- 异步非阻塞:开启新线程调用 Observer,或者 EventBus
- 进程间:用户注册成功后,发用户信息给大数据征信系统,使用消息队列
EventBus
Observer 使用注解查找,EventBus 管理所有的 Observer,并决定是否异步执行
@startuml
!theme plain
top to bottom direction
skinparam linetype ortho
class AsyncEventBus {
+ AsyncEventBus(Executor):
}
class EventBus {
+ EventBus(Executor):
+ EventBus():
- registry: ObserverRegistry
- executor: Executor
+ unRegister(Object): void
+ register(Object): void
+ post(Object): void
}
class EventBusRegisObserver {
+ EventBusRegisObserver():
+ f(Long): void
}
class ObserverAction {
+ ObserverAction(Object, Method):
- target: Object
- method: Method
+ execute(Object): void
}
class ObserverRegistry {
+ ObserverRegistry():
- registry: Map<Class<?>, CopyOnWriteArraySet<ObserverAction>>
+ register(Object): void
- getAnnotatedMethods(Class<?>): List<Method>
+ getMatchedObserverActions(Object): List<ObserverAction>
- findAllSubscribers(Object): Map<Class<?>, Collection<ObserverAction>>
}
annotation SubScribe << annotation >>
class UserController {
+ UserController():
- userService: UserService
- DEFAULT_EVENTBUS_THREAD_POOL_SIZE: int
- eventBus: EventBus
+ register(String, String): Long
+ setRegisObservers(List<Object>): void
+ login(String, String): void
}
AsyncEventBus -[#000082,plain]-^ EventBus
UserController o-- AsyncEventBus
EventBus *-- ObserverRegistry
ObserverRegistry *-- ObserverAction
EventBusRegisObserver *-- SubScribe
UserController -- EventBusRegisObserver
@enduml
状态√
状态机实现方式
- 查表
- 状态模式
状态机概念:状态 State,事件 Event,动作 Action
备忘录
设计实例
给出功能需求,做设计实现。基于代码实现,讨论代码可读性,扩展性。
业务系统开发难度
- 高性能
- 业务复杂
限流,鉴权,告警,通知,灰度发布
CR
- 代码结构合理
- 代码容易理解
- 业务正确
- 异常全面
- 隐藏BUG
- 线程安全
- 性能满足业务需求
- 符合编码规范
写文档,使用 项目工具,描述上下文,确保能看懂设计意图
代码开放,要实现某个功能时,搜一下代码仓库,可以找到优质代码作为参考
位置
- 实体
- 用例:用例关注与输入数据并产生输出数据,但用例对象不应去关心数据如何传递给用户或其他任何组件
- 接口
就像企业的业务和组织,软件也有他的功能和设计。
功能是软件的第一价值、设计是第二价值。两者在软件不同的生命周期发挥着不同的作用,功能更多的是其紧急且重要部分,设计更多的是重要非紧急部分。设计可以降低系统的生命周期成本并提高程序员的生产力。架构师的职责是利用其专业知识为功能和设计分配资源以获得最佳的回报率。
关键业务逻辑应该是系统中最独立(独立于框架)和可重用(扩展而不改动)的代码。
关键的点就是推迟决定。很难在初期对我们的数据使用场景做个准确的预测,数据库作为细节隔离在核心系统外,以后因为性能规模等等问题变动数据库更加容易。
框架和业务耦合在一起,使用便利,但框架变动和局限。业务无法 预见抽象的必要性。
架构是演化的,不能简单地在项目的开始时决定实施哪些边界以及忽略哪些边界。相反,随着系统的发展,边界会慢慢变的清晰,你会关注到它,再权衡实施和忽略的成本,在实施成本低于忽略成本的拐点时做出决定。
Articles
读 Clean Architecture | DouO's Blog (dourok.info)
类命名
Bootstrap,Starter:启动类
Initializer:初始化
处理:
Handler:事件处理、请求处理、异常处理
- 观察者模式,订阅事件
- 请求链,处理请求
Processor:数据转换、批处理、管道处理
Node:数据结构或分布式系统中的组件
Command:参数化不同的请求,如 队列请求、日志请求、可撤销操作
Service:某个服务
Task:某个任务,通常指 Thread Runnable
Executor:线程
管理
Manager:资源入口,如TransactionManager
Holder:Map<String,Object> 对象集合存储。可以用于线程安全的单例模式,或是为对象提供额外的行为。
Factory:创建有关对象
Provider:资源入口,强调提供功能
Registrar:声明式存储管理配置信息
Engine:核心模块,处理特定的大功能。
传播类
Context:需要在方法间传递的变量
回调
Handler
Callback:接口,响应某类消息
Listener:只用于观察者模式
Aware:感知,提供内容
监控
Tracker:日志或监控值
内存管理
Pool:池子,Connection,Memory
过滤检测
Pipeline,Chain:责任链,Netty,Spring MVC,Tomcat等都有大量应用
Filter:请求的入口点和出口点工作,它们不深入到业务逻辑,通用功能,如编码转换、安全控制、压缩、日志记录
Interceptor:拦截器,深入到方法,可以插入代码
Evaluator:return bool
Detector:用于测试,如Android的手势检测,温度检测
结构类
Cache 缓存
Buffer 缓冲
Composite 组合
Wrapper 包装,增加或去掉功能
Option, Param,Attribute:配置信息
Batch:可以批量执行的对象
Limiter:限流器
Builder:建造者
Template:模版
Proxy:代理
Adapter:适配器
Strategy:同一方法结果不同,实现策略不同
解析
Converter,Resolver 转换,解析
Parser:非常复杂的解析器,如 DSL
Customizer:特别的配置
Formatter:格式化;字符串、数字或者日期的格式化
网络
Packet:网络中的数据包
Protocol:网络协议,如 HttpProtocol
Encoder、Decoder、Codec:编码,解码器
Request,Response:请求,响应
其他
Util:工具类
Helper:特定处理,如表单处理、数据映射、API调用;可以实例化
Mode,Type:枚举
Invoker:反射
Generator:生成器,生成代码或ID