重构系统的套路-写有组织的代码

如果一个项目经历了快速发展,势必在业务发展背后留下了一个很无序,结构混乱的代码,无序而混乱的代码势必造成很大的bug修复及扩展成本。

说到搭建系统都在谈论高并发,大数据,而易于维护和可扩展性则被大部分人抛之脑后,增加最基础的面向对象思想和设计模式帮助我们组织好易于维护和阅读的代码。

不要好高骛远看一下高并发,高可用的东西,做好以下这些最基础的东西,你的系统在可读性和可维护可扩展方面将会提升一倍的能力,将大家的人效从每次bug产生梳理代码的过程中解放出来,建立标准建立原则才是架构师首先要做的。

  • 进行适当的封装
  • 开放封闭原则
  • 单一只能原则
  • 代码具备随时删掉而不影响上下文的能力

命名与注释

命名需要见名知义,注释则可以帮助我们去了解当时的业务逻辑,不然后期只能通过一行行的日志去定位问题了。

考虑到IDE会帮助我们创建变量名称,如果名称相似则存在误用可能造成很难定位的BUG,入参可以以Req结尾或者Command结尾,返回值以Resp结尾或者Result结尾。

抽离业务骨骼

Web系统的入口层是Controller,基于RPC的服务入口层往往是XxxServiceImpl,入口层应该像一本书的目录和前言部分,说明了这个方法的主要目的,同时梳理了核心的业务流程,流程不要太细或者太粗,刚刚好满足产品的需求骨骼为主,可以简单的理解为是产品PRD的信息抽象。

建立胶水层代码

大型系统或者业务系统具有一定的复杂程度,势必在某些问题场景包含一些必要的大逻辑处理,于是建立一个胶水层代码,胶水类可以用入口层的一个方法名称作为名称定义。

胶水层代码向外暴露的public接口则为入口层的核心骨骼逻辑,将内部复杂逻辑进行封装,达到部分方法随时可以删掉,注释掉,替换掉而不影响核心骨骼逻辑的效果,可以理解为TDD,关注入参和返回值就好。

如果通过RPC或者ResultAPI和其他系统具有一定的依赖,则放到这里。

其实这层有些上DDD里面的Domain,但是DDD用不好的话在分布式微服务场景下会出现很难把控的问题。

下层依赖代码

在入口和业务逻辑之下基本就是Service层代码和Dao层代码了,Dao主要是和存储系统打交道,主要目的是可以随时切换到其他的存储逻辑中去,而不影响上层业务和代码。

Service则是进行一定的数据结构组织,数据结构可能来自于底层Dao,可能来自于消息队列的订阅,可能来自于Redis缓存或者Hbase等,放在这一层可以有效分离依赖系统数据和本系统数据。

示例

入参

public class WmPoiReq {
    private long userId;
    private long wmPoiId;
    private int channel;
}

返回结果

public class WmPoiResp {
} 

实体

public class Activity {
    int activityId;
    Long wmPoiId;
    int channel;
    long valida;
}

服务入口

public class CServiceImpl { 

    /**
     * 发送积分
     * @param req
     * @return
     */
    public WmPoiResp wmPSendC(WmPoiReq req){
        // 入参不合法,及时失败
        if(null == req || req.getUserId() < 1l || req.getPId() < 1l || req.getCh() < 0){
            throw new IllegalStateException("参数无效");
        } 

        PSendCHandler handler = new PSendCHandler();
        // 1. 获取可用列表
        List<Activity> activities = handler.getActivityList(req.getPId(), req.getUserId(), req.getCh()); 

        // 2. 满足,进行发操作
        boolean sendStatus = handler.sendC(req.getPId(), activities); 

        // 3. 调用接口服务化发
        // RPC调用服务发送 

        WmPoiResp resp = new WmPoiResp();
        return resp;
    }
}

创建胶水代码,实现流程细节

public class PSendCHandler { 

    /**
     * 获取可用活动列表
     * @param iId
     * @param userId 用户id
     * @param channel
     * @return
     */
    public List<Activity> getActivityList(long PiId, long userId, int channel){
        // 1. 根据PiId, channle获取活动列表
        List<Activity> activities = ... // 假装从底层数据获取
        if(activities.size() == 0) return new ArrayList<>(); 

        // 2. 判断获取是否已过期
        boolean expire = activities.size() > 0 ? activities.get(0).getValida() > new Date().getTime() : true;
        if(expire) return new ArrayList<>(); 

        // 3. 判断是否是新用户
        boolean freshMan = ... // 假装从底层数据获取 

        if(freshMan){ // 新用户,验证是否有适用于新用户活动
           Iterator<Activity> iterator = activities.iterator();
           while (iterator.hasNext()){
               // 检查每个activity是否适用于新用户
           }
           // 所有活动不适用于新用户
            return new ArrayList<>();
        } 

        // 返回可用活动列表
        return activities;
    } 

    /**
     * 发券
     * @param PiId 门店id
     * @param activities 活动集合
     * @return
     */
    public boolean sendC(long PiId, List<Activity> activities){
        // 通过线程池异步发
        // 同时记录缓存
        return true;
    }
}

总结

其实总结起来很简单,增加必要的封装和抽离,通过入参和返回值把控。

用看书的思维组织代码系统的,增加一个业务的可阅读可理解能力,在一个系统发展一定阶段之后,最让RD同学苦恼的不是技术问题,往往是一些业务逻辑或者布丁代码,所以研发同学要有意识的对业务和技术进行抽离,而不是简单的将技术和业务纠缠在一起,做好某块业务逻辑代码随时可以删掉而不影响系统的能力。

建立适当的代码命名规则,避免IDE带来的不必要的误用。

丰富wiki及文档,涉及到测试用例,数据库字段文档,产品PRD等。

原文地址:https://www.cnblogs.com/xiguain/p/9237418.html

时间: 2024-11-12 08:20:29

重构系统的套路-写有组织的代码的相关文章

重构系统的套路-提高并发能力

提高系统并发能力,总结起来有三点:异步,缓存,并行. 异步 比如我们在某段业务逻辑中加了一个同步写kafka的操作,tp99瞬间多了30毫秒,这样在整个监控曲线看起来非常扎眼,于是我们需要将这个同步改成异步. 对于老系统需要在业务进行梳理,如果业务场景中不关心返回值,这样完全可以做成异步. 如果业务关心返回值,比如订单逻辑,很多下游服务需要传入本次主订单ID与下游服务进行连接,这样写主订单就必须变成了一个同步逻辑,但是主要关心的还是这个OrderId,我们可以建立一个OrderId生成器,这样单

重构系统的套路-微服务化

服务拆分 根据业务或组织架构进行基本服务拆分,每个服务实例会拥有专属的网络地址.独立的计算资源,并且独立部署.客户端通过访问服务实例的地址来调用服务 API.不同服务也可以相互调用. 统一配置管理 一个服务可能会跑多个实例,每个服务实例都会需要做配置.为了方便统一调整配置,我们可以把配置中心化,每个服务实例都去找配置管理器(Configuration Manager)拿配置.当配置更新的时候,我们也可以让服务实例再去拿新的配置. 命名服务 多服务实例带来的问题:网络地址(比如 IP)很容易因为扩

机房重构系统的分类

这段时间真的是一心扑上机房了,不断的克服一些对VB.NET语言的水土不服的感觉后,终于对个人版机房重构有了很直观的认识.这个系统跟我们上一次的机房功能一样,也是对自己学习情况的一个检查,经过对机房的望闻问切,我觉得这张图很符合我对机房的理解. 在机房重构中,重点还是在系统与数据库的交互过程,同时在这次的系统中,我们还要考虑系统开发的时候的编码规范化.正规化,把我们在C/S过程中学到的知识运用到机房重构中,这就是检验我们的学习成果. 总结下这段时间的收获:对机房重构的功能进行分类,便于理清整个系统

IOS APP 国际化(实现不跟随系统语言,不用重启应用,代码切换stroyboard ,xib ,图片,其他资源)

接了个变态的需求,要在程序内切换程序语言实现国际化. 可以先看看这个,比较详细. http://blog.csdn.net/xwren362922604/article/details/17190061 看完之后问题来了, 1,工程里面大量的 xib  或 storyboard 里面的UI 设定了text ,怎么实现让它们动态加载语言[非设置系统语言重启] 2,一个简单的思路,当代码触发切换语言 发个通知让 内存里的viewController 刷新UI的text .[工程里这么都vc 每个都加

Unity如何有效的组织的代码

转自知乎:梁伟国 准确地说,代码作为Unity项目里的一种资源,此问题应该扩展到如何组织Unity资源.简单说说我们的经验:- Unity有一些自身的约定,譬如项目里的Editor,Plugins等目录作为编辑器,插件目录等等.知名的插件会自己存放一个目录,譬如NGUI等.所以我们自己的代码,一般目录名会以下划线开头,譬如 "_Scripts", "_Prefabs"等.对于场景,文档等目录,用两条下划线,以便他们能排在最顶部.- 代码用C#,别用JS.必要的话用n

linux内核探索之内存管理(二):linux系统中的内存组织--结点、内存域和页帧

本文主要参考<深入linux内核架构>(3.2节)及Linux3.18.3内核源码 概述:本文主要描述了内存管理相关的数据结构:结点pg_data_t.内存域struct zone以及页帧(物理页):struct page ,以及该结构相关的一些基本概念. 1. 概述 内存划分为接点,每个结点关联到系统中的一个处理器,在内核中表示为pg_data_t. 各个结点又划分为内存域,比如DMA内存域,高端内存域,普通内存域. 内核内存域的宏: enum zone_type { #ifdef CONF

组织Golang代码

本月初golang官方blog(需要自己搭梯子)上发布了一篇文章,简要介绍了近几个月Go在一 些技术会议上(比如Google I/O.Gopher SummerFest等)的主题分享并伴有slide链接.其中David Crawshaw的“Organizing Go Code”对Golang的代码风格以及工程组 织的最佳实践进行的总结很是全面和到位,这里按Slide中的思路和内容翻译和摘录如下(部分伴有我个人的若干理解). 一.包 (Packages) 1.Golang程序由package组成

常用的6种代码重构方法帮你提升80%的代码质量

在过去做了不少代码走读,发现了一些代码质量上比较普遍的问题,以下是其中的前五名: 臃肿的类: 类之所以会臃肿,是因为开发者缺乏对最基本的编码原则,即“单一职责原则”(SRP)的理解.这些类往往会变得很臃肿,是由于不同的且在功能上缺少关联的方法都放在了相同的类里面. 长方法: 方法之所以会变得很长主要是有以下几个原因: 许多没有关联性的.功能复杂的模块的代码都放在相同的方法内.这主要是开发者缺乏SRP的概念. 多种条件都放在同一个方法内,这在长方法内经常会发生的.这是由于缺乏McCabe代码复杂度

电影院购票系统1.0--还未看老师代码自己写的

/*打开团购软件后,进入电影票购买模块.可选择影院地址优先或影片优先(暂时实现以下标号的步骤) 1.欢迎界面,内容:按1-选择电影票 按2-选择演唱会门票(2暂时不实现,实现方法同电影票) 2.接收输入1,显示所有电影票信息(暂定2部):电影编号,影片名字,主演,上映时间,时长,票价,是否3D(BOOL),放映厅号 3.接收输入,输出对应影片的大厅的座位表(打出行列号和座位(NSMutableArray数组,遍历数组转成c字符串后,printf分行打印) 提示用户选择座位行列号. 4.接收用户输