[英]static vs non-static method for immutable class
鑒於下面的類定義。 如何決定存根方法是靜態還是非靜態?
class Point {
private final int x;
private final int y;
public Point(int x, int y) {
this.x = x;
this.y = y;
}
// Should the methods add(), subtract() and inverseOf() be non-static ...
public Point add(Point point) {
}
public Point subtract(Point point) {
}
public Point inverseOf() {
}
// Or static?
public static Point add(Point point1, Point point2) {
}
public static Point subtract(Point point1, Point point2) {
}
public static Point inverseOf(Point point) {
}
}
我會選擇方法。 然后,您可以使方法成為接口的一部分並覆蓋它們。 當你必須處理2d點或3d點並且有一些不太關心的客戶端代碼並且只需要在實現接口的Point上執行操作時,你將獲得好處。
我認為這取決於你想要完成的事情。 如果您提供的方法可以將任意兩點相加,那么您需要一個靜態方法。 但是,如果您想要一個向給定Point實例添加點的方法,那么您需要一個非靜態方法。
如果使用靜態方法,則可以考慮將靜態方法放入僅包含靜態方法的單獨實用程序類(PointCalculator)中。 這類似於Math類。
我會選擇面向對象的非靜態方法(是的,使用太多的靜態方法會破壞多態,繼承等對象的好處),即使你的Point
是不可變的。 實際上,這與BigDecimal
或BigInteger
等類的設計方式一致。 最重要的是, 靜態方法使類更難以測試,所以我更願意盡可能避免使用它們,特別是當它有意義時。
從語義上講,靜態方法似乎更有意義。 兩者當然都可以工作,但是非靜態方法優先於一個點而不是另一個點,並且還意味着可以根據調用修改point1(調用add的方法)。
作為使用您的類的開發人員,如果我看到以下內容:
Point p1 = new Point(1,2);
Point p2 = new Point(2,3);
p1.Add(p2);
要么..
Point p1 = new Point(1,2);
Point p2 = new Point(2,3);
Point.Add(p1, p2);
我的自然傾向是假設非靜態版本中的add()方法修改了point1以添加第2點的結果。使用靜態方法,它更清晰(盡管不能保證!)該方法是純粹的並且代表點沒有被修改。
當方法的主體不依賴於任何特定實例時,請使用靜態方法。
舉個例子,看看你的add(Point, Point)
方法。 您正在將作為參數傳遞給函數的兩個Point
加在一起,並返回另一個Point
。 這真的需要內部this
引用某些Point
嗎?
另一方面,你有一個方法add(Point)
。 據推測,這會將函數參數添加到實例中 - 在這種情況下,您必須使這個實例方法成為兩個Point
。
編輯 :我想我最初誤會了。 回顧過去,您可以獲得靜態和非靜態實現的正確簽名。 在這一點上,我會說這是一個風格問題,因為你知道兩者都能正常工作。 您希望如何使用您的積分課程? 想想是否使代碼更直觀地說Point a = Point.add(b, c)
或Point a = b.add(c)
。 就個人而言,我喜歡前者,因為它告訴我兩個操作數都不會被修改。
如果您打算使用Java並創建對象,那么在風格上,我認為您應該嘗試最大限度地利用對象和數據封裝。 對我來說,這意味着將數據保留在它所在的位置(在Point類中),而不是將其傳遞給單獨的方法來處理它。 讓你的對象為你工作; 不只是有吸氣劑和二傳手。 事實上,要認真考慮如何避免需要吸氣劑。
在不可變類上使用add()和subtract()等方法來返回不可變類的新實例是很常見的。 這對於類似FP的編程來說是一種很好的風格,對於像這樣的類來說非常合理。 (請參閱BigInteger或BigDecimal以獲取良好示例。請勿查看日期或日歷,以了解糟糕的可怕示例。:)
保持類中的方法允許您可選地定義這些類可能實現的接口,使用Decorator或Adapter模式,編寫某些類型的測試等。
這些功能自然必須是非靜態的。 但如果您懷疑請參考GRASP,他們會描述這樣的事情。
根據GRASP信息專家,這些功能不應該是靜態的。
盡管事實上沒有關於靜態方法的直接信息,但有
信息專家將引導我們將責任放在具有實現它所需的最多信息的課程中。
如果使方法成為靜態,則會將邏輯從實際數據中進一步移動,並且必須將數據傳遞給方法。
刪除靜態會使邏輯更接近它使用的數據。
我傾向於違背常規,但無論哪種方式對我來說都是合理的。
對於像Java這樣的語言,我會使用靜態方法,特別是因為上面的第二點。 對於具有運算符重載的語言(如Ruby),我會使用實例方法來利用它。
使它們靜止也會使它們更難以進行單元測試! 我在.NET中知道的唯一可以處理這個問題的模擬框架是TypeMock。
如果目的是使這個類不可變,那么你將在任何訪問器中返回新的Point對象,所以調用它們使它們靜態在這里沒有多大意義。
這些方法應該是靜態的,因為Class本身有助於通過構造函數創建,並且由於x和y是最終的,所以賦值一次。 這意味着您可以創建Points,但不會繼續操縱他們的數據。 Add / Substract / Etc方法是實用方法,不需要使用Point實例。
在您的情況下,除非您將簽名更改為public static Point add(Point point1, Point point2)
否則它必須是非靜態的。
編輯 :我投了票。 沒關系。 我並沒有試圖給出一些簡單的建議,比如把靜態放在前面的方法中。 在這種情況下,實例方法更好,但實際上並沒有單一的答案。 這取決於您的偏好。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.