首页 > 试题

重零开始

更新时间:2023-02-03 04:17:58 阅读: 评论:0

初二数学函数图像-中流砥柱的意思


2023年2月3日发(作者:完结小说排行)

从零开始学重构——重构的流程及基础重构⼿法

重构的流程

重构⼿法

正如上⼀次所讲的那样,重构有两个基本条件,⼀是要保持代码在重构前后的⾏为基本不变,⼆是整个过程是受控且尽可能少地产⽣错

误。尤其是对于第⼆点,产⽣了⼀系列的重构⼿法,每种重构⼿法都是⼀系列简单⽽机械的操作步骤,通过遵循这⼀系列的操作来实现代码

的结构性调整。因此,重构的整个过程就是不断运⽤不同重构⼿法的过程,是⼀个相对有章可循的流程。

重构⼿法有⼤有⼩,⼤的重构⼿法⼀般由若⼲⼩的基础重构组成,进⽽聚沙成塔实现对代码结构⼤幅度的调整。完整的重构列表请参见

《重构,改善既有代码的设计》⼀书。

例如,replaceconditionalwithpolymorphism这项复杂重构⼿法,就⾄少需要使⽤lfencapsulate,extractmethod,move

method,pulldownmethod这四种基础重构⼿法。因此在学习类级别的复杂重构⼿法前,需要先掌握⾏级别和⽅法级别的基础重构⼿法。

重构步骤

重构的宏观步骤⼀般有如下两种:⾃上⽽下式和⾃下⽽上式。

⾃上⽽下的重构在重构前,⼼中已经⼤致知道重构后的代码将会是什么形态,然后⾄上⽽下地将步骤分解出来,并使⽤相应的重构步骤

⼀⼀实现,最终达到重构后的形态。其流程为:

1.识别代码中的坏味道

2.运⽤设计原则,构思出修改后的⽬标状态

3.将⽬标状态分解为⼀或多项重构步骤

4.运⽤重构步骤

⾃下⽽上的重构则对重构后的代码没有⼀个完整⽽清晰的认识。⼀般⽽⾔,每种重构⼿法都有助于我们解决某种类型的代码坏味,⽽⾃

下⽽上的重构则针对每个发现的代码坏味直接运⽤对应的重构⼿法,直到没有明显的坏味,此时的代码即能⾃动满⾜某种设计模式。是⼀种

迭代的思路,也是所谓重构到模式的思路。其流程为:

1.识别代码中的坏味道

2.运⽤⼀项或多项重构步骤,消除坏味

3.重复1-2,直到没有明显坏味

在⼀般的情况下,这两种重构流程并不是互斥的,经常交错进⾏或互相包含。如先运⽤⾃上⽽下的⽅法识别出代码中的坏味,然后根据

设计原则重构到某个实现,再运⽤⾃下⽽上的⽅法重新寻找新的坏味,迭代重构。

基础重构⼿法

由于基础重构⼿法⽐较多,⽽且相对⽐较简单。因此先列出常⽤的基础重构⼿法和简单介绍,并在最后的实践案例中结合基础重构⼿法

来重构代码。

rename(重命名变量/⽅法/类)

坏味:含义不清的命名

说明:变量名应当体现出变量的作⽤和含义、⽅法名应当表现出⽅法的效果、类名也应提⽰类的职责和在继承体系中的位置。

操作⽅法:IntelliJShift+F6

reorder(调整语句顺序)

坏味:变量的申请和使⽤分离太远

说明:变量的使⽤应当尽可能离使⽤近⼀些,否则会扩⼤变量的作⽤域,在重构时也会产⽣困难。

操作⽅法:IntelliJAlt+Shift+↑↓针对⽆副作⽤的语句,直接调整语句位置。

splitfor/block(拆分for循环/代码块)

坏味:⼀个循环或代码块中同时操作了多个变量或执⾏了多个职责

说明:⼀个循环中若有太多变量要计算,不利于将此循环提取为单独⽅法。

操作⽅法:

1.将循环复制⼀次

2.每个循环中只保留⼀个变量的计算

3.将循环提取为独⽴⽅法

4.将所有循环的出现替换为⽅法的调⽤

guardclaus(卫语句)

坏味:过深的条件嵌套

说明:先判断跳出/过滤的条件,并直接return或continue,可除去多余的el嵌套深度。

操作⽅法:

iJ在if语句上Alt+Enter,选择invertif,可倒转if和el语句

iJ在el语句上Alt+Enter,选择removeredundantel

extractvariable(提取变量)

坏味:单条语句过长,含义不清

说明:将部分语句提取出变量,并为变量起⼀个能够解释变量含义的名称来替代注释

操作说明:IntelliJCtrl+Alt+V

extractmethod(提取⽅法)

坏味:单个⽅法过长,含义不清

说明:将做同⼀件事的代码提取出⽅法(⼀般为计算某个变量,或进⾏单个复杂操作),并为⽅法起⼀个能够解释”这件事”的名称来

替代注释

操作说明:IntelliJCtrl+Alt+M,需要考虑返回和参数的列表,返回不能超过1个变量

inlinemethod(内联⽅法)

坏味:⽅法只有⼀⾏代码,且内容本⾝已经很明确(多⾏也可以,但若原⽅法有返回值,则会⽐较复杂,不推荐)

说明:⽅法的作⽤是聚合操作并提供注释信息,若⽅法内容已经明确,则⽅法本⾝就起不到作⽤,反⽽增加复杂度

操作说明:将⽅法内容复制后,替换⽅法调⽤的部分。再删除⽅法本⾝

addparameter(⽅法增加参数)

坏味:⽅法主体只有部分变量不同

说明:可以提取变化的部分成为参数,从⽽合并两个相似的⽅法

操作说明:

1.将变量的部分提取为变量,并提到⽅法的最开始处

2.将⽅法剩余的内容提取为⼀个新的⽅法,新⽅法会含有新的参数

3.将原来的⽼⽅法内联

案例实践

重构前

代码中的坏味有:1.过长的⽅法,超过了20⾏或⼀屏,2.变量的命名含义不清,读者⽆法理解channelColumnClauTemp,

channelColumnClau以及它们之间的关系,-el中存在重复代码。

下来我们就来使⽤⼀系列基础重构⼿法来整理这段代码。

调整变量申明位置

仔细观察,发现channelColumnClauTemp变量只在if语句中使⽤,因此将channelColumnClauTemp变量的申请放到if中去:

privateSetchannelColumns;

publicStringgenerateSql(){

StringchannelColumnClauTemp=(channelColumns,",","","");

StringchannelColumnClau;

Setcolumns=newTreeSet<>();

for(Stringstr:channelColumns){

if(_BUS_EML_ng().equals(str)){

(_EML_ng());

}el{

(str);

}

}

channelColumnClau=(columns,",","","");

StringchannelColumnsReviewTemp="";

StringchannelColumnsReview="";

if(!y()){

channelColumnsReviewTemp=channelColumnClauTemp+

(ns(idTypeColumn)?"":(","+idTypeColumn))+",batch_id";

channelColumnsReview=channelColumnClau+

(ns(idTypeColumn)?"":(","+idTypeColumn))+",batch_id";

}el{

channelColumnsReviewTemp=idTypeColumn+",batch_id";

channelColumnsReview=idTypeColumn+",batch_id";

}

StringBuffervsql=newStringBuffer();

("inrtinto").append(_SCHEMA).append(".").append(tableName)

.append("(").append(channelColumnsReview).append(")")

.append("lectdistinct").append(e(ME_COLUMN,"isnull("+ME_COLUM

.append("from").append(_SCHEMA).append(".").append(sourceTableName).append(";n");

returnreviewTempTableSql+ng();

}

同样,channelColumnClau也只在if块中使⽤,但其中还涉及了columns及for循环部分,也⼀并移动到if块中:

重命名变量

仔细观察channelColumnsReviewTemp和channelColumnsReview两个变量的使⽤场景,发现它们是所拼接的sql语句的lect

xxx和inrt(yyy)这两个部分,因此实际上是源表的列名和⽬标表的列名。从⽽将channelColumnsReviewTemp命名为

sourceColumnsStr,将channelColumnsReview命名为targetColumnsStr。

同样,观察channelColumnClauTemp和channelColumnClau,它们分别⽤于计算channelColumnsReviewTemp和

channelColumnsReview,因此对应的命名为sourceColumnsWithoutBatchId和targetColumnsWithoutBatchId:

if(!y()){

StringchannelColumnClauTemp=(channelColumns,",","","");

channelColumnsReviewTemp=channelColumnClauTemp+

(ns(idTypeColumn)?"":(","+idTypeColumn))+",batch_id";

channelColumnsReview=channelColumnClau+

(ns(idTypeColumn)?"":(","+idTypeColumn))+",batch_id";

}

if(!y()){

StringchannelColumnClauTemp=(channelColumns,",","","");

channelColumnsReviewTemp=channelColumnClauTemp+

(ns(idTypeColumn)?"":(","+idTypeColumn))+",batch_id";

StringchannelColumnClau;

Setcolumns=newTreeSet<>();

for(Stringstr:channelColumns){

if(_BUS_EML_ng().equals(str)){

(_EML_ng());

}el{

(str);

}

}

channelColumnClau=(columns,",","","");

channelColumnsReview=channelColumnClau+

(ns(idTypeColumn)?"":(","+idTypeColumn))+",batch_id";

}

StringsourceColumnsStr="";

StringtargetColumnsStr="";

if(!y()){

StringsourceColumnsWithoutBatchId=(channelColumns,",","","");

sourceColumnsStr=sourceColumnsWithoutBatchId+

(ns(idTypeColumn)?"":(","+idTypeColumn))+",batch_id";

StringtargetColumnsWithoutBatchId;

Setcolumns=newTreeSet<>();

for(Stringstr:channelColumns){

if(_BUS_EML_ng().equals(str)){

(_EML_ng());

}el{

(str);

}

}

targetColumnsWithoutBatchId=(columns,",","","");

targetColumnsStr=targetColumnsWithoutBatchId+

(ns(idTypeColumn)?"":(","+idTypeColumn))+",batch_id";

}el{

sourceColumnsStr=idTypeColumn+",batch_id";

targetColumnsStr=idTypeColumn+",batch_id";

}

拆分代码块

再观察,发现if-el代码块中同时操作了sourceColumnsStr和targetColumnsStr两个变量,不利于后续运⽤提取⽅法的重构⼿法。

因此需要运⽤splitblock⼿法,将这两个变量的计算拆分到两个代码块中。先完整拷贝⼀份if-el代码,并在第⼀份中保留对

sourceColumnsStr的计算,在第⼆份中保留对targetColumnsStr的计算,并且调整⼀下这两个变量申明的位置,到if-el计算逻辑的前

⾯:

提取⽅法

⾄此,可以提取两个⽅法:computeSourceColumnsStr()以及computeTargetColumnsStr():

StringsourceColumnsStr="";

if(!y()){

StringsourceColumnsWithoutBatchId=(channelColumns,",","","");

sourceColumnsStr=sourceColumnsWithoutBatchId+

(ns(idTypeColumn)?"":(","+idTypeColumn))+",batch_id";

}el{

sourceColumnsStr=idTypeColumn+",batch_id";

}

StringtargetColumnsStr="";

if(!y()){

StringtargetColumnsWithoutBatchId;

Setcolumns=newTreeSet<>();

for(Stringstr:channelColumns){

if(_BUS_EML_ng().equals(str)){

(_EML_ng());

}el{

(str);

}

}

targetColumnsWithoutBatchId=(columns,",","","");

targetColumnsStr=targetColumnsWithoutBatchId+

(ns(idTypeColumn)?"":(","+idTypeColumn))+",batch_id";

}el{

targetColumnsStr=idTypeColumn+",batch_id";

}

观察⼀下提取出来的两个⽅法,发现他们的不同之处在于computeTargetColumnsStr()中多了⼀个对columns集合的计算逻辑,于

是将columns的计算逻辑再封装⼀下:

privateStringcomputeTargetColumnsStr(){

StringtargetColumnsStr="";

if(!y()){

StringtargetColumnsWithoutBatchId;

Setcolumns=newTreeSet<>();

for(Stringstr:channelColumns){

if(_BUS_EML_ng().equals(str)){

(_EML_ng());

}el{

(str);

}

}

targetColumnsWithoutBatchId=(columns,",","","");

targetColumnsStr=targetColumnsWithoutBatchId+

(ns(idTypeColumn)?"":(","+idTypeColumn))+",batch_id";

}el{

targetColumnsStr=idTypeColumn+",batch_id";

}

returntargetColumnsStr;

}

privateStringcomputeSourceColumnsStr(){

StringsourceColumnsStr="";

if(!y()){

StringsourceColumnsWithoutBatchId=(channelColumns,",","","");

sourceColumnsStr=sourceColumnsWithoutBatchId+

(ns(idTypeColumn)?"":(","+idTypeColumn))+",batch_id";

}el{

sourceColumnsStr=idTypeColumn+",batch_id";

}

returnsourceColumnsStr;

}

privateStringcomputeTargetColumnsStr(){

StringtargetColumnsStr="";

if(!y()){

Setcolumns=getTargetColumns();

StringtargetColumnsWithoutBatchId;

targetColumnsWithoutBatchId=(columns,",","","");

targetColumnsStr=targetColumnsWithoutBatchId+

(ns(idTypeColumn)?"":(","+idTypeColumn))+",batch_id";

}el{

targetColumnsStr=idTypeColumn+",batch_id";

}

returntargetColumnsStr;

}

privateSetgetTargetColumns(){

Setcolumns=newTreeSet<>();

for(Stringstr:channelColumns){

if(_BUS_EML_ng().equals(str)){

(_EML_ng());

}el{

(str);

}

}

returncolumns;

}

经过对⽐,还有

这两⾏与StringsourceColumnsWithoutBatchId=(channelColumns,",","","");存在不⼀致。但可以使⽤变量的内联重构成⼀样的

形式。

经过整理后代码的形式如下:

StringtargetColumnsWithoutBatchId;

targetColumnsWithoutBatchId=(columns,",","","");

publicStringgenerateSql(){

StringsourceColumnsStr=computeSourceColumnsStr();

StringtargetColumnsStr=computeTargetColumnsStr();

StringBuffervsql=newStringBuffer();

("inrtinto").append(_SCHEMA).append(".").append(tableName)

.append("(").append(targetColumnsStr).append(")")

.append("lectdistinct").append(e(ME_COLUMN,"isnull("+ME_COLUMN+

.append("from").append(_SCHEMA).append(".").append(sourceTableName).append(";n");

returnreviewTempTableSql+ng();

}

privateStringcomputeTargetColumnsStr(){

StringtargetColumnsStr="";

if(!y()){

Setcolumns=getTargetColumns();

StringtargetColumnsWithoutBatchId=(columns,",","","");

targetColumnsStr=targetColumnsWithoutBatchId+

(ns(idTypeColumn)?"":(","+idTypeColumn))+",batch_id";

}el{

targetColumnsStr=idTypeColumn+",batch_id";

}

returntargetColumnsStr;

}

privateStringcomputeSourceColumnsStr(){

StringsourceColumnsStr="";

if(!y()){

StringsourceColumnsWithoutBatchId=(channelColumns,",","","");

sourceColumnsStr=sourceColumnsWithoutBatchId+

(ns(idTypeColumn)?"":(","+idTypeColumn))+",batch_id";

}el{

sourceColumnsStr=idTypeColumn+",batch_id";

}

returnsourceColumnsStr;

}

privateSetgetTargetColumns(){

Setcolumns=newTreeSet<>();

for(Stringstr:channelColumns){

if(_BUS_EML_ng().equals(str)){

(_EML_ng());

}el{

(str);

}

}

returncolumns;

}

提炼参数

观察两个⽅法,发现只有columns和channleColumns不同,其它均相同。因此彩提炼参数的⽅法,为其增加参数,从⽽合并为⼀个

⽅法。做法是先将channleColumns再重新提取⼀个名为columns的变量,并提到⽅法最开始处。再把⽅法中的局部变量重命名⼀下,就

变成了:

同样,将computeTargetColumnsStr()⽅法也处理⼀下:

再将两个⽅法的剩下内容提取为⼀个新的⽅法,新⽅法含有columns作为参数:

内联⽅法,并整理

privateStringcomputeSourceColumnsStr(){

Setcolumns=lColumns;

StringcolumnsStr="";

if(!y()){

StringcolumnsStrWithoutBatchId=(columns,",","","");

columnsStr=columnsStrWithoutBatchId+

(ns(idTypeColumn)?"":(","+idTypeColumn))+",batch_id";

}el{

columnsStr=idTypeColumn+",batch_id";

}

returncolumnsStr;

}

privateStringcomputeTargetColumnsStr(){

Setcolumns=getTargetColumns();

StringcolumnsStr="";

if(!y()){

StringcolumnsStrWithoutBatchId=(columns,",","","");

columnsStr=columnsStrWithoutBatchId+

(ns(idTypeColumn)?"":(","+idTypeColumn))+",batch_id";

}el{

columnsStr=idTypeColumn+",batch_id";

}

returncolumnsStr;

}

privateStringcomputeTargetColumnsStr(){

Setcolumns=getTargetColumns();

returntransformToString(columns);

}

privateStringcomputeSourceColumnsStr(){

Setcolumns=lColumns;

returntransformToString(columns);

}

privateStringtransformToString(Setcolumns){

StringcolumnsStr="";

if(!y()){

StringcolumnsStrWithoutBatchId=(columns,",","","");

columnsStr=columnsStrWithoutBatchId+

(ns(idTypeColumn)?"":(","+idTypeColumn))+",batch_id";

}el{

columnsStr=idTypeColumn+",batch_id";

}

returncolumnsStr;

}

原先提取出来的两个⽅法就只剩下2⾏内容了,其中⼀⾏中变量的申明,可以将变量内联:

再将这两个⽅法内联,最终形成如下的形式:

再⼀次提炼⽅法

⾄此,整个代码中没有重复代码,每个⽅法长度得到控制,命名也⽐较恰当,重构可以⾄此结束。但对于⾼要求的风格⽽⾔,应该要求

⽅法中的每个⼦⽅法都在同⼀抽象粒度上。然⽽transformToString()⽅法与后续的sql拼接并不在⼀个抽象粒度上,因此可以将sql拼接再

提取到⼀个新⽅法中,从⽽增加可读性。

做法是先将ng()提取为变量:

privateStringcomputeTargetColumnsStr(){

returntransformToString(getTargetColumns());

}

privateStringcomputeSourceColumnsStr(){

returntransformToString(lColumns);

}

publicStringgenerateSql(){

StringsourceColumnsStr=transformToString(lColumns);

StringtargetColumnsStr=transformToString(getTargetColumns());

StringBuffervsql=newStringBuffer();

("inrtinto").append(_SCHEMA).append(".").append(tableName)

.append("(").append(targetColumnsStr).append(")")

.append("lectdistinct").append(e(ME_COLUMN,"isnull("+ME_COLUMN+

.append("from").append(_SCHEMA).append(".").append(sourceTableName).append(";n");

returnreviewTempTableSql+ng();

}

privateStringtransformToString(Setcolumns){

StringcolumnsStr="";

if(!y()){

StringcolumnsStrWithoutBatchId=(columns,",","","");

columnsStr=columnsStrWithoutBatchId+

(ns(idTypeColumn)?"":(","+idTypeColumn))+",batch_id";

}el{

columnsStr=idTypeColumn+",batch_id";

}

returncolumnsStr;

}

privateSetgetTargetColumns(){

Setcolumns=newTreeSet<>();

for(Stringstr:channelColumns){

if(_BUS_EML_ng().equals(str)){

(_EML_ng());

}el{

(str);

}

}

returncolumns;

}

再将return之前的部分提取到新⽅法generateInrtSql()中:

总结

重构过程到此结束。

整个重构过程中,使⽤了reorder,rename,extractvariable,extractmethod,inlinemethod,splitfor/codeblock,add

parameter等⼿法。观察⼀下每个步骤都是可控的,如果重构在每个步骤后停⽌,代码依然可以运⾏。更重要的是,每个步骤都能被证明保

持了原有代码的⾏为。这也是重构最重要的两个条件。

重构的案例代码:

StringBuffervsql=newStringBuffer();

("inrtinto").append(_SCHEMA).append(".").append(tableName)

.append("(").append(targetColumnsStr).append(")")

.append("lectdistinct").append(e(ME_COLUMN,"isnull("+ME_COLUMN+",'未知城市

.append("from").append(_SCHEMA).append(".").append(sourceTableName).append(";n");

StringinrtSql=ng();

returnreviewTempTableSql+inrtSql;

publicStringgenerateSql(){

StringsourceColumnsStr=transformToString(lColumns);

StringtargetColumnsStr=transformToString(getTargetColumns());

StringinrtSql=generateInrtSql(sourceColumnsStr,targetColumnsStr);

returnreviewTempTableSql+inrtSql;

}

本文发布于:2023-02-03 04:17:58,感谢您对本站的认可!

本文链接:https://www.wtabcd.cn/fanwen/fan/88/181222.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

上一篇:丰宁坝上草原
下一篇:嵇康打铁
标签:重零开始
相关文章
留言与评论(共有 0 条评论)
   
验证码:
推荐文章
排行榜
Copyright ©2019-2022 Comsenz Inc.Powered by © 专利检索| 网站地图