没有十几年的积累,你还真写不出什么好的代码

news2024/11/13 11:07:25

        如标题所说,我不知道大家怎么看待这句话?

        拿一个正常的程序员举个例子,18开始上大学学习写代码,22岁大学毕业,一直干到30岁就需要考虑程序员的中年危机。

        小编身边很多程序员都不喜欢写代码,感觉写代码没有意思, 每天围绕着业务写来写去,除了CURD就是CURD,写完DAO层、写Service,写完Service,在创建一个Controller去调一下Service。

        小编我不否定,很多中小型公司都是业务来推动技术,技术就是实现服务公司业务,从而给公司带来收益。不是每家公司都像阿里,华为那些大厂一样,有专门研究技术、写中间价、写开源的团队。

        久而久之,所以很多公司就开始裁员一些年纪偏大的程序员,用成本小、年纪轻的人来代替。

        今天不跑题,我们来聊聊平时在工作中,小编是如何写代码。

        小编从2017步入社会开始写代码,对代码质量的要求,是从2019年开始的,19年的时候又重新开始学习软件设计7大原则、设计模式。 

        那个时候是有想法去提升,但是学习完之后,根本就不能去理解其中的含义,就感觉学了,但又感觉什么没学到。

       再后来就是慢慢开始学习框架源码,看一个开源框架,里面就有很多灵活运用设计模式的例子,这样就会每次加深对某个设计模式的理解和使用方式。

        小编最近在写一个业务网关功能,网关有接入、接出的能力。

        接入一家新的第三方,需要按照第三方要求来对数据进行加解密,之前公司实现有一点不能让我理解是,我需要一步步找到网关最底层,竟然是一个HttpClientUtils的类,然后在里面doPost方法中,在写一个if判断,如果code=新公司的code,就调用新公司加解密的逻辑。

        当然不排除最开始的研发人员,可能要求段时间内完成,所以就这么简单的进行了实现,然后之后的伙伴也是图方便就直接在后面写if判断,就导致的后果,大家都懂得。

        我不知道小伙伴们在设计代码的时候,有没有模块化的概念,这个也是小编之前总监对我说的,在写代码的时候,不要把全部的逻辑都写在一个方法里,要学会拆分,把一件事拆成几件事。一个方法里面上百、上千行的代码,相信大家应该都见过吧,不管是写的人,还是后面接手的人,看这代码,谁心里都不好受哇。

        很多公司对于Http的请求,可能也只是利用一个HttpClient工具类,里面有post、get静态方法,传递url、data、header等参数,完成http请求就完事了。对于没有负责的业务逻辑来说,确实就行了,一旦有一些特殊化处理的,就不太好扩展了。

        小编先是定义了两个接口类,设计功能,底层一定要是抽象的,而绝对不能是具体的代码实现,抽象往往会使系统更加稳定。

/**
 * Http 请求
 *
 * @author IT贱男
 * @version v1.0.0
 * @date 2022/10/08 09:48
 */
public interface HttpClientRequest extends Closeable {

    /**
     * 执行http请求
     *
     * @param uri               请求地址
     * @param httpMethod        请求类型
     * @param requestHttpEntity 包装参数
     * @return
     * @throws Exception
     */
    HttpClientResponse execute(URI uri, String httpMethod, RequestHttpEntity requestHttpEntity) throws Exception;

}

/**
 * HTTP 返回处理
 *
 * @author IT贱男
 * @version v1.0.0
 * @date 2022/9/30 14:00
 */
public interface HttpClientResponse extends Closeable {

    /**
     * 获取请求头
     *
     * @return
     */
    Header getHeaders();

    /**
     * 获取输入流
     *
     * @return
     * @throws IOException
     */
    InputStream getBody() throws IOException;

    /**
     * 设置String 类型的body
     *
     * @param body
     * @throws IOException
     */
    void setBodyString(String body);

    /**
     * 获取String body
     *
     * @param
     * @throws IOException
     */
    String getBodyString();

    /**
     * 获取http请求状态码
     *
     * @return
     * @throws IOException
     */
    int getStatusCode() throws IOException;

    /**
     * 获取http请求状态码 字符串类型
     *
     * @return
     * @throws IOException
     */
    String getStatusText() throws IOException;

    /**
     * 关闭输入流
     */
    @Override
    void close();
}

        HttpClientRequest中定义了一个execute方法,然后交给子类去实现,比如说小编公司固定使用的就是RSA加解密,小编就创建RSAHttpClientRequest的类,去实现对参数进行RSA加解密的操作。DefaultHttpClientRequest就是默认的http请求,CustomHttpClientRequest就是为了满足新的接入方,就自己去创建一个新的HttpClient,去实现自己的业务逻辑。

        RSAClient、CustomClient都是在DefaultClient基础之上做一些自己额外的功能,所以继承了DefaultClient,为了代码复用。        

        Response就不多说了,只是为了响应请求做的一层包装。

        这样我们就把请求、响应的模块定义好了,那怎么使用?

        在上层只需要传入请求地址、请求头、请求参数,然后调用get、post方法就行,是不需要关心具体代码细节实现,所以肯定还会有一层包装类,包装类里面就是我们很熟悉的使用方式了。

@Slf4j
public class HttpClientRestTemplate extends AbstractHttpRestTemplate {

    private final HttpClientRequest requestClient;

    public HttpClientRestTemplate(HttpClientRequest requestClient) {
        this.requestClient = requestClient;
    }

    public <T> HttpRestResult<T> get(String url, Object query) throws Exception {
        return execute(url, HttpMethodConstants.GET, new RequestHttpEntity(this.buildHead(), conversionObj(query)), String.class);
    }

    
    private <T> HttpRestResult<T> execute(String url, String httpMethod, RequestHttpEntity requestEntity,
                                          Type responseType) throws Exception {
        URI uri = HttpUtils.buildUri(url, requestEntity.getQuery());

        // 获取response handler 处理类
        ResponseHandler<T> responseHandler = super.selectResponseHandler(responseType);
        HttpClientResponse response = null;

        try {
            response = this.requestClient().execute(uri, httpMethod, requestEntity);
        } catch (Exception e) {
            log.error("HttpClientRestTemplate Http 请求失败", e);
            throw new BusinessException("HttpClientRestTemplate Http 请求失败", ErrorCodeEnum.SERVICE_EXCEPTION_500);
        }

        try {
            return responseHandler.handle(response);
        } catch (Exception e) {
            log.error("HttpClientRestTemplate handler 处理失败", e);
            throw new BusinessException("HttpClientRestTemplate handler 处理失败", ErrorCodeEnum.SERVICE_EXCEPTION_500);
        } finally {
            if (response != null) {
                response.close();
            }
        }
    }

}

        包装类有了,怎么灵活去使用,应该是A公司就需要使用A公司的HttpClient、B公司就需要使用B公司的HttpClient,那这个时候工厂模式就派上使用了,交给工厂去生产相对应的HttpClient。

/**
 * HTTP 工厂类
 *
 * @author IT贱男
 * @version v1.0.0
 * @date 2022/10/11 10:01
 */
public interface HttpClientFactory {

    /**
     * 创建HttpClient
     *
     * @return
     */
    HttpClientRestTemplate createHttpClientRestTemplate();

}

        小编拿了一个基于Config配置类的工厂为例子,Config配置是可以在Web后台有对应的页面进行配置,根据配置信息创建对应的HttpClient。


/**
 * 基于配置类创建HttpClient工厂
 *
 * @author IT贱男
 * @version v1.0.0
 * @date 2022/10/11 13:19
 */
@Slf4j
public class ConfigHttpClientFactory implements HttpClientFactory {

    // API后台配置信息
    private OpenapiKeyOutConfigDO openapiKeyOutConfigDO;

    public ConfigHttpClientFactory(OpenapiKeyOutConfigDO openapiKeyOutConfigDO) {
        this.openapiKeyOutConfigDO = openapiKeyOutConfigDO;
    }

    @Override
    public HttpClientRestTemplate createHttpClientRestTemplate() {

        // 获取请求客户端
        CloseableHttpClient closeableHttpClient = getCloseableHttpClient();

        // 判断是否加密,不加密走DefaultClient
        if (OpenFlagEnum.CLOSE.getCode().equals(openapiKeyOutConfigDO.getIsEncryption())) {
            // 使用默认的 DefaultHttpClientRequest
            return new HttpClientRestTemplate(new DefaultHttpClientRequest(closeableHttpClient));
        }

        // 判断加密方式是不是RSA
        if (OpenFlagEnum.OPEN.getCode().equals(openapiKeyOutConfigDO.getIsEncryption())
                && EncryptionTypeEnum.RSA.getCode().equals(openapiKeyOutConfigDO.getEncryptionType())) {
            // 使用默认的 RSAHttpClientRequest
            return new HttpClientRestTemplate(new RSAHttpClientRequest(closeableHttpClient, openapiKeyOutConfigDO.getTheirPublicKey(), openapiKeyOutConfigDO.getSelfPrivateKey()));
        }

        // 判断是不是自定义类加密
        if (OpenFlagEnum.OPEN.getCode().equals(openapiKeyOutConfigDO.getIsEncryption())
                && EncryptionTypeEnum.CUSTOM_CLASS.getCode().equals(openapiKeyOutConfigDO.getEncryptionType())) {
            try {
                if (StringUtils.isEmpty(openapiKeyOutConfigDO.getRequestClientImplClassName())) {
                    throw new BusinessException("创建自定义请求类错误,requestClientImplClassName参数为空", ErrorCodeEnum.SERVICE_EXCEPTION_500);
                }
                // 通过反射创建对应的HttpClient,代码省略
                
            } catch (Exception e) {
                log.error(e.getMessage(), e);
            }
        }
        throw new BusinessException("createHttpClientRestTemplate 失败", ErrorCodeEnum.SERVICE_EXCEPTION_500);
    }
   
}

        在其他地方想要使用网关的话,只需要把相对应的配置告诉我,工厂就可以创建出你要想的HttpClient对象出来。

        这样以后接一家新的第三方,程序员只需要创建一个HttpClient,专注自己的业务逻辑,其他细节可以不用去考虑,这样也不需要去改之前原来的代码,符合开闭原则。

        为了扩展,小编设计了,有些第三方请求完成之后,需要做一些额外的特殊处理,最常见的就是字段映射,或者对象类型转换。

/**
 * response 处理结果转换
 *
 * @param <T>
 */
public interface ResponseHandler<T> {

    void setResponseType(Type responseType);

    /**
     * 处理response 转换成对应自己所需要的内容
     *
     * @param response
     * @return
     * @throws Exception
     */
    HttpRestResult<T> handle(HttpClientResponse response) throws Exception;
}

        首先会有一个公共处理的对象,比如说请求失败了,可以在这个类中做统一处理,如果没报错就调用convertResult方法,当然这个方法也是交给子类去实现。

        这个扩展是放在了HttpClientRestTemplate类中execute方法中去了


/**
 * 公用处理对象
 *
 * @author IT贱男
 * @version v1.0.0
 * @date 2022/10/11 10:08
 */
public abstract class AbstractResponseHandler<T> implements ResponseHandler<T> {

    private Type responseType;

    @Override
    public final void setResponseType(Type responseType) {
        this.responseType = responseType;
    }

    @Override
    public final HttpRestResult<T> handle(HttpClientResponse response) throws Exception {
        // 请求失败就进行错误处理
        if (HttpStatus.SC_OK != response.getStatusCode()) {
            return handleError(response);
        }
        // 结果转换
        return convertResult(response, this.responseType);
    }

    private HttpRestResult<T> handleError(HttpClientResponse response) throws Exception {
        Header headers = response.getHeaders();
        String message = IoUtils.toString(response.getBody(), headers.getCharset());
        return new HttpRestResult<T>(headers, response.getStatusCode(), null, message);
    }

    /**
     * 把请求结果进行转换
     *
     * @param response     http client response
     * @param responseType responseType
     * @return HttpRestResult
     * @throws Exception ex
     */
    public abstract HttpRestResult<T> convertResult(HttpClientResponse response, Type responseType) throws Exception;

}
/**
 * 把结果处理成String类型
 *
 * @author IT贱男
 * @version v1.0.0
 * @date 2022/10/11 10:06
 */
public class StringResponseHandler extends AbstractResponseHandler<String> {

    @Override
    public HttpRestResult<String> convertResult(HttpClientResponse response, Type responseType) throws Exception {
        final Header headers = response.getHeaders();
        if (StringUtils.isNotEmpty(response.getBodyString())) {
            String extractBody = ParameterRequestWrapper.jsonStringTrim(response.getBodyString());
            return new HttpRestResult<>(headers, response.getStatusCode(), extractBody, null);
        }

        String extractBody = ParameterRequestWrapper.jsonStringTrim(IoUtils.toString(response.getBody(), headers.getCharset()));
        return new HttpRestResult<>(headers, response.getStatusCode(), extractBody, null);
    }
}

        其实这一套设计下来,代码结构上,小编觉得是很清晰的,在扩展上也是互不影响的,代码维护上也更加直观了,直接定位到类。

        小编平时也是在写这些业务代码,但是在写的时候,小编尽量做到可扩展、代码清爽、维护性高的方向去设计。当然不是吹捧小编写代码有多好,只是想提醒大家,写代码就应该去考虑这些问题,而不是一股脑的吧功能实现好了就行了。

        只有这样慢慢去锻炼自己,写出来的代码质量才会越来越好,写代码也才会越来越有意思。

        文章回到标题,“没有十几年的积累,你还真写不出什么好的代码”

        这句话是小编逛BOSS的时候看到的,所以想来说说,其实吧,在中小型公司,技术往上走,多多少少都会带点管理。

        

        标题中的话,小编也是觉得有道理,喜欢写代码的程序员,自然就会去考虑自己的代码该怎么写才是最好,这样累计起来的经验,代码质量才会越来越好。

        行吧,有感而发,到这里结束了,对于标题中的话,小伙伴们怎么看?

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2204.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

C · 进阶 | 指针的进阶

啊我摔倒了..有没有人扶我起来学习.... &#x1f471;个人主页&#xff1a;《CGod的个人主页》\color{Darkorange}{《CGod的个人主页》}《CGod的个人主页》交个朋友叭~ &#x1f492;个人社区&#xff1a;《编程成神技术交流社区》\color{Darkorange}{《编程成神技术交流社区》…

数电学习(六、时序逻辑电路)(一)

文章目录引言概述特点时序电路的一般结构形式与功能描述方法时序电路分类时序电路的分析方法同步时序电路的分析方法状态转换表状态转换图&#xff08;回顾&#xff09;在现在的场景下看触发器的动态特性&#xff08;四个时间&#xff09;&#xff08;举例&#xff09;分析下面…

计算机毕设(附源码)JAVA-SSM佳音大学志愿填报系统

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

python有哪些编译器

python有哪些编译器 1、Brython把Python转换成Javascript代码。 是一个流行的Python编译器&#xff0c;它可以把Python转换成Javascript代码。该软件支持所有网络浏览器(包括手机网络浏览器)。 它还支持最新的Html5/CSS3标准&#xff0c;可以使用流行的CSS框架&#xff0c;如…

200、无线网桥与无线AP的带机量是多少?一篇文章搞明白

正文&#xff1a; 一个无线ap的带机量是多少&#xff1f;也有朋友提到无线网桥的带机量&#xff1f;这个我们之前有提到过&#xff0c;在了解他们的带机量的话&#xff0c;我们就不得不了解ap的性能指标了&#xff0c;那么本期我们来总结下带机量的问题。 一、选择AP前需要考虑…

用通俗易懂的大白话彻底搞明白mysql的数据类型以及mysql中的int(11),这个11到底是啥?

今天抽时间来讲一下mysql里的知识点&#xff0c;之前有不少人问过我&#xff0c;mysql中的int(11)&#xff0c;这个11到底是啥意思&#xff1f;是11位的意思吗&#xff1f;你是否也想过这个问题&#xff0c;是否也有这个疑问&#xff1f; ok&#xff0c;今天就展开来讲一下&am…

深度分析React源码中的合成事件

热身准备 明确几个概念 在React17.0.3版本中&#xff1a; 所有事件都是委托在id root的DOM元素中&#xff08;网上很多说是在document中&#xff0c;17版本不是了&#xff09;&#xff1b;在应用中所有节点的事件监听其实都是在id root的DOM元素中触发&#xff1b;React自…

【MySQL 第十一天 创建和存储|复合结构的存储|存储过程和函数的区别】

【MySQL 第十一天 创建和存储|复合结构的存储|存储过程和函数的区别】【1】mysql储存过程及语法结构【1.1】mysql过程体【2】mysql创建和使用存储过程【2.1】mysql创建无参的存储过程【2.2】mysql创建有参的输入输出存储过程【3】mysql删除存储过程【4】mysql创建复合结构的存储…

专精特新小巨人的认定条件

奖励&#xff1a;对新认定的专精特新“小巨人”企业&#xff0c;聊城市财政最高一次性奖励50万元。其他地区各有不同。 认定条件 专精特新“小巨人”企业认定需同时满足专、精、特、新、链、品六个方面指标。 (一)专业化指标&#xff1a;坚持专业化发展道路&#xff0c;长期…

大学生体育运动网页设计模板代码 DIV布局校园运动网页作业成品 HTML学校网页制作模板 学生简单体育运动网站设计成品

&#x1f389;精彩专栏推荐 &#x1f4ad;文末获取联系 ✍️ 作者简介: 一个热爱把逻辑思维转变为代码的技术博主 &#x1f482; 作者主页: 【主页——&#x1f680;获取更多优质源码】 &#x1f393; web前端期末大作业&#xff1a; 【&#x1f4da;毕设项目精品实战案例 (10…

C++知识精讲14 | 算法篇之二分查找算法

博主主页&#xff1a;Cool Kid~Yu仙笙_C领域博主&#x1f984; 目录 二分查找定义 二分查找效率 二分查找与遍历的对比 二分查找的限制性 二分查找的限制性&#xff08;总结&#xff09; 二分查找搭建 循环实现二分查找 循环二分查找基本框架&#xff1a; 循环二分查找源码&am…

【苹果家庭iMessage推送】Aupperpushslcertificate或ProductPushsCertificate证书不可以过期

推荐内容IMESSGAE相关 作者推荐内容iMessage苹果推软件 *** 点击即可查看作者要求内容信息作者推荐内容1.家庭推内容 *** 点击即可查看作者要求内容信息作者推荐内容2.相册推 *** 点击即可查看作者要求内容信息作者推荐内容3.日历推 *** 点击即可查看作者要求内容信息作者推荐…

前端实现给文字添加动态背景

&#x1f4cb; 个人简介 &#x1f496; 作者简介&#xff1a;大家好&#xff0c;我是阿牛&#xff0c;全栈领域优质创作者。&#x1f61c;&#x1f4dd; 个人主页&#xff1a;馆主阿牛&#x1f525;&#x1f389; 支持我&#xff1a;点赞&#x1f44d;收藏⭐️留言&#x1f4d…

【JAVA开发】提高开发效率的工具分享

代码管理工具 仓库&#xff1a; GitHub or GitLab or 本地仓库 版本控制&#xff1a; git or svn 推荐gitLabgit 多分支敏捷开发 开发工具 IDEA 最方便开发工具了 当然如何你是全栈也可以考虑使用VS(visual studio)、HBuider、AS(android studio) 文本工具 Sublime text …

Redis数据结构之整数集合

目录 基本数据结构 例子 升级 升级之后新元素的摆放位置 好处 降级 整数集合可以理解为一个有序&#xff08;升序&#xff09;的不允许元素重复的数组。 基本数据结构 intset会根据 编码格式分配空间。 例子 升级 当新添加的元素超过了当前编码格式所能 表示的范围&…

Linux常用命令工具

1、查找特定文本中的特定字符 cat filename | grep myStr eg: cat .config | grep KCOV 2、查找特定文本中的特定字符并打印具体行数 cat filename | grep -n myStr eg:: cat .config | grep -n KCOV 3、查找一个文件夹中的特定字符 grep -r myStr filedir eg: grep -r __NR_…

Mysql注入

&#x1f4aa;&#x1f4aa;mysql注入前言1.mysql之union注入1.1.判断是否有注入&#xff1a;1.2.信息收集1.3.进行注入1.4.完整注入实列2. mysql 跨库注入&#xff1a;2.1第一步就是找到数据库2.2第二步就是注入3.自己写union注入4.其他注入操作前言 &#x1f48e;&#x1f48…

JVM笔记:垃圾回收及垃圾回收算法

垃圾是指在运行程序中没有任何指针指向的对象&#xff0c;这个对象就是需要被回收的垃圾。如果不及时对内存中的垃圾进行清理&#xff0c;那么&#xff0c;这些垃圾对象所占的内存空间会一直保留到应用程序结束&#xff0c;被保留的空间无法被其他对象使用。甚至可能导致内存溢…

【Vue项目回顾】网络模块的封装

选择什么网络模块 选择一: 传统的Ajax是基于XMLHttpRequest(XHR) 为什么不用它呢? 非常好解释, 配置和调用方式等非常混乱.编码起来看起来就非常蛋疼.所以真实开发中很少直接使用, 而是使用jQuery-Ajax 选择二: 在前面的学习中, 我们经常会使用jQuery-Ajax 相对于传统的A…

【PyTorch深度学习项目实战100例】—— 基于Pytorch的语音情感识别系统 | 第71例

前言 大家好,我是阿光。 本专栏整理了《PyTorch深度学习项目实战100例》,内包含了各种不同的深度学习项目,包含项目原理以及源码,每一个项目实例都附带有完整的代码+数据集。 正在更新中~ ✨ 🚨 我的项目环境: 平台:Windows10语言环境:python3.7编译器:PyCharmPy…