脚本宝典收集整理的这篇文章主要介绍了还重构?就你那代码只能铲了重写!,脚本宝典觉得挺不错的,现在分享给大家,也给大家做个参考。
我们不一样,就你没对象!
对,你是面向过程编程的!
我说的,绝大多数码农没日没夜被需求憋着肝出来的代码,无论有多么的吭哧瘪肚,都不可能有重构,只有重新写。为什么?因为重新写所花的时间成本,远比重构一份已经烂成团的代码,要节省时间。但谁又不敢保证重写完的代码,就比之前能好多少,况且还要承担着重写后的代码事故风险和几乎体现不出来的业务价值
!
虽然代码是给机器运行的,但同样也是给人看的,并且随着每次需求的迭代、变更、升级,都需要研发人员对同一份代码进行多次开发和上线,那么这里就会涉及到可维护
、易扩展
、好交接
的特点。
而那些不合理分层实现代码逻辑、不写代码注释、不按规范提交、不做格式化、命名随意甚至把 queryBatch 写成 queryBITch 的,都会造成后续代码没法重构的问题。那么接下来我们就分别介绍下,开发好能重构的代码,都要怎么干!
# 提交:主要 tyPE
feat: 增加新功能
fix: 修复bug
# 提交:特殊 type
docs: 只改动了文档相关的内容
style: 不影响代码含义的改动,例如去掉空格、改变缩进、增删分号
build: 构造工具的或者外部依赖的改动,例如webpack,npm
refactor: 代码重构时使用
revert: 执行git revert打印的message
# 提交:暂不使用type
test: 添加测试或者修改现有测试
perf: 提高性能的改动
ci: 与CI(持续集成服务)有关的改动
chore: 不修改src或者test的其余修改,例如构建过程或辅助工具的变动
# 注释:类注释配置
/**
* @description:
* @author: ${USER}
* @date: ${DATE}
*/
日期_用户_用途
,210905_xfg_updateRuleLOGic作者,type: desc
如:小傅哥,fix:更新规则逻辑问题
参考Commit message 规范Editor -> File and Code Templates -> File Header
推荐下载安装 IDEA P3C 插件 Alibaba Java Coding Guidelines
,统一标准化编码方式。在编写 RPC 接口的时候,返回的结果中一定要包含明确的Code码
和Info描述
,否则使用方很难知道这个接口是否调用成功还是异常,以及是什么情况的异常。
定义 Result
public class Result implements java.io.Serializable {@H_263_126@
PRivate static final long serialVersionUID = 752386055478765987L;
/** 返回结果码 */
private String code;
/** 返回结果信息 */
private String info;
public Result() {
}
public Result(String code, String info) {
this.code = code;
this.info = info;
}
public static Result buildSuccessResult() {
Result result = new Result();
result.setCode(Constants.ResponseCode.SUCCESS.getCode());
result.setInfo(Constants.ResponseCode.SUCCESS.getInfo());
return result;
}
// ...get/set
}
返回结果包装:继承
public class RuleResult extends Result {
private String ruleid;
private String ruleDesc;
public RuleResult(String code, String info) {
super(code, info);
}
// ...get/set
}
// 使用
public RuleResult execRule(DecisionMatter request) {
return new RuleResult(Constants.ResponseCode.SUCCESS.getCode(), Constants.ResponseCode.SUCCESS.getInfo());
}
返回结果包装:泛型
public class ResultData<T> implements Serializable {
private Result result;
private T data;
public ResultData(Result result, T data) {
this.result = result;
this.data = data;
}
// ...get/set
}
// 使用
public ResultData<Rule> execRule(DecisionMatter request) {
return new ResultData<Rule>(Result.buildSuccessResult(), new Rule());
}
Code码
并做出相应的处理。三范式:是数据库的规范化的内容,所谓的数据库三范式通俗的讲就是设计数据库表所应该遵守的一套规范,如果不遵守就会造成设计的数据库不规范,出现数据库字段冗余,数据的查询,插入等操作等问题。
数据库不仅仅只有三范式(1NF/2NF/3NF),还有BCNF、4NF、5NF…,不过在实际的数据库设计时,遵守前三个范式就足够了。再向下就会造成设计的数据库产生过多不必要的约束。
三大范式是设计数据库表结构的规则约束,但是在实际开发中允许局部变通:
通常在我们实际的业务功能逻辑开发中,为了能满足一些高并发的场景,是不可能对数据库表上锁扣减库存、也不能直接for循环大量轮训操作的,通常需要考虑🤔在这样场景怎么去中心化以及降低@R_438_1304@。
秒杀:去中心化
独占锁
后的业务逻辑处理中发生异常,释放锁失败。导致所有的用户都不能再拿到锁,也就造成了有商品但不能下单的问题。算法:反面教材
@Test
public void test_idx_hashMap() {
Map<String, String> map = new HashMap<>(64);
map.put("alderney", "未实现服务");
map.put("luminance", "未实现服务");
map.put("chorology", "未实现服务");
map.put("carline", "未实现服务");
map.put("fluorosis", "未实现服务");
map.put("angora", "未实现服务");
map.put("insititious", "未实现服务");
map.put("insincere", "已实现服务");
long startTime = System.currentTimeMillis();
for (int i = 0; i < 100000000; i++) {
map.get("insincere");
}
System.out.println("耗时(initialCapacity):" + (System.currentTimeMillis() - startTime));
}
HashMap
存放业务实现key,通过key调用服务的功能。但这里的key
,只有insincere
有用,其他的都是未实现服务。那你看到有啥问题了吗?
new HashMap<>(64);
为啥默认初始化64个长度?因为默认长度是8,插入元素时,当链表长度为8时候会进行扩容和链表树化判断,此时就会把原有的key散列了,不能让所有key构成一个时间复杂度较高的链表。key
都是刻意选出来的,因为他们在 HashMap
计算下标时,下标值都为0,idx = (size - 1) & (key.hashCode() ^ (key.hashCode() >>> 16))
,这样就能让所有 key
都散列到同一个位置进行碰撞。而且单词 insincere
的意思是;不诚恳的、不真诚的
!key
,不起任何作用,只有最后一个 key 有服务。那么这样就可以在HashMap中建出来很多这样耗时的碰撞链表,当然要满足0.75
的负载因子,不要让HashMap扩容。其实很多算法包括:散列、倒排、负载等,都是可以用到很多实际的业务场景中的,包括:人群过滤、抽奖逻辑、数据路由等等方面,这些功能的使用可以降低时间复杂度,提升系统的性能,降低接口响应时常。
为了可以让程序的逻辑实现更具有扩展性,通常我们都需要使用设计模式来处理各个场景的代码实现结构。而设计模式的使用在代码开发中的体现也主要为接口的定义、抽象类的包装和继承类的实现。通过这样的方式来隔离各个功能领域的开发,以此保障每次需求扩展时可以更加灵活的添加,而不至于让代码因需求迭代而变得更加混乱。
案例
public interface IRuleExec {
void doRuleExec(String req);
}
public class Ruleconfig {
protected Map<String, String> configGroup = new ConcurrentHashMap<>();
static {
// ...
}
}
public class RuleDataSupport extends RuleConfig{
protected String queryRuleConfig(String ruleId){
return "xxx";
}
}
public abstract class AbstractRuleBase extends RuleDataSupport implements IRuleExec{
@override
public void doRuleExec(String req) {
// 1. 查询配置
String ruleConfig = super.queryRuleConfig("10001");
// 2. 校验信息
checkRuleConfig(ruleConfig);
// 3. 执行规则{含业务逻辑,交给业务自己处理}
this.doLogic(configGroup.get(ruleConfig));
}
/**
* 执行规则{含业务逻辑,交给业务自己处理}
*/
protected abstract void doLogic(String req);
private void checkRuleConfig(String ruleConfig) {
// ... 校验配置
}
}
public class RuleExec extends AbstractRuleBase {
@Override
protected void doLogic(String req) {
// 封装自身业务逻辑
}
}
类图
AbstractRuleBase
抽象类中,是负责完成整个逻辑调用的定义,并且这个抽象类把一些通用的配置和数据使用单独隔离出去,而公用的简单方法放到自身实现,最后是关于抽象方法的定义和调用,而业务类 RuleExec
就可以按需实现自己的逻辑功能了。你的代码出过线上事故吗?为什么出的事故,是树上有十只鸟开一枪还剩几只的问题吗?比如:枪是无声的吗、鸟聋吗、有怀孕的吗、有绑在树上的鸟吗、边上的树还有鸟吗、鸟害怕枪声吗、有残疾的鸟吗、打鸟的人眼睛花不花,… …
实际上你的线上事故基本回围绕在:数据库连接和慢查询、服务器负载和宕机、异常逻辑兜底、接口幂等性、数据防重性、MQ消费速度、RPC响应时常、工具类使用错误等等。
下面举个例子:用户积分多支付,造成批量客诉。
不够抽象、不能写死、不好扩展,是不是总是你的代码,每次都像一锤子买卖,完全是写死的、绑定的,根本没有一点缝隙让新的需求扩展进去。
为什么呢,因为很多研发写出来的代码都不具有领域聚合的特点,当然这并不一定非得是在DDD的结构下,哪怕是在MVC的分层里,也一样可以写出很多好的聚合逻辑,把功能实现和业务的调用分离开。
如果你想让你的系统工程代码可以支撑绝对多数的业务需求,并且能沉淀下来可以服用的功能,那么基本你就需要在做代码开发实现的时候,抽离出技术组件、功能领域和业务逻辑这样几个分层,不要把频繁变化的业务逻辑写入到各个功能领域中,应该让功能领域更具有独立性,可以被业务层串联、编排、组合实现不同业务需求。这样你的功能领域才能被逐步沉淀下来,也更易于每次需求都 扩展。
在分布式场景开发系统,要尽可能运用上分布式的能力,从程序设计上尽可能的去避免一些集中的、分布式事物的、数据库加锁的,因为这些方式的使用都可能在某些极端情况下,造成系统的负载的超标,从而引发事故。
你有了解过 HashMap 的拉链寻址数据结构吗、知道哈希散列和扰动函数吗、懂得怎么结合Spring动态切换数据源吗、AOP 是怎么实现以及使用的、MyBatis 是怎么和 Spring 结合交管Bean对象的,等等。看似都是些面试的八股文,但在实际的开发中其实是可以解决很多问题的。
@Around("aopPoint() && @annotation(dbrouter)")
public Object doRouter(ProceedingJoinPoint jp, DBRouter dbRouter) throws Throwable {
String dbKey = dbRouter.key();
if (StringUtils.isBlank(dbKey)) throw new RuntimeException("annotation DBRouter key is null!");
// 计算路由
String dbKeyAttr = getAttrValue(dbKey, jp.getargs());
int size = dbRouterConfig.getDbCount() * dbRouterConfig.getTbCount();
// 扰动函数
int idx = (size - 1) & (dbKeyAttr.hashCode() ^ (dbKeyAttr.hashCode() >>> 16));
// 库表索引
int dbIdx = idx / dbRouterConfig.getTbCount() + 1;
int tbIdx = idx - dbRouterConfig.getTbCount() * (dbIdx - 1);
// 设置到 ThreadLocal
DBContextHolder.setDBKey(String.format("%02d", dbIdx));
DBContextHolder.setTBKey(String.format("%02d", tbIdx));
logger.info("数据库路由 method:{} dbIdx:{} tbIdx:{}", getMethod(jp).getName(), dbIdx, tbIdx);
// 返回结果
try {
return jp.proceed();
} finally {
DBContextHolder.cleardbKey();
DBContextHolder.clearTBKey();
}
}
(size - 1) & (key.hashCode() ^ (key.hashCode() >>> 16));
解决数据碰撞严重的问题。以上是脚本宝典为你收集整理的还重构?就你那代码只能铲了重写!全部内容,希望文章能够帮你解决还重构?就你那代码只能铲了重写!所遇到的问题。
本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
如您有任何意见或建议可联系处理。小编QQ:384754419,请注明来意。