2

IntelliJ-IDEA中有一個重構工具,它允許我從方法中提取參數對象。提取參數對象有什麼優點?

enter image description here

這將做類似如下:

public interface ThirdPartyPoint { 
    float getX(); 
    float getY(); 
} 

前:

class Main { 
    public static float distanceBetween(float x1, y1, x2, y2) { 
     return distanceBetween(x1, y1), x2, y2); 
    } 

    public static float distanceBetween(ThirdPartyPoint point1, ThirdPartyPoint point2) { 
     return distanceBetween(point1.getX(), point1.getY(), point2.getX(), point2.getY()); 
    } 
} 

後:

class Main { 

    public static float distanceBetween(Point point1, Point point2) { 
     return Math.sqrt(Math.pow(point2.getX() - point1.getX(), 2) + Math.pow(point2.getY() - point1.getY(), 2)); 
    } 

    public static float distanceBetween(ThirdPartyPoint point1, ThirdPartyPoint point2) { 
     return distanceBetween(new Point(point1.getX(), point2.getY()), new Point(point2.getX(), point2.getY())); 
    } 

    private static class Point { 
     private final float x; 
     private final float y; 

     private Point(float x, float y) { 
      this.x = x; 
      this.y = y; 
     } 

     public float getX() { 
      return x; 
     } 

     public float getY() { 
      return y; 
     } 
    } 
} 

爲什麼這比任何更好過嗎?

現在,如果我必須使用此方法,每次調用它時都需要創建一個新的點對象。而之前,我只能使用原始類型。

我的感覺是方法簽名通常應該朝相反的方向走。例如,如果你有一些函數,發現一個名字是如何流行的是這樣的:

public int nameRanking(String name) { 
    // do something with name 
} 

你提取參數對象是這樣的:

public int nameRanking(Person person) { 
    // do something with person.getName() 
} 

不使事情變得更糟?例如,如果在從重構菜單創建Person類後,我決定刪除getName()方法,因爲我不希望名稱對所有人公開可用,但其他類使用nameRanking函數?現在我需要更改我的姓名排名功能。如果我使用內置的String類,我知道沒有任何注入這個函數會改變。

回答

0

我認爲你的第二個例子(namePerson)與你的第一個例子有很大的不同。在第一個示例中,您不會發送更多或更少的信息,而是以不同的形式發送完全相同的信息。在你的第二個例子中,如你所說,你最終發送了大量的信息,這個函數並不需要執行它的職責。作爲一個經驗法則,您總是希望將您發送功能的信息限制爲執行任務時需要執行的操作。在你的情況下,這就是這個人的名字。這有助於減少耦合,並讓您的函數不必知道太多其他類的行爲/結構來執行它的任務。你可以提出一個例子,你的姓名排名函數可能有一天會被用於使用年齡組和其他信息加權的排名,並且由於這個原因,最好發送一個整體Person的對象,但現在情況並非如此,所以我不會進入那個。

讓我們回到你的第一個例子。正如我所提到的,在這種情況下,您最終會以另一種形式發送相同的信息。在哪種情況下比發送更長的原語列表更好?假設我們編碼爲Red Square。在這個遊戲中,你必須控制一個紅色的方塊並躲開移動的藍色矩形。我將向您展示兩種不同的方式,其中可以編寫一些代碼。

第一個實現看起來是這樣的:

int x2 = game.player.pos_x 
int y2 = game.player.pos_y 
int w2 = game.player.width 
int h2 = game.player.height 
for(Enemy enemy : game.enemies) { 
    int x2 = enemy.pos_x 
    int y2 = enemy.pos_y 
    int w2 = enemy.width 
    int h2 = enemy.height 

    // If it is colliding with the player 
    if(collides(x1, y1, w1, h1, x1, y2, w2, h2)) { 
     die() 
    } 

    // If it is going out of bounds, bump it back inside 
    if(isInBounds(0, 0, game.map.width, game.map.height, x2, y2, w2, h2)) { 
     enemy.bump() 
    } 

    //... More functions that use x1, y1, w1, w1, ... 
} 

這是第二個實現,我們已經introduced a Parameter ObjectRectangle

Rectangle playerRect = game.player.rect 
for(Enemy enemy : game.enemies) { 
    Rectangle enemyRect = enemy.rect 

    // If it is colliding with the player 
    if(collides(playerRect)) { 
     die() 
    } 

    // If it is going out of bounds, bump it back inside 
    if(isInBounds(game.map.rect, enemyRect)) { 
     enemy.bump() 
    } 

    //... More functions that use playerRect and enemyRect 
} 

第二個執行的一些優點包括:

  1. 更可讀的函數調用(更少的參數)
  2. 的空間更小的錯誤與發送參數亂序(如果是我送的(width, height, x, y)代替(x, y, width, height)?)
  3. 你一定要始終與矩形的整體情況下工作(發送功能,您的播放器的X,而不是沒有機會敵人的X,或者錯誤這樣的)

的,我相信你有Point第一個例子是更類似於Rectangle一個比你的第二個例子(你在哪裏結束注入更緊密的耦合到您不需要的信息)。如果您有一組參數經常發送到不同的功能,這可能表示它們正在作爲一個實體運行,並且會從「介紹參數對象」重構中受益。例如,在您的Point示例中,您很少(從不?)使用xy作爲獨立信息。你總是將它們作爲一個實體使用,點(x,y)

請記住,使用自動重構工具時,您必須小心。 「Introduce Parameter Object」不應該在每個單一的函數調用中盲目使用。正如我前面所說的,你應該問自己是否有意義(概念上)將這些參數作爲一個整體進行捆綁。據說,重構可以幫助您提高代碼的可讀性並減少發送函數的參數數量,從而減少順序錯誤或參數不匹配。不要相信我?你有沒有注意到第一個collides(...)函數調用中的細微錯誤? :-)

相關問題