重构的艺术
重构的艺术🤪
强烈建议大家去看一下:
《重构:改善代码的既有设计》这本书,超级好看,计算机圣经。🥳前半部分讲解为什么要进行重构、什么时候进行重构以及重构的好处,超级有意思;后半部分就是纯纯工具书,不知道咋重构的时候翻一翻就行💩
里面有句话我现在都记得:傻瓜都能写出计算机能看懂的代码,而能写出人能够看懂的代码的才叫优秀的程序员
一、为什么需要重构🤡
其实在计算机早期发展阶段是没有重构的概念的,因为那个时候程序体量很小,所以推崇在开发之前就规划好一整个程序。
但是随着程序体量越来越大,即使是最优秀的大牛也不能在开发之前就把所有情况都考虑进去。👾
所以只能一次又一次地给原来的程序贴补丁,然而这种面对bug贴补丁的治标不治本的方式只会使补丁越来越多,程序越来越臃肿,越来越难以维护🫨
终于,到了一点都写不下去的时候,重构的概念就诞生了,并且迅速获得了大家的青睐,并被认为是一种可以极大提高开发效率的方式。
在大家的
miniproject中肯定也会遇到相似的问题:产品要求越来越多😵,一些原来根本没考虑的用法的产生,会导致代码逻辑越来越混乱,越写越自闭(去年我就在是这样的🤡🤡🤡)。这个时候,别害怕,捋清思路,果断重构!!!
但是,重构的意义不止于此:随着技术的迅速发展,老一代技术渐渐被取代,这个时候,用新技术重构老项目就显得格外重要
就比如用react重构之前jQuery的项目🤪
因此,小小总结一下,重构就是因为:
- 之前写的烂,改不了一点或者改的很耗时很费精力
- 项目要与时俱进,换上新技术,提高效能
二、要怎么重构🤕
《重构》的作者称这个为发现代码里的坏味道💩
首先,肯定是要捋清
逻辑混乱产生的原因,分析过后,把逻辑重写,重新组织函数、组件的关系
- 一些逻辑混乱来源于不合理的组件拆分,不要想着让一个组件适应所有的情况,这样只会让这一个组件越来越难懂复杂 - 一个组件只干一件事情😃😃😃 - 就比如 - button组件,一开始的功能就是按下有反应。但是现在发现又需要一个可以有弹窗的- button组件。- 这时候一般的想法就是做个判断,给原来的组件上加一个 - hasModal,有这个属性就有弹窗,也不是不行嗷🤣。- 确实是这样,稍微加一点 - prop会让我们的组件功能更强大,但是过多的- props就挺让人头大了- 就比如再给这个 - button加上跳转页面、下载文件、上传文件等等的功能,这个时候浅浅看一下有多少个- props呢?- hasModal,- navigatePath,- download,- upload- 这个时候我就已经相当烦了,但是情况可能更超乎意料,可能还有 - button在页面上存在3秒之后页面就要开始抖动这种逆天的功能呢。你永远想象不到你的老板有什么鬼马点子😏- 所以,这个时候完全可以把这些个 - button拆开,正常的- button用一个组件,有特殊功能的,就单独再写一个,避免别人要这个组件的时候点开看到1000行的- props提示发呆。👾
- 一些逻辑问题出现在组件之间 - 耦合度太高,改动一个小部分要修改大半个页面的代码,这种是真的痛苦🤗- 还是拿上边的 - button做例子,- hasModal,- navigatePath,- download,- upload这些功能,完全可以改成一个- onClick回调函数😏,之后要用的时候把要执行的任务作为函数传进来,这样- button就只用单独处理自身- onClick的逻辑就行啦,复用性自然也就提高了- 一个好的组件写完之后用800年也不需要改一次代码 
- 还有一些出在乱七八糟的逻辑条件判断上 - if嵌800行,图灵来了都看不懂,再来点循环,上帝都看不懂🦢- 好好 - review自己写的💩💩💩,把类似什么- 1 - const True = flag ? 1 : 0- 这种脱裤子放屁的东西都化简了(有时候 - ide都看不下去会给你标黄🤣);再把- if这种语句尽量改成- 三目运算符;把- switch这种语句换成- 多态类;- 提高点可读性,对你和你的 - partner都好🤯🤯🤯
其次就是重新组织文件结构
- 文件结构应该层次分明,逻辑清晰,以下是 - husky🐶推荐的目录结构- 1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27- ├── .husky # husky的配置文件
 ├── dist # 打包后的代码和资源文件
 ├── node_modules # 依赖库和模块
 ├── src
 │ ├── assets # 静态资源
 │ ├── components # 公共组件
 │ ├── pages # 页面
 │ ├── type # 全局类型声明
 │ ├── utils # 工具函数
 │ ├── App.tsx
 │ ├── fetch.ts # 网络请求
 │ ├── main.tsx
 │ ├── router.tsx # 路由配置信息
 │ └── vite-env.d.ts
 ├── .commitlintrc.cjs # commitlint的配置文件
 ├── .eslintignore
 ├── .eslintrc.cjs # ESLint的配置文件
 ├── .gitignore
 ├── .prettierignore
 ├── .prettierrc.cjs # Prettier的配置文件
 ├── index.html
 ├── package.json
 ├── README.md
 ├── tsconfig.json
 ├── tsconfig.node.json
 ├── vite.config.ts
 └── yarn.lock- 其中,每个 - page和- component也都可以有自己独有的- component,最外层的- component只放不同页面不同组件均要用到的组件
然后就是一个被很多人忽略的点:换个好的名字
- 有时候,换一个人类能看懂的名字真的能帮上大忙😐。 - 不要怕名字太长什么的,又不是你自己写, - ide都没抱怨你急什么?- 就比如说(真实案例哈😕): - 你看到一个组件,叫 - Ritem,你能看出来它是干啥的吗?- 但是,如果改个名字,叫 - RescueInfoItem,是不是就清晰多了- 顺带提一句,前几天我起出了这辈子来最长的名字: - RescueInfoSortedByTargetIDSlice😶🌫️
四、重构的步骤
光说说可能没什么感觉,接下来,我将会使用我最近重构的一段代码(js->ts)来给大家展示重构的魅力👽,用例子的方式给大家讲解一下重构的步骤(这段代码的来源保密🤪)
msgItem.jsx (这是我们要重构的组件)
| 1 |  | 
这是一段超级长的代码,有110行,一般我们组件超过70行就已经不是很优雅了(反正我看到超过70行的组件就头大🥺),因此,重构就应该提上日程
那问题来了,该如何下手呢?🤨
- 要重构一个组件,首先要清除在哪里会被用到,我这里找了它的几个用法: - 1 
 2
 3
 4
 5
 6
 7- // R_record.jsx
 // 这里Ritem就是msgItem
 {list?list.map((item)=>{
 return (
 <Ritem {...item} data={item} teacher={teacher} status={status} lap={lap} key='rescue' flag='record' />
 )
 }):<View className='img'>- 1 
 2
 3
 4
 5
 6- // Main.jsx
 {mine?mine.map((item)=>{
 return (
 <MsgItem {...item} data={item} ifmine key='item' flag='main' />
 )
 })- 1 
 2
 3
 4
 5
 6
 7
 8
 9
 10- //Search.jsx
 // 这段是之前我写的,一坨💩
 {form &&
 form.map((item) => <MsgItem
 data={item}
 {...item}
 ifmine={form.some(itm => JSON.stringify(item) === JSON.stringify(itm))}
 key='item'
 flag='main'
 ></MsgItem>)}- 在真正重构的过程中,一定要理解这些个组件有什么用,是干啥的,但是现在这个例子中,先不用管,因为这个东西有点子复杂的,我自己都看半天😁,只知道它接受啥参数就可以了。 - 观察不难发现 - 参数中有个 - flag和- key,这俩名字一般是用来区分组件行为的,所以这个组件很可能会有两三个不同的功能
- {...item}和- data={item}这俩玩意传的一摸一样,也可以删去
- 还有一些 - teacher,- status等可传可不传的- props,这些说明组件功能可能有较大差异,可能之后要把它分为多个组件;或者把参数取消,传回调函数,完全受控组件降低耦合度
 - 分析完之后,发现任务很艰巨啊,开摆得了(bushi) 
- 之后,面对这样艰巨的任务,我一般是先挑软柿子捏,啥事软柿子嘞? - 就是 - 名称,- 目录结构这些改了无伤大雅但是看着会很舒服的东西🐶- 《重构》的作者在书中说: 如果有一个人在重构的过程中有一段时间代码无法运行,那他肯定不在重构 - 因此,咱们重构要一步一步来。 - 从 - 名称,- 目录结构这些“皮毛”的东西开始,保证能正常运行的情况下,一点点慢慢改😝- 这里目录结构没啥问题,改个名字就行了,我给它起了个响当当的大名: - RerscueInfoItem,之后都用- RescueInfoItem代替- MsgItem- 之后扫一眼代码,发现 - 1 - const jiean = param.status==1?'未结案':'已结案'- 1 - backgroundColor:color==2?'#76A9FF':color==1?'#f9a53e':'#f57b70'- 1 
 2
 3
 4
 5
 6- const status = [
 '未救援',
 '救援中',
 '已救援'
 ]
 setFinish(status[res.data.rescue_target_info.status])- 这种东西,完全可以把它们搞成一个 - Map,再拿到外边的- type或- config文件里面嘛- 新开个文件咯,就叫 - rescueMessageItemTypes:- 1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19- // rescueMessageItemTypes.ts
 export type rescueStatusType = {
 text: string
 color: string
 }
 export const rescueStatus: rescueStatusType[] = [
 {
 text: '未救援',
 color: '#f57b70',
 },
 {
 text: '救援中',
 color: '#f9a53e',
 },
 {
 text: '已救援',
 color: '#76A9FF',
 },
 ]- 结案这种一下子就搞定而且非常易懂的事情,就不单独拎出来做变量了,改完之后 - return里就变成这样:- 1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21- <View className="inline">
 <View
 className="state"
 style={{
 backgroundColor:
 rescueStatus[targetInfo ? targetInfo.status : 0].color,
 }}
 >
 {rescueStatus[targetInfo ? targetInfo.status : 0].text}
 </View>
 <View className="rescued_level">风险等级:{props.risk_level}</View>
 </View>
 <View>
 <View className="rescued_info">
 <View className="row">
 <View className="border_l"></View>微博内容:
 </View>
 <View className="demand">{props.text}</View>
 </View>
 <View className="rescued_city">地点:{props.area}</View>
 </View>
- 还是聚焦于表面功夫,作为 - props传进的参数一堆,而且要- state数量也不少,影响性能不说,反正是不好看- 1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11- const [flag,setFlag]=useState('');
 const [finish,setFinish]=useState('已救援')
 // const [jiean,setJiean]=useState('')
 const [color,setColor]=useState(0)//0:#f57b70 1:#76A9FF 2:#f9a53e
 // const [status,setStatus]=useState(0)
 const [text,setText]=useState('')
 const [area,setArea]=useState('')
 const [level,setLevel]=useState('')
 const [data,setData]=useState([])
 const [ifmine,setIfmine] = useState(false)- 但是这里很多 - state只是跟- props绑定,而- props不会经常改变,因此完全可以去掉这些- state,直接使用- props,优化性能。- 注意到跳转时传参很多,完全可以采用 - redux的方式,优化逻辑,把相似的变量和参数都提到外部- : - 1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44- // rescueInfoSlice.ts
 import { createSlice, PayloadAction } from '@reduxjs/toolkit'
 import {
 SingleRescueInfo,
 RescueTargetInfoType,
 RescueProcess,
 } from '../Service/fetchTypes'
 import { rescueInfoSliceType } from './sliceTypes'
 const rescueInfoSlice = createSlice({
 name: 'rescueInfo',
 initialState: {
 rescueInfo: {} as SingleRescueInfo,
 targetInfo: {} as RescueTargetInfoType,
 process: [],
 eventID: 0,
 targetID: 0,
 } as rescueInfoSliceType,
 reducers: {
 updateRescueInfo: (
 state: rescueInfoSliceType,
 action: PayloadAction<SingleRescueInfo>,
 ) => ({
 ...state,
 rescueInfo: action.payload,
 eventID: action.payload.id,
 targetID: action.payload.rescue_target_id,
 }),
 updateTargetInfo: (
 state: rescueInfoSliceType,
 action: PayloadAction<RescueTargetInfoType>,
 ) => ({ ...state, targetInfo: action.payload }),
 updateProcess: (
 state: rescueInfoSliceType,
 action: PayloadAction<RescueProcess[]>,
 ) => ({ ...state, process: action.payload }),
 },
 })
 export default rescueInfoSlice.reducer
 export const { updateTargetInfo, updateRescueInfo, updateProcess } =
 rescueInfoSlice.actions- 这里 - redux不理解不要紧,主要是理解抽出- state的这个思路,减少传参,提升可维护性😊- 现在,只需要一个变量:使用变量只需要从 - store中取出即可,也不需要另外传参给其余页面。- 同时,注意到 - useEffect中的调用:- 1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41- useEffect(()=>{
 /* console.log(param.flag) */
 // setStatus(param.status)//主页item
 
 if(param.flag=='main') //主页
 {
 setFlag('alarm')
 if(param.ifmine)
 {
 setIfmine(true)
 setFinish('救援中')
 setColor(1)
 }
 else{
 // 未认领的救援
 setIfmine(false)
 Fetch(
 '/rescue/targetinfo',
 {
 "rescue_target_id": param.rescue_target_id,
 },
 'POST'
 ).then(
 res=>{
 //console.log(res)
 setFinish(status[res.data.rescue_target_info.status])
 setColor(res.data.rescue_target_info.status)
 }
 )
 }}
 else{
 //record
 setFlag('record')
 setFinish(status[param.status])
 setColor(param.status)
 }
 setText(param.text)
 setArea(param.area)
 setLevel(param.risk_level)
 setData(param.data)
 },[param])- 这些 - setState都可以省去,但是,很明显,这里逻辑很复杂, 需要重新组织一下逻辑- 这个时候,尘封已久的大脑就要开始转动了: - 可以看到,主要是 - flag这个值是逻辑复杂的开始,根据注释可以知道,写的人当时也很挣扎🐶。- 这可能是因为后端接口的问题,导致主页面上有未认领的任务,需要重新请求才能得到信息。 - 然而猛然一看,看到这段, - 1 
 2
 3
 4
 5
 6- if(param.ifmine)
 {
 setIfmine(true)
 setFinish('救援中')
 setColor(1)
 }- 也没在干什么,所以就索性一视同仁,都发个请求呗 
- 这个时候,我是喜欢把请求这些东西放到外边去的,搞个 - service文件夹咯- 1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85- // /service/fetchType.ts
 export type BaseResType<T> = {
 code: number
 msg: string
 data: T
 }
 export type requestType = 'GET' | 'POST' | 'PUT' | 'DELETE'
 export interface RescueTargetInfoResponse {
 rescue_target_info: RescueTargetInfoType
 }
 // 这段直接从apifox上copy就行
 export interface RescueTargetInfoType {
 create_time: string
 /**
 * 救援过程描述
 */
 description: string
 /**
 * 救援结束时间
 */
 end_time: string
 /**
 * 最终评价
 */
 evalutaion: string
 /**
 * 救援对象id
 */
 id: number
 /**
 * 救援对象昵称(按最新的救援信息数据)
 */
 nickname: string
 /**
 * 救援老师1id
 */
 rescue_teacher1_id: number
 /**
 * 救援老师1姓名
 */
 rescue_teacher1_name: string
 /**
 * 救援老师1身份
 */
 rescue_teacher1_role: number
 /**
 * 救援老师2id
 */
 rescue_teacher2_id: number
 /**
 * 救援老师2姓名
 */
 rescue_teacher2_name: string
 /**
 * 救援老师2身份
 */
 rescue_teacher2_role: number
 /**
 * 救援老师3id
 */
 rescue_teacher3_id: number
 /**
 * 救援老师3姓名
 */
 rescue_teacher3_name: string
 /**
 * 救援老师3身份
 */
 rescue_teacher3_role: number
 /**
 * 救援起始时间
 */
 start_time: string
 /**
 * 救援状态(0-待救援 1-救援中 2-已救援)
 */
 status: number
 update_time: string
 /**
 * 救援对象微博地址(唯一标识)
 */
 weibo_address: string
 }- 1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11- // /service/rescueInfoByID.ts
 export const FetchRescueTargetInfo = async (targetID: number) => {
 const data = {
 rescue_target_id: targetID,
 }
 return Fetch<BaseResType<RescueTargetInfoResponse>>(
 '/rescue/targetinfo',
 data,
 'POST',
 )
 }- 根据它传进来的 - props,来请求- target_info,并且变成- state放起来- 这个时候,文件已经开始变得清爽了: - 1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51- const MsgItem=(props)=>{
 const { navURL, ...restProps } = props
 const [targetInfo, setTargetInfo] = useState<RescueTargetInfoType>()
 useEffect(() => {
 FetchRescueTargetInfo(restProps.rescue_target_id).then((res) => {
 setTargetInfo(res.data.rescue_target_info)
 })
 }, [])
 function toR_detail()
 {
 console.log(flag)
 if(flag=='alarm')
 Taro.navigateTo({
 url:`/moduleA/pages/Alarm/index?ifmine=${ifmine}&data=${JSON.stringify(data)}&finish=${finish}&color=${color}`
 })
 else{
 console.log('todetail')
 const jiean = param.status==1?'未结案':'已结案'
 Taro.navigateTo({
 url:`/moduleA/pages/Rescue_detail/index?finish=${jiean}&data=${JSON.stringify(data)}&teacher=${JSON.stringify(param.teacher)}&lap=${JSON.stringify(param.lap)}`
 })
 }
 }
 return(
 <View className='noti-item column' onClick={toR_detail} >
 <View className="inline">
 <View
 className="state"
 style={{
 backgroundColor:
 rescueStatus[targetInfo ? targetInfo.status : 0].color,
 }}
 >
 {rescueStatus[targetInfo ? targetInfo.status : 0].text}
 </View>
 <View className="rescued_level">风险等级:{props.risk_level}</View>
 </View>
 <View>
 <View className="rescued_info">
 <View className="row">
 <View className="border_l"></View>微博内容:
 </View>
 <View className="demand">{props.text}</View>
 </View>
 <View className="rescued_city">地点:{props.area}</View>
 </View>
 </View>
 )
 }
- 接下来,就剩一个 - toR_detail函数要改咯,也是撒撒水啦,因为刚刚我们把state都放到外边去了,这里也不用通过传参来让其他页面访问咯,超级爽,略微改改,就这样咯,其中- Nav是我封装的跳转函数,因为不想写那么多- Taro.navigateTo({url:"......"}):- 1 
 2
 3
 4
 5
 6- function Navi() {
 const navURL_real = navURL || '/moduleA/pages/Alarm/index'
 store.dispatch(updateRescueInfo(restProps))
 targetInfo && store.dispatch(updateTargetInfo(targetInfo))
 Nav(navURL_real)
 }
- 这下就大功告成了,吗?还记得一开始的那些组件传的 - props吗?也该清清啦!,像什么- flag,- ifmine,都可以扔掉啦,只留下基本的msg信息就行- 1 
 2
 3- // R_record.jsx
 // 这里Ritem就是msgItem
 {list?list.map((item)=><Ritem {...item} />):<View className='img'>- 1 
 2- // Main.jsx
 {mine?mine.map((item)=><MsgItem {...item}/>)- 1 
 2
 3- //Search.jsx
 // 这段是之前我写的,一坨💩
 {form && form.map((item) => <MsgItem {...item} />)}
- 现在回头看看我们重构后的组件的全貌吧: - 1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60- import { View } from '@tarojs/components'
 import React, { useEffect, useState } from 'react'
 import './index.less'
 import {
 RescueMessageItemProps,
 rescueStatus,
 } from '../types/rescueMessageItemTypes'
 import { Nav } from '../../utils/taroFunctions'
 import { FetchRescueTargetInfo } from '../../Service/rescueInfoByID'
 import { RescueTargetInfoType } from '../../Service/fetchTypes'
 import {
 updateRescueInfo,
 updateTargetInfo,
 } from '../../slices/rescueInfoSlice'
 import store from '../../store/store'
 const RescueMessageItem: React.FC<RescueMessageItemProps> = (props) => {
 const { navURL, ...restProps } = props
 const [targetInfo, setTargetInfo] = useState<RescueTargetInfoType>()
 useEffect(() => {
 FetchRescueTargetInfo(restProps.rescue_target_id).then((res) => {
 setTargetInfo(res.data.rescue_target_info)
 })
 }, [])
 function Navi() {
 const navURL_real = navURL || '/moduleA/pages/Alarm/index'
 store.dispatch(updateRescueInfo(restProps))
 targetInfo && store.dispatch(updateTargetInfo(targetInfo))
 Nav(navURL_real)
 }
 return (
 <View className="noti-item column" onClick={Navi}>
 <View className="inline">
 <View
 className="state"
 style={{
 backgroundColor:
 rescueStatus[targetInfo ? targetInfo.status : 0].color,
 }}
 >
 {rescueStatus[targetInfo ? targetInfo.status : 0].text}
 </View>
 <View className="rescued_level">风险等级:{props.risk_level}</View>
 </View>
 <View>
 <View className="rescued_info">
 <View className="row">
 <View className="border_l"></View>微博内容:
 </View>
 <View className="demand">{props.text}</View>
 </View>
 <View className="rescued_city">地点:{props.area}</View>
 </View>
 </View>
 )
 }
 export default RescueMessageItem- 一共是59行,是不是特别的一目了然,看到之后,心情都变好了,不枉我重构这么久🤗 
五、总结⬇️
在《重构》中,作者提到了营地法则,即要保证在你来之后,代码变得更好而不是更糟糕,而保证代码变得更好的秘诀,就是重构。这次通过一个小例子,来让大家知道把时间花在重构上并不可怕,经常重构并不见得是一件坏事,相反的,经常重构代表着你在积极思考,努力让代码变得更优秀;而不重构,只能是给未来的自己和同伴添堵咯。当然,过早过频繁的重构也是不提倡的,就比如:本来这个按钮只用负责跳转,你写着写着,突然想到以后要是要让他能点一下出来一个ggbond怎么办,你库库开始重构,重构完之后,一天就过去了。然而产品老板到最后也没有点一下出来一个ggbond的需求(虽然这真的很酷😝)。那这一天就是白费的。总之,重构就是一门艺术,过多过少都不提倡,要根据项目要求,灵活重构。不多说了,这些写多了自然就有感觉啦🤗