2015-10-28 12 views
6

私は、不変クラスの概念についてはかなり新しいです。私は、このクラスは不変のものに変更することができる方法を理解しようとしていクラス内の不変性と状態の変化

public class ConnectionMonitor implements MessageConsumer { 

    private final MonitorObject monitorObject; 
    private boolean isConnected = true; 

    private final static Logger logger = LogManager.getLogger(ConnectionMonitor.class); 

    public ConnectionMonitor(final MonitorObject monitorObject) { 
     this.monitorObject = monitorObject; 
    } 

    public boolean isConnected() { 
     return isConnected; 
    } 

    public void waitForReconnect() { 
     logger.info("Waiting for connection to be reestablished..."); 
     synchronized (monitorObject) { 
      enterWaitLoop(); 
     } 
    } 

    private void enterWaitLoop() { 
     while (!isConnected()) { 
      try { 
       monitorObject.wait(); 
      } catch (final InterruptedException e) { 
       logger.error("Exception occured while waiting for reconnect! Message: " + e.getMessage()); 
      } 
     } 
    } 

    private void notifyOnConnect() { 
     synchronized (monitorObject) { 
      monitorObject.notifyAll(); 
     } 
    } 

    @Override 
    public void onMessage(final IMessage message) { 
     if (message.getType() == IMessage.Type.CONNECTION_STATUS) { 
      final String content = message.getContent(); 
      logger.info("CONNECTION_STATUS message received. Content: " + content); 
      processConnectionMessageContent(content); 
     } 
    } 

    private void processConnectionMessageContent(final String messageContent) { 
     if (messageContent.contains("Disconnected")) { 
      logger.warn("Disconnected message received!"); 
      isConnected = false; 
     } else if (messageContent.contains("Connected")) { 
      logger.info("Connected message received."); 
      isConnected = true; 
      notifyOnConnect(); 
     } 
    } 
} 

: このクラスを考えてみましょう。

特に、ブール値フィールドisConnectedが接続状態を表すため、最終的にどのように表示されるかわかりません。

ConnectionMonitorのすべてのクライアントは、接続状態を取得するために isConnected()を照会するだけです。

私は、isConnectedへのロック変更が可能であること、またはアトミックブール値を使用していることを認識しています。

しかし、これを不変クラスに書き換える方法はわかりません。

+0

すべてのオブジェクトが不変である必要はありません。 –

+1

私は、変更可能/不変のクラスをいつ作成するかを判断するだけの経験が必要ですか? – Juergen

+0

体験が役に立ちます。一般的に、可能な場合は不変性を優先しますが、可能な場合に限ります。私は以下の答えで詳細を追加しました。 –

答えて

5

を最小化することは、の変更を最小限に抑えることです。

不変オブジェクトには複数の利点があります。それらはシンプルでスレッドセーフで、自由に共有することができます。

ただし、時には変更が必要です。で

「有効ジャワ、」ジョシュア・ブロックは、これらのガイドラインを提案している:それらを可変させるために非常に良い理由がありますしない限り、

  • クラスは不変でなければなりません。
  • クラスを不変にすることができない場合は、可能な限りクラスの変更を制限します。

この例では、クラスのインスタンスが変更可能であるという非常に良い理由があります。しかし、プレイ中の第2のガイドラインも見ることができます。フィールドmonitorObjectにはfinalとマークされています。

+0

合理的なアドバイスのように聞こえます。私はすべてのクラスが不変でなければならないという表現を得た。しかし、私はそれをどうやって行うことができ、どうしてなぜ純粋な関数型プログラミング言語を使用しないのだろうと思います。 – Juergen

-1

教科書またはパラダイムの外では、実生活のオブジェクト(単目的クラスを考慮しない場合)はありません。zealot cellarは100%不変またはステートレスです。彼らは通常、彼らの声の音のようなチャンスを得るたびに公開時に不変性を説く人々。ある程度の変更がありますが、ここでの鍵は安全で非暴力的なやり方で行い、理由だけでオブジェクトを変異させないことです。オブジェクトの特定の部分が変更可能でなければならない理由を知り、手元にある可能性のある副作用をすべて知っている場合は、変更可能であることはまったく問題ありません。あなたが言ったように、ブール値isConnectedには、現在の接続状態を反映するためのセッターとゲッターまたはその他のメカニズムが必要です。基本的に2つの状態しか持たないバイナリスイッチであるため、変更可能な影響は最小限です。あなたは他の方法で同じ結果を達成することができますが、別のクラスの外部メソッドでそれを達成するには、フープとループを通過する必要があります。

TL; DR:変更可能な状態は悪いものではありませんが、悪用される可能性があります。

+0

ちょうど不思議なことに、なぜdownvotes?私の答えは事実上間違っているとは思わない。 –

+0

不変の場合は「オブジェクト」と呼ぶべきではないと言っている人がいるかもしれませんが、「final」フィールドしか持たないJavaクラスを書く人がいないと本当に信じるならば、数千の反例。 –

+3

私はdownvotersの1人ではありませんが、実際のオブジェクトが不変ではないという主張は真実ではありません( 'String'、' BigInteger'、 'Double'、' ImmutableList'、 'new int [0]'、大部分のenum定数、 'Locale'など)。ここでのポイントは、すべてのクラスが不変であるというわけではなく、不変の点がないことではありません。 –

2

単純にその状態を他の場所に置きます。しかし、あなたの状況のた​​めに、それは論理的ですか?あなたはそれ自身に尋ねるべきです。

ConnectionMonitorを変更可能なままにすることをお勧めします。それは接続を「監視」する責任があるため、変更される可能性のある値を追跡する必要があります。それ以外の場合は、であり、その状態を追跡できる別のオブジェクトが必要です。あなたは、

class MonitorManager { 
    Map<ConnectionMonitor, Boolean> connectionStatuses = ...; 
} 

それを簡単にする:

あなたはそれがのConnectionStateだとConnectionMonitorsをマップあなたのモニターのためのコンテナクラスを持つことができます:それは十分に説得力はない場合

は、その後、ここではいくつかの方法がありますこのマネージャーを各モニターに渡して、リスナーがマップにアクセスしてその接続のブール値を変更できるようにします。

class ConnectionMonitor { 
    private MonitorManager manager; 

    //.... 

    private void processConnectionMessageContent(final String messageContent) { 
     if (messageContent.contains("Disconnected")) { 
      logger.warn("Disconnected message received!"); 
      manager.connectionStatuses.put(this, false); 
     } else if (messageContent.contains("Connected")) { 
      logger.info("Connected message received."); 
      manager.connectionStatuses.put(this, true); 
      notifyOnConnect(); 
     } 
    } 
} 

しかし、開発者は、子オブジェクトがそのコンテナについて知ってはならないことを伝えるために並んでいます。データの責任が集まっthatsの接続を監視しながら

だから新しいオブジェクトを作成します。

class MonitorManager { 
    private Map<ConnectionMonitor, MonitoredData> data = ...; 

    public void createMonitor() { 
     MonitoredData data = new MonitoredData(); 
     this.data.put(new ConnectionMonitor(data), data); 
    } 
} 

class ConnectionMonitor inplements MessageConsumer { 
    private MonitoredData data; 

    public ConnectionMonitor(MonitoredData data) { 
     this.data = data; 
    } 

    //... 

    private void processConnectionMessageContent(final String messageContent) { 
     if (messageContent.contains("Disconnected")) { 
      logger.warn("Disconnected message received!"); 
     data.setConnected(false); 
    } else if (messageContent.contains("Connected")) { 
      logger.info("Connected message received."); 
      data.setConnected(true); 
      notifyOnConnect(); 
     } 
    } 
} 

class MonitoredData { 
    private boolean connected; 

    public void setConnected(boolean connected) { 
     this.connected = connected; 
    } 

    public boolean isConnected() { 
     return connected; 
    } 
} 

たぶん詳細をMonitoredDataに希望監視されているオブジェクトでより良いフィット感。より多くの文脈を与えたら助けが簡単になるでしょう。

+0

長い回答と代替案をありがとう。しかし私のスタイルでは、「MonitoredData」のようなセッター/ミューテータを避けようとしています。これまでのところ、不変クラスは値オブジェクトにとってもっと便利だと思いますが、ConnectionMonitorはアイデンティティを持っています。 – Juergen

+0

@Juergen私は、あまりにも多くの行動様式を使用することを理解します。しかし、それを守るためには、 'connect()'や 'disconnect()'が呼び出されたとき(動作によって変更された状態)に 'connected'を設定する必要があります。しかし、この場合、あなたはそのようなメソッドを呼び出すことはできません(もしそうなら、あなたが取るべきルートです)。オブジェクトには振る舞いがありますが、オブジェクトの中にはデータオブジェクト(例えば、 'read'は基本形式のゲッターを表し、' create'や 'add'はある意味ではセッターを表します) –

関連する問題