没有十几年的积累,你还真写不出什么好的代码
创始人
2024-01-18 11:11:58
0

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

        拿一个正常的程序员举个例子,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;/*** 关闭输入流*/@Overridevoid 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  HttpRestResult get(String url, Object query) throws Exception {return execute(url, HttpMethodConstants.GET, new RequestHttpEntity(this.buildHead(), conversionObj(query)), String.class);}private  HttpRestResult execute(String url, String httpMethod, RequestHttpEntity requestEntity,Type responseType) throws Exception {URI uri = HttpUtils.buildUri(url, requestEntity.getQuery());// 获取response handler 处理类ResponseHandler 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;}@Overridepublic HttpClientRestTemplate createHttpClientRestTemplate() {// 获取请求客户端CloseableHttpClient closeableHttpClient = getCloseableHttpClient();// 判断是否加密,不加密走DefaultClientif (OpenFlagEnum.CLOSE.getCode().equals(openapiKeyOutConfigDO.getIsEncryption())) {// 使用默认的 DefaultHttpClientRequestreturn new HttpClientRestTemplate(new DefaultHttpClientRequest(closeableHttpClient));}// 判断加密方式是不是RSAif (OpenFlagEnum.OPEN.getCode().equals(openapiKeyOutConfigDO.getIsEncryption())&& EncryptionTypeEnum.RSA.getCode().equals(openapiKeyOutConfigDO.getEncryptionType())) {// 使用默认的 RSAHttpClientRequestreturn 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 */
public interface ResponseHandler {void setResponseType(Type responseType);/*** 处理response 转换成对应自己所需要的内容** @param response* @return* @throws Exception*/HttpRestResult handle(HttpClientResponse response) throws Exception;
}

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

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


/*** 公用处理对象** @author IT贱男* @version v1.0.0* @date 2022/10/11 10:08*/
public abstract class AbstractResponseHandler implements ResponseHandler {private Type responseType;@Overridepublic final void setResponseType(Type responseType) {this.responseType = responseType;}@Overridepublic final HttpRestResult handle(HttpClientResponse response) throws Exception {// 请求失败就进行错误处理if (HttpStatus.SC_OK != response.getStatusCode()) {return handleError(response);}// 结果转换return convertResult(response, this.responseType);}private HttpRestResult handleError(HttpClientResponse response) throws Exception {Header headers = response.getHeaders();String message = IoUtils.toString(response.getBody(), headers.getCharset());return new HttpRestResult(headers, response.getStatusCode(), null, message);}/*** 把请求结果进行转换** @param response     http client response* @param responseType responseType* @return HttpRestResult* @throws Exception ex*/public abstract HttpRestResult 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 {@Overridepublic HttpRestResult 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的时候看到的,所以想来说说,其实吧,在中小型公司,技术往上走,多多少少都会带点管理。

        

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

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

 

相关内容

热门资讯

开学日记500字 开学日记500字  开学,汉语解释有开设学校、启作学者、学期开始三个释义。以下是小编收集的开学日记相...
朝花夕拾读书笔记摘抄好词好句 朝花夕拾读书笔记摘抄好词好句  一、朝花夕拾的主要内容  《朝花夕拾》里作者鲁迅用夹叙夹议的方法,以...
net.coobird.thu... 1.简单介绍 net.coobird.thumbnailator.Thumbnails 是一个用于创...
《红楼梦》读书笔记 《红楼梦》读书笔记600字《红楼梦》读书笔记600字娴静似娇花照水,行动如弱柳扶风,心较比干多一窍,...
增广贤文读书笔记 增广贤文读书笔记(精选30篇)  读完一本名著以后,想必你有不少可以分享的东西,何不写一篇读书笔记记...
[解决]Splunk KV S... 1: 背景: 今天客户反映数据搜索的时候,不是很稳定,想确定一下 ,这个是什么原因造成的,我去系统里...
爱的教育作文 爱的教育作文爱的教育看到这个书名,我不禁开始思考一个问题:在这个缤纷多彩的世界里,爱究竟是什么含义?...
梁衡《把栏杆拍遍》读书笔记 梁衡《把栏杆拍遍》读书笔记梁衡《把栏杆拍遍》读书笔记这是一篇写得很美的散文,有以下特点:一、联想丰富...
【每日一题Day150】LC1... 分割两个字符串得到回文串【LC1616】 给你两个字符串 a 和 b ,它们长度相同...
一年级春游日记 一年级春游日记一年级春游日记1  今天是春游,我作天就去买许多零食和矿泉水,打算在春游的时候干掉,我...
课外读书笔记摘抄 课外读书笔记摘抄(精选12篇)  导语:舍弃就是这样,它也许出于无奈,可在无奈之后是另一份希望,它也...
蚂蚁观察日记 【热门】蚂蚁观察日记4篇蚂蚁观察日记 篇1  我家有一个后院,我经常到后院去观察那些鹭绿上得小精灵—...
ImageView(图像视图) 本节介绍的UI基础控件是:ImageView(图像视图),就是用来显示图像的一个View或者说控件!...
关于接口测试——自动化框架的设... 一、自动化测试框架 在大部分测试人员眼中只要沾上“框架”,就感觉非常神秘,...
【2023.3.8】数据结构复... 【2023.3.8】数据结构复习笔记 文章目录【2023.3.8】数据结构复习笔记序言一、绪论二、线...
数学周记 【精华】数学周记范文(通用20篇)  不经意间,一个星期已经结束了,想必有很多难忘的瞬间吧,是时候仔...
男生贾里全传读书笔记 男生贾里全传读书笔记(通用24篇)  当品读完一部作品后,相信大家一定领会了不少东西,不能光会读哦,...
1.计算机网络和因特网 1.因特网的描述1.1 具体构成描述根据底层实现(硬件软件)端系统(主机...
使用YOLO部署哨岗相机 流程 一.模型选取 将YOLO和Faster RCNN进行搭配,通过多次实验ÿ...
大蒜观察日记 大蒜观察日记3篇  【导语】大蒜虽然是一种很不起眼的植物,但是它的生命力很强,无论在任何的环境下都可...