簡體   English   中英

可變或不可變的類?

[英]Mutable or immutable class?

我在一些設計書中讀到,不可變類提高了可伸縮性,並且盡可能地編寫不可變類。 但我認為如此不可改變的階級會增加對象的擴散。 因此,為了提高可伸縮性,使用靜態類(具有所有靜態方法的類)更好地進行不可變類或更好嗎?

但是, 不可變類的主要好處是可以公開不可變的內部數據成員,因為調用者無法修改它們。 這是一個巨大的問題,比如java.util.Date 它是可變的,因此您無法直接從方法返回它。 這意味着你最終會做各種防御性復制 這增加了對象的擴散。

另一個主要好處是根據定義,不可變對象沒有同步問題。 這就是可擴展性問題的來源。編寫多線程代碼很難。 不可變對象是(大多數)繞過問題的好方法。

至於“靜態類”,通過你的評論,我認為它是指具有工廠方法的類,這是通常描述的方式。 那是一種無關的模式。 可變類和不可變類都可以具有公共構造函數或具有靜態工廠方法的私有構造函數。 這對類的(im)可變性沒有影響,因為可變類是在創建后可以更改其狀態的類,而在實例化后不能更改不可變類的狀態。

然而,靜態工廠方法可以具有其他好處。 想法是封裝對象創建。

不可變類確實會促進對象擴散,但是如果你想要安全,可變對象會促進更多的對象擴散,因為你必須返回副本而不是原始副本以防止用戶更改你返回的對象。

至於使用所有靜態方法的類,在大多數可以使用不變性的情況下,這不是一個真正的選項。 從RPG中獲取此示例:

public class Weapon
{
    final private int attackBonus;
    final private int accuracyBonus;
    final private int range;

    public Weapon(int attackBonus, int accuracyBonus, int range)
    {
        this.attackBonus = attackBonus;
        this.accuracyBonus = accuracyBonus;
        this.range = range;
    }

    public int getAttackBonus() { return this.attackBonus; }
    public int getAccuracyBonus() { return this.accuracyBonus; }
    public int getRange() { return this.range; }
}

您如何使用僅包含靜態方法的類實現此操作?

正如cletus所說,不可變類簡化了同步方法中的類設計和處理。

即使在單線程應用程序中,它們也簡化了集合中的處理。 不可變類永遠不會改變,因此鍵和哈希碼不會改變,所以你不會搞砸你的收藏。

但是你應該記住你正在建模的東西的生命周期和構造函數的“重量”。 如果您需要更改該東西,則不可變對象變得更加復雜。 您必須替換它們,而不是修改它們。 並不可怕,但值得考慮。 如果構造函數需要非常重要的時間,那也是一個因素。

需要考慮的一件事是:如果您打算將類的實例用作HashMap中的鍵,或者如果您要將它們放在HashSet中,那么使它們成為不可變的更安全。

HashMap和HashSet指的是只要對象在map或set中,對象的哈希碼就保持不變。 如果你將一個對象用作HashMap中的一個鍵,或者你把它放在一個HashSet中,然后改變對象的狀態,以便hashCode()返回一個不同的值,那么你就會混淆HashMap或HashSet,你會得到奇怪的東西; 例如,當您迭代地圖或設置對象時,但是當您嘗試獲取它時,就好像它不存在一樣。

這是由於HashMap和HashSet如何在內部工作 - 它們通過哈希代碼組織對象。

Java並發大師Brian Goetz 撰寫的這篇文章很好地概述了不可變對象的優缺點。

不可變性通常用於實現可伸縮性,因為不可變性是Java中並發編程的推動因素之一。 因此,正如您所指出的那樣,“不可變”解決方案中可能存在更多對象,這可能是提高並發性的必要步驟。

另一個同樣重要的用途是使用不可變性來消耗設計意圖 ; 制作不可變類的人意圖將其置於其他地方。 如果你開始改變那個類的實例,你可能會破壞設計的初衷 - 誰知道后果可能是什么。

以字符串對象為例。 有些語言或類庫提供可變字符串,有些則不提供。

使用不可變字符串的系統可以對具有可變字符串的系統進行某些優化。 例如,您可以確保只有任何唯一字符串的副本。 由於對象“開銷”的大小通常遠小於任何非平凡字符串的大小,因此這可能節省大量內存。 還有其他潛在的空間節省,如實習子串。

除了可能的內存節省之外,不可變對象還可以通過減少爭用來提高可伸縮性。 如果您有大量線程訪問相同的數據,則不可變對象不需要精心設計的同步過程來進行安全訪問。

再考慮一下這個問題。 使用不可變對象允許您緩存它們而不是每次都重新創建它們(即字符串)它對您的應用程序性能有很大幫助。

我想,如果你想在不同的變量之間共享同一個對象,它需要是不可變的。

例如:

String A = "abc";
String B = "abc";

Java中的String對象是不可變的。 現在A和B都指向相同的“abc”字符串。 現在

A = A + "123";
System.out.println(B);

它應該輸出:

abc

因為String是不可變的,所以A只會指向新的“abc123”字符串對象,而不是修改前一個字符串對象。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM