0
评估代码质量:可维护性,可读性,可拓展性,灵活性,简洁性,可复用性,可测试性
1. 面向对象
抽象:接口interface,函数方法;作用屏蔽细节;
封装:作用隐藏信息,可控
继承:作用:复用
多态:继承并重写,interface,作用,提高代码复用性,设计模式实现的基础
破坏面向对象
- 所有属性都有get set,任意处都能调用修改破坏了一致性和封装
- constants类,被很多地方引用,打包引入不必要的(尽量放到constant依赖的类中,尽量细化,如redisconstant,dbconstant),utils类同理
- 基于贫血模型的开发方式
抽象类和接口(基于接口而非实现编程)
is-a和has-a的区别
抽象类:代码复用
接口:解耦(定义和实现分离)
基于接口而非实现编程
- 函数命名不能暴露实现细节
- 使用者依赖接口编程
- 接口通用,替换实现的时候,不需要1接口定义的改动
多用组合少用继承
继承层次高,可读性降低,父类抽象方法违背最小知识原则(AbatractBird fiy())
接口,组合和委托代替继承(flyable接口)
结构稳定,继承关系简单,层次低适合用继承(模版模式)
mvc,充血,贫血,domain
do,vo和service分离,数据和业务隔离,贫血模型,破坏了封装性
充血模型的ddd也是按照mvc三层架构,区别在service层,
贫血(重service轻bo);适合简单的业务
充血(重domain轻service);适合复杂的业务
充血模型业务逻辑代码移到领域模型domain(充血的bo)中,能实现复用,service层跨领域模型调用,还有幂等,mq,log等,service依赖domain
面向对象开发
面向对象分析:输出需求描述,或者流程图时序图
面向对象设计:(需求转化为类的设计)
1. 识别哪些类(authToken,apiRequest,passwordStorage)
2. 定义类的属性方法
3. 定义类与类之间关系(泛化,实现,组合,依赖)
4. 类组装提供入口(auth函数)
面向对象实现:(编码)
2. 设计原则
SOLID原则
- S(single)单一原则:不同场景标准不一,可以通过方法是否过多,依赖的类过多,私有方法过多,难以命名;
- 开闭原则:对拓展开放,对修改关闭(新功能要拓展代码而不是修改代码);编码参考
实践:多态,基于接口编程,依赖注入,设计模式 - 里氏替换:子类替换父类,不改变原有程序逻辑及正确性;拿父类的单元测试运行子类,结果要一致
- 接口隔离原则:不要暴露不必要的接口功能,类似单一原则
- 依赖反转原则(依赖倒置)
控制反转:对流程的控制从程序员到代码框架;可以通过依赖注入,模版模式等
依赖注入:不通过new的方式,而是传参,构造函数等,注入的对象可以是接口
spring的控制反转就是通过依赖注入实现的
依赖反转:高低层之间通过抽象(接口)来互相依赖,抽象不依赖细节,细节依赖抽象
KISS原则(keep it short and simple);可读性高,不要过度优化,用已有类库
DRY原则(donot repeat yourself);do,vo,bo重复,可以用继承来优化
迪米特原则:模块只应该了解与之关系密切模块的有限知识(有限的方法);有限知识可以通过实现多个接口来实现
方法技巧:画图,聚焦简单应用场景,然后迭代;画系统图;拆分;看26,39,40
3. 编程规约与代码重构
小重构
单元测试保证重构不出错
提高代码可测试性,mock数据,依赖外部的服务就mock解依赖
大重构
解耦,方法:封装抽象;中间层;模块化
编码技巧
1:不要用函数参数控制逻辑
2:使用解释形变量
看35
异常返回
能用异常尽量不用错误码;因为异常能携带更多信息
查找的函数不存在返回异常,null,-1都可以;但是要规范和说明清楚
返回空集合或者空字符串不用做判空逻辑
异常抛出,看外层是否关心,关心则抛出,否则吞掉
4. 设计模式
创建型:单例,工厂
结构型:代理,乔接,装饰者,适配器
行为型:观察者,模版,策略,责任链
1.创建型(创建和使用的解耦)
单例
作用1:解决并发冲突
2:代表全局唯一,配置类
private构造函数,创建对象要线程安全,getInstance性能要高,是否延迟加载
1:饿汉式,类加载的时候instance就已经创建并初始化,缺点不支持延迟加载,占用资源多
2:懒汉式,可延迟加载,缺点是getInstance方法需要加锁,并发度低
3:两阶段锁+volitile(禁止冲排序访问到并发中未初始化的对象)
4:静态内部类;外部类加载的时候不会创建内部类的实例对象,getInstance创建内部类
5:枚举;利用枚举本身的特性
单例是进程间唯一,线程间唯一可以用threadlocal
工厂
工厂方法,抽象工厂
简单工厂:通过if返回对应的多个对象
工厂方法:去调简单工厂的if(工厂的工厂)
构建者模式(Builder,使用)
建造者类负责对象的创建;可以定制化的创建对象
适合以下场景
原型模式(复制对象)
深浅拷贝
不可变模式
jdk 的Collections.unmodifiableList 引用
guava的ImmutableList.copyof 值,深拷贝
2.结构型(一层wrapper类封装原始类)
代理模式,装饰器,适配器可以统称为wrapper模式,可以通过组合,对原始类二次封装
代理模式(给原始类增加功能,使用)
功能:不改变原始类,通过代理类增加功能
实现方式
1: 代理类和原始类实现相同的接口,代理类中注入原始类,然后代理类添加增加的代码;通过接口创建代理类进行调用
2: 对于外部类,可以通过继承的方式
3: 动态代理,即运行时动态创建代理类; @Around注解
@Pointcut("execution(* com.controller.*.*(..))")
public void pointCut2() {}
@Around("pointCut2()")
public Object run2(ProceedingJoinPoint joinPoint) throws Throwable {
//获取方法参数值数组
Object[] args = joinPoint.getArgs();
//得到其方法签名
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
//获取方法参数类型数组
Class[] paramTypeArray = methodSignature.getParameterTypes();
logger.info("请求参数为{}",args);
//动态修改其参数
//注意,如果调用joinPoint.proceed()方法,则修改的参数值不会生效,必须调用joinPoint.proceed(Object[] args)
Object result = joinPoint.proceed(args);
logger.info("响应结果为{}",result);
//如果这里不返回result,则目标对象实际返回值会被置为null
return result;
}
桥接模式(try)
将抽象部分与实现部分分离,使它们都可以独立的变化。
抽象类依赖实现类,jdbc抽象代码依赖mysql实现
装饰器模式(原始类增强的功能,支持嵌套,使用)
场景:房子装修,画图,io类
shape类画图,抽象draw方法,但要有颜色,粗细等功能的话。都放在draw方法中不合适,此时装饰器嵌套
与代理区别:可以层层增加增强的功能。而代理模式则一般是用于增加特殊的功能
解决继承关系过于复杂的问题,给原始类增强的功能,在不想增加很多子类的情况下扩展类。
实现:
装饰器类继承父类;多个子类可以嵌套(类似代理模式的实现方式1),一层嵌套代表多一层功能,实现功能增强
Component 类充当抽象角色,不应该具体实现。 2、修饰类引用和继承 Component 类,具体扩展类重写父类方法。
适配器模式(兼容老接口,调用依赖的外部接口,implents ITarget,使用)
可以通过组合(内部依赖对象)或者继承的方式
如果adaptee和itarget很多一样的方法,推荐用继承,代码少一点
如果adaptee和itarget很多一样的方法,推荐组合,更灵活
门面模式
为子系统提供一个统一的接口,定义一组高层接口让子系统更易用
解决了易用性(屏蔽底层),高性能(减少调用),分布式事务问题(spring同一接口事物)
组合模式
将一组对象组合成树形结构
这种模式创建了一个包含自己对象组的类
享元模式(实现)
被共享的单元,享元对象需要是不可变对象
可以用工厂类+map存储不变对象
3. 行为型(行为的解耦)
观察者模式(EventBus,看源码,使用)
一对多的依赖,被依赖的发生改变,依赖对象做出响应
producer-consumer;subject-observer;dispatcher-listener;
一般的模版
上面是同步阻塞,还可以异步非阻塞(新线程),跨进程,跨应用(mq,rpc)
可以用google的guava EventBus
模版模式(复用,扩展,使用)
模版定义骨架,可以定义为final,某些实现延迟到子类实现,abstract
应用:
复用:java inputstream read(),abstractList add()。
扩展:servlet的父类的service方法是模版方法,会调用子类可以自己实现的doGet和doPost方法
和回调的区别(看)
策略模式(解耦策略的定义,创建和使用,避免if,else太多,使用)
定义一组方法,可以相互替换,比如通道的选择
定义通道接口并实现,工厂类创建通道,调用工厂类使用不同通道策略,需要传type
避免if,else;满足开闭原则,新增策略时,集中化改动代码,减少引入bug风险
责任链模式(写,前置处理,使用)
存在两种,一种截断式,另一种所有处理器都要处理
看servlet的实现63
状态模式(没看)
迭代器模式(类库已提供,看)
访问者模式(一般不用,难,没看)
备忘录模式(没看,用的不多)
主要用于防丢失,恢复
命令模式(不常用)
把函数封装成对象可以存储下来,主要适用于控制命令的执行,比如异步,延迟,排队执行命令(请求),存储命令(请求)
游戏的服务端,接收命令和处理逻辑
解释器模式(没看,很少用,规则引擎,编译器)
中介模式(少用)
类似代码的中间层,将多对多的网状关系转为一对多的形状关系
飞机和塔台沟通而不是其他飞机沟通,微服务的服务治理
经常看74的总结回顾
5. 实战
看77
大型项目开发经验linux:封装和抽象,分层模块化,基于接口通信,高内聚松耦合;为扩展设计(讲可变部分封装接口供外部使用,只改变实现);kiss原则
Guava