天天看點

IntelliJ IDEA 複雜的重構技巧

重構是 IDE 給人類生活帶來便利的一個重要方面。但是 IDE 永遠不是我們肚子裡的蛔蟲,有時我們會有複雜到 IDE 不可能直接提供的重構需求。

下面我來告訴大家怎麼利用有限的 IDE 重構功能, 創造無限的價值 處理複雜的情況。

https://blog.didispace.com/intellij-idea-refactoring-skills/#%E5%A4%8D%E4%B9%A0%E4%B8%80%E4%B8%8B%E5%BF%AB%E6%8D%B7%E9%94%AE 複習一下快捷鍵

先複習一下快捷鍵吧,我們這次就看兩個就好。

https://blog.didispace.com/intellij-idea-refactoring-skills/#inline inline

這個叫

inline

的東西快捷鍵是 Ctrl+Alt+n。

這個東西的作用是把目前光标上的東西,在代碼級别内聯掉。

按下這個快捷鍵後,會看到一個彈窗(這個是

inline

一個 Kotlin 方法的彈窗,對于 Java 還多幾個選項。 不過這都不是重點啦):

我們都預設選第一個,就是在

inline

之後删除被

inline

的東西,第二個是

inline

後保留。

如果你是在調用處而不是定義處這麼搞,第三個選項就可以選,是隻

inline

這一處。

我們一般不管,使用第一個。

https://blog.didispace.com/intellij-idea-refactoring-skills/#rename rename

這個我就不多介紹了,應該是最常用的快捷鍵之一了: Shift+F6 。

https://blog.didispace.com/intellij-idea-refactoring-skills/#%E5%88%A0%E9%99%A4%E4%B8%80%E4%B8%AA%E8%A2%AB%E5%A4%9A%E6%AC%A1%E5%BC%95%E7%94%A8%E7%9A%84%E7%A9%BA%E5%87%BD%E6%95%B0 删除一個被多次引用的空函數

https://blog.didispace.com/intellij-idea-refactoring-skills/#%E5%9C%BA%E6%99%AF 場景

我們知道, IntelliJ 會把 “沒有被用到的函數” 标灰(這個 “沒有被用到” 的定義其實蠻複雜的,比如你實作了一個接口, 那麼這個接口的方法即使沒被調用也不會被标灰。這裡我就不糾結這個細節了),并且會給出 “Safe delete” 的提示。

這往往不是我們想要的,因為我們看到這個東西的時候, 多半都是剛寫完一個函數還沒來得及調用的時候。

而我們有時在重構的時候,一個函數裡面的東西被全部移出去後,這個函數體就是空的了,而它仍然在多處被調用。

我們這時想删除這個函數,以及它的所有調用處。

fun SymbolList.addGetSetFunction() {
}      

比如這個,我在重構

Lice

的時候,就産生了很多上面這種東西。

這個函數被調用了,是以 IntelliJ IDEA 不會給出 “Safe delete” 的選項。

雖然語言是 Kotlin ,但是這就是一個樸素的函數聲明,我覺得不需要進行進一步的說明。

private fun initialize() {
  addDefines()
  addGetSetFunction()
  addControlFlowFunctions()      

它像這樣被不停調用着。

當然,你可以按下 Ctrl 然後點選這個函數,再一處一處地删除。

https://blog.didispace.com/intellij-idea-refactoring-skills/#%E8%A7%A3%E5%86%B3%E6%96%B9%E6%B3%95 解決方法

不過我們為什麼不試試直接

inline

掉它呢?

private fun initialize() {
  addDefines()
  addControlFlowFunctions()      

它的函數體本身就是空的,是以說

inline

掉後,每個調用處就啥都沒了。

https://blog.didispace.com/intellij-idea-refactoring-skills/#%E4%BF%AE%E6%94%B9%E5%A4%A7%E9%87%8F%E5%87%BA%E7%8E%B0%E7%9A%84%E7%9B%B8%E5%90%8C%E7%BB%93%E6%9E%84 修改大量出現的相同結構

https://blog.didispace.com/intellij-idea-refactoring-skills/#%E5%9C%BA%E6%99%AF-1

比如,我們有這樣的,自己用的庫代碼(為了讓更多人看懂,我在這裡使用了 Java):

// code 0
class Val {
  private Object o;
  public Object getO() { return o; }
}
interface Node { Val eval(); }      

然後我們可以通過 

someNode.eval().getO()

 來擷取一個 

Object

 ,對吧。

然後想象一下我們有這樣的業務代碼:

// code 1
Object a = xxx.getNode().eval().getO();
xxx.use(yyy.eval().getO());
Val bla = blablabla.eval();
switch (bla.getO().toString()) {
  case "2333":
    break;
}
...      

這是我們現在的代碼。

https://blog.didispace.com/intellij-idea-refactoring-skills/#%E9%97%AE%E9%A2%98 問題

然後我們經過一番小重構,把剛剛的庫代碼重構成了這樣:

// code 0
interface Node { Object eval(); }      

直接把 

Val

 去掉了,然後讓這個 

eval()

 直接傳回原本裝在 

Val

 裡面的變量,然後 

Node

 的各種實作也都改了。

這時候我們的業務代碼已經是一坨紅色了。

我們想批量去掉這個

.getO()

的結構,應該怎麼辦呢?

首先我們不考慮查找替換,因為

  • 有這種結構的檔案很多(假設有一萬個),很麻煩(不過 IntelliJ IDEA 有 “Replace in path” 功能)
  • 有很多其他的叫

    getO()

    但不需要被重構掉的函數,會受到波及(這才是最主要的)

這也是很常見的原因,對吧。

這時我們就需要技巧性地重構了。

https://blog.didispace.com/intellij-idea-refactoring-skills/#%E8%A7%A3%E5%86%B3%E6%96%B9%E6%B3%95-1

首先,我們先把庫代碼中的

Node

臨時性地改成這個樣子(也就是說,臨時性地把

Val

弄回來,隻是實作變得不一樣了):

// code 0
class Val {
  public Object getO() { return this; }
}
interface Node { Val eval(); }      

注意這裡的

getO()

被改成了傳回

this

這時我們剛剛的代碼中,

.getO()

上的紅色已經消失了(畢竟這幾乎就是改之前的樣子)。

然後,我們對 public Object getO() { return this; } 中的 getO() 使用 inline, 這樣所有的

.getO()

結構就被消除了(想想為什麼,很簡單的道理)

然後我們 把 Val 重命名為 Object ,然後直接删除 ,這樣剩下的代碼中用到

Val

的地方也就全部變成了

Object

, 也就是我們所期望使用的那個類型啦。

https://blog.didispace.com/intellij-idea-refactoring-skills/#%E5%8F%A6%E4%B8%80%E7%A7%8D%E6%83%85%E5%86%B5 另一種情況

上面說的,是針對 “批量删除對于一個方法的調用” 的解決方案。

但我們有時不是想删除,而是增加。這怎麼辦嘞?

比如,我們現在有上面那段重構完了的代碼(which 沒有

getO()

)。

我們現在要把每一處

eval()

後面加上

toString()

(反正就是需要加一層方法調用)。

這個也很好解決,我們隻需要先把

eval()

随便改成(不是重命名,是直接改)另外一個名字(比如

rua

):

// code 0
interface Node { Object rua(); }      

然後我們可以看到業務代碼全紅了:

然後我們再寫一個叫 

eval

 的方法,裡面傳回這個 

rua

 的調用結果再 

toString()

 (就是加上你要的那個方法調用):

// code 0
interface Node {
  default Object eval() {
    return rua().toString();
  }

  Object rua();
}      

這時業務代碼已經不報錯了。

我們再對這個 

eval()

 進行 

inline

 ,之後就是這個樣子的了:

然後再把

rua()

使用 IntelliJ 的重命名功能改成之前的

eval()

,就一切照舊啦。

https://blog.didispace.com/intellij-idea-refactoring-skills/#%E6%9C%AC%E6%96%87%E5%AE%8C 本文完

祝大家聖誕節快樂。

__________________________________________________
|                    _                             |
| /|,/ _   _ _      / ` /_  _ .  _ _/_ _ _   _    _|
|/  / /_' / / /_/  /_, / / / / _\  /  / / / /_| _\ |
|             _/                                   |
|                ~~** ice1000 **~~                 | 
|__________________________________________________| 


                      ___
                   /`   `'.
                  /   _..---;
                  |  /__..._/  .--.-.
                  |.'  e e | ___\_|/____
                 (_)'--.o.--|    | |    |
                .-( `-' = `-|____| |____|
               /  (         |____   ____|
               |   (        |_   | |  __|
               |    '-.--';/'/__ | | (  `|
               |      '.   \    )"";--`\ /
               \        ;   |--'    `;.-'
               |`-.__ ..-'--'`;..--'`

:*~*:._.:*~*:._.:*~*:._.:*~*:._.:*~*:._.:*~*:._.:*~*      

這是一個來自 coding.net 的驚喜,我在推送代碼的時候看到的: