# design-pattern **Repository Path**: my-resources/design-pattern ## Basic Information - **Project Name**: design-pattern - **Description**: 设计模式 - **Primary Language**: Java - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 1 - **Created**: 2021-03-16 - **Last Updated**: 2022-07-15 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 设计模式之美 #### 大纲 ![](./pic/总概.png) #### 面向对象 ![](./pic/面向对象.png) ![](./pic/面向对象2.png) #### 设计原则 ![](./pic/设计原则.png) ![](./pic/设计原则2.png) #### 编程规范 ##### 命名与注释 ```txt 1. 命名-> 准确达意 2. 借助类信息简化属性、函数命名,利用函数信息简化函数参数的命名 3. 命名可读、可搜索,避免使用生僻、不好读的英文单词 4. 接口命名方式:1)接口前缀带"I";2)接口实现带后缀"Impl"。抽象类带前缀"Abstract" 5. 注释内容:做什么、为什么、怎么做。复杂的类和接口,还要写明"如何用" 6. 类和函数一定要写注释,尽可能全面详细 ``` ##### 代码风格 ```txt 7. 函数的代码行数不要超过一个屏幕大小, 比如50行 8. 一行代码最好不要超过IDE的显示宽度 9. 善用空格分隔单元块 10.推荐俩格缩减,节省空间 11.大括号跟上一条语句同一行, 节省代码行数 12. 依赖类按照字母序从小到大排列 类中先写成员变量后写函数 成员变量间、函数间,先写静态成员变量或函数,后写普通变量或函数,按照作用域大小依次排列 ``` ##### 编程技巧 ```txt 13. 复杂的逻辑提炼拆分为函数、类 14. 通过拆分多个函数将参数封装为对象的方式,处理参数过多问题 15. 函数中避免使用参数做代码执行逻辑的控制 16. 函数设计要职责单一 17. 移除过深的嵌套层次 18. 用字面常量取代魔法数 19. 用解释性变量解释复杂表达式 ``` ##### 代码规范 ```txt 10. 团队或项目执行统一的编码规范,code review督促执行 ``` #### 代码重构 ##### 定义(what) ```txt 是一种对软件内部结构的改善,目的是在不改变软件的可见行为的情况下,使其更易理解,修改成本更低 ``` ##### 目的(why) ```txt 1. 项目角度:保持项目代码质量持续可控,不至于腐化到无可救药 2. 个人角度:锻炼代码能力 ``` ##### 对象(what) ```txt 1.大规模高层次【顶层设计层】:代码分层、模块化、解耦、梳理类间交互关系、抽象复用层件 2.小规模低层次【代码细节】:规范命名、注释、修正函数参数过多、消除超大类、提取重复代码。。 ``` ##### 时机(when) ```txt 持续重构、养成良好习惯 ``` ##### 方法(how) ```txt 1. 大规模高层次:难度大,需要组织、计划地进行,分阶段小步快跑 2. 小规模低层次:影响范围小、改动耗时短,随时随地都可以做 ``` #### 单元测试 ##### 对象 ```txt 概念:代码层面的测试,用于测试"自己"编写的代码的正确性 单元:一般指类或函数,不是指模块或系统 ``` ##### 编写原因 ```txt 1. 有效发现代码中Bug、设计上的问题 2. 写单元测试的本身就是代码重构的过程 3. 单元测试是对集成测试的有利补充,有利于快速熟悉代码,是TDD可落地的这种方案 ``` ##### 如何编写 ```txt 概念:针对代码设计覆盖各种输入、异常、边界条件的测试用例,并将其翻译成代码的过程 方法:借助测试框架来简化测试代码的编写 认知: 1) 编写单元测试尽管繁琐,但并不太耗时 2) 可以稍微放低单元测试的质量要求 3) 覆盖率作为衡量单元测试好坏的唯一标准是不合理的 4) 写单元测试一般不需要了解代码的实现逻辑 5) 单元测试框架无法测试多半是代码的可测试行不好 !!! ``` ##### 落地难 ```txt 本身比较繁琐、技术挑战不大、很多人不愿意写 国内研发比较偏向快糙猛,容易因为开发进度紧导致单元测试的执行虎头蛇尾 没有建立对单元测试正确的认识,认为可有可无,单靠督促很难执行的很好 ``` ##### 代码的可测试行 ```txt 定义:针对代码编写单元测试的难易程度 手段:1)依赖注入通过mock的方法将不可控的依赖变得可控;2)二次封装 不好测试代码: 1)未决行为(代码输出是随机或不确定的,例如:时间、随机数) 2)滥用可变全局变量 3)滥用静态方法 4)使用复杂的继承关系 5)高度耦合的代码 ``` #### 设计模式 ```txt 设计模式主要干的事情就是解耦. 创建型:将创建和使用代码解耦 结构型:将不同功能代码解耦 行为型:将不同行为代码解耦 ``` ##### 创建型 - 单例模式 ```txt 定义:用来创建全局唯一的对象 实现方式:饿汉式、懒汉式、双重检测、静态内部类、枚举 ``` - 工厂模式 ```txt 简述:针对创建相同类型对象的时候,封装对象的创建过程,将对象的创建和使用相分离 细分:简单工厂(最常用)、工厂方法、抽象工厂 场景:依赖注入框架(Spring IOC、Google Guice) ``` - 建造者模式 ```txt 概述:用来创建复杂对象,通过设置不同的可选参数,"定制化"地创建不同地对象 场景: 类的必填参数放到构造函数中,同时必填属性很多,利用set()方法设置时地校验逻辑 属性存在一定依赖关系、约束条件 创建不可变对象,对象创建完毕后无法在修改内部属性值 ``` - 原型模式 ```txt 概述:利用已有对象(原型)进行复制地方式来创建新对象,节省创建时间地目的 场景:对象地创建成本比较大,同一个类地不同对象之间差别不大(大部分字段都相同) 思考:深拷贝、浅拷贝 ``` ##### 结构型 - 代理模式 ```txt 概述:在不改变原始类接口地条件下,为原始类定义一个代理类 实现方式:接口、继承;静态代理 vs 动态代理 场景:非功能性需求(监控、统计、鉴权、限流、事务、幂等、日志、RPC、缓存) ``` - 桥接模式 ```txt 理解:将抽象与实现解耦,让它们独立开发 组合优于继承 ``` - 装饰器模式 ```txt 解决问题:继承关系过于复杂地问题,通过组合代替继承,给原始类添加增强功能 特点:对原始类可以嵌套使用多个装饰器 =》装饰器类需要与原始类继承相同地抽象类或接口 ``` - 适配器模式 ```txt 概述:将不兼容地接口转换为可兼容的接口 实现方式:类适配器【继承方式】、对象适配器【组合】 场景:封装有缺陷的接口设计 统一多个类的接口设计 替换依赖的外部系统 兼容老版本接口 适配不同格式的数据 ``` - 门面模式 ```txt 概述:封装细粒度接口,提供组合各个细粒度接口的高层次接口,提供接口易用性 场景:接口易用性 性能问题 分布式事务 ``` - 组合模式 ```txt 场景:树形结构数据 ``` - 享元模式 ```txt 概述:将对象设计成享元,内存中只保留一份实例,供多处代码引用,可以减少内存中对象的数量、节约内存 实现:工厂模式 ``` ##### 行为型 - 观察者模式 ```txt 概述:解耦观察者和被观察者代码 场景:代码层面、架构层面 实现:同步阻塞、异步非阻塞(eventbus 框架)、进程内、跨进程(消息队列) ``` - 模板模式 ```txt 定义:模板方式模式在一个方法中定义一个算法骨架,并将某些步骤延迟到子类中实现 作用:复用、拓展 实现:抽象类、回调 ``` - 策略模式 ```txt 概述:定义:模板方式模式在一个方法中定义一个算法骨架,并将某些步骤延迟到子类中实现 包含部分:定义、创建、使用 实现:工厂模式 ``` - 职责链模式 ```txt 概述:将请求的发送和接受解耦,让多个接受对象都有机会处理这个请求 场景:过滤器、拦截器。。。 ``` - 迭代器模式 ```txt 概述:也叫游标模式,它用来遍历集合对象 作用:解耦容器代码和遍历代码 问题:删除 + 遍历 =》 fail-fast ``` - 状态模式 ```txt 概述:一般用来实现状态机,而状态机常用在游戏、功能引擎等系统开发中 实现方式:状态模式、分支逻辑发、查表法 ``` - 访问模式 ```txt 定义:允许一个或者多个操作应用到一组对象上,解耦操作和对象本身 缺点:代码实现复杂、难以理解 ``` - 备忘录模式 ```txt 概述:也叫快照模式 场景:防止丢失、撤销、恢复等 ``` - 命令模式 ```txt 概述:将请求(命令)封装为一个对象 场景:异步、延迟、排队执行命令、撤销重做命令、存储命令、给命令记录日志 编码实现:将函数封装成对象 ``` - 解释器模式 ```txt 定义:解释器模式为某个语言定义它的语法(或者叫文法)表示,并定义一个解释器用来处理这个语法 核心思想:将语法解析的工作拆分到各个小类中,避免大而全的解析类 ``` - 中介模式 ```txt 定义:中介模式定义了一个单独的(中介)对象,来封装一组对象之间的交换 作用:将多组对象交互关系从多对多转换为一对多、降低代码复杂度 ``` #### 案例分析 ##### JDK源码分析设计模式 ```java java.util.Calendar - 工厂模式: 通过时区来生成对象 getInstance(java.util.TimeZone, java.util.Locale) - 建造者模式:内部类来定制化创建对象 java.util.Calendar.Builder java.util.Collections -装饰器模式:增强原始类 UnmodifiableCollection(Collection c) -适配器模式:兼容老版本接口 enumeration(final Collection c) -模板模式:拓展实现 sort(List list, Comparator c) java.util.Observer、java.util.Observable -观察者模式 java.lang.Runtime -单例模式 java.util.AbstractList -模板模式 java.lang.Integer -享元模式 ``` ##### Unix开源项目学习应对复杂项目 ```yaml 设计原则和思想角度 -封装与抽象 -分层与模块化 -基于接口通信 -高内聚、松耦合 -为拓展而设计 -KISS 首要原则 -遵循开发规范 研发管理和开发技巧角度 -吹毛求疵般地执行编码规范 -编写高质量地单元测试 -不流于形式地Code Review -开发未动、文档先行 -持续重构 -对项目与团队进行拆分 Code Review角度 -践行"三人行必有我师" -摈弃"个人英雄主义" -能有效提高代码可读性 -技术传帮带的有效途径 -保证代码不止一个人熟悉 -可以打造良好的技术氛围 -是一种技术沟通方式 -能提高团队自律性 -https://github.com/xindoo/eng-practices-cn ``` ##### Google Guava学习发现与开发通用功能模块 ```yaml 业务开发中发现、提炼非业务、可复用的功能点 产品意识、服务意识、代码质量意识、不要重复造轮子 ``` ##### Spring ```yam Spring框架蕴含的经典设计思想或原则 -约定优于配置 -低侵入、松耦合 -模块化、轻量化 -再封装、再抽象 Spring框架用来支持拓展的俩种设计模式 -观察者模式 ===》 spring event -模板模式 ===> spring bean Spring框架中用到的其他十几种设计模式 -适配器模式 ===》 mvc 处理器类型不同(注解驱动、实现 controller接口、继承httpServlet) org.springframework.web.servlet.DispatcherServlet#getHandlerAdapter -策略模式 ===》 aop 的动态代理采用Jdk的动态代理还是Cglib org.springframework.aop.framework.DefaultAopProxyFactory#createAopProxy -组合模式 ===》Spring Cache org.springframework.cache.CacheManager#getCache -装饰器模式 ===》数据库 + 缓存 -工厂模式 ===》IOC容器 -解释器模式 ===》SpEL表达式 spring-expression -职责链模式 ===》拦截器 -代理模式 ===》 AOP ``` ##### Mybatis ```yam Mybatis 如何权衡代码的易用性、性能、灵活性 -半自动的orm框架,执行开发者提供的业务SQL -易用性:Hibernate > MyBatis > JdbcTemplate -性能:JdbcTemplate > MyBatis > Hibernate -灵活性:MyBatis > JdbcTemplate > Hibernate 如何利用职责链与代理模式实现MyBatis Plugin -自定义插件类实现接口: org.apache.ibatis.plugin.Interceptor, 并将插件类配置到mybatis配置文件中 -mybatis 解析配置文件并架加载插件类 -org.apache.ibatis.builder.xml.XMLConfigBuilder#parseConfiguration -插件解析 org.apache.ibatis.builder.xml.XMLConfigBuilder#pluginElement -插件注册 org.apache.ibatis.session.Configuration#addInterceptor -mybatis 执行类包装及代理 -执行类/被代理对象:Executor、StatementHandler、ParameterHandler、ResultSetHandler -org.apache.ibatis.session.Configuration#newExecutor -多个插件包装 org.apache.ibatis.plugin.InterceptorChain#pluginAll -动态代理改方法:org.apache.ibatis.plugin.Plugin#invoke - 先运行插件方法后执行正常逻辑 总结罗列MyBatis 框架中用到的十几种设计模式 -建造者模式 ===》org.apache.ibatis.session.SqlSessionFactoryBuilder -屏蔽构造细节 -工厂模式 ===》 org.apache.ibatis.session.SqlSessionFactory -组合多个参数生成对象 -模板模式 ===》 SQL执行抽象类 org.apache.ibatis.executor.BaseExecutor -解释器模式 ===》 解析动态SQL -语法片段 org.apache.ibatis.scripting.xmltags.SqlNode -调用入口 org.apache.ibatis.scripting.xmltags.DynamicSqlSource#getBoundSql -单例模式 ===》 线程唯一单例 org.apache.ibatis.executor.ErrorContext -装饰器模式 ===》缓存 org.apache.ibatis.cache.impl.PerpetualCache -迭代器模式 ===》属性解析器 org.apache.ibatis.reflection.property.PropertyTokenizer -适配器模式 ===》日志框架 org.apache.ibatis.logging.Log ``` #### 项目实战 ##### [设计实现一个支持各种算法的限流框架](./project_one.md) ##### 设计实现一个通用的接口幂等框架 ##### 设计实现一个支持自定义规则的灰度发布组件