Immutability versus state change in class

I am new to the concept of immutable classes. Consider this class:

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(); } } } 

I am trying to understand how this class can be changed to immutable.

In particular, I do not see how the isConnected boolean field can be made final, since it represents the state of the connection.

All ConnectionMonitor clients should simply request isConnected() to get the connection status.

I know that locking changes on isConnected is possible either using an atomic boolean.

But I don’t see how to rewrite this in an immutable class.

+7
java immutability design oop
source share
3 answers

The ideal is to minimize variability - not to rule it out.

Immutable objects have several advantages. They are simple, thread safe and free to use.

However, sometimes we need variability.

In Effective Java, Joshua Bloch offers the following recommendations:

  • Classes should be unchanged if there is no good reason for changing them.
  • If a class cannot be made immutable, limit its variability as much as possible.

In your example, there is a very good reason for class instances that can be modified. But you can also see the second guide in the game: the monitorObject field monitorObject marked final.

+6
source share

Simply put, this is another place. But is this logical for your situation? You should ask yourself that.

It might be better to leave ConnectionMonitor mutable. He is responsible for “monitoring” the connection, so he is required to keep track of values ​​that may change. Otherwise, you will need another object that you can modify to track this state.

If this is not convincing enough, then here are a few ways:

You may have a container class for your monitors that maps ConnectionMonitors to it ConnectionState:

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

To make this simple, you can transfer this manager to each monitor, allowing the listener to access the map and change the logical value for this connection:

 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(); } } } 

But some developers have lined up to let you know that children do not need to know about their containers.

Create a new object that is responsible for the data collected during connection monitoring:

 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; } } 

The details in MonitoredData may be better suited for the object being monitored. It would be easier to help if you provided more context.

+2
source share

There is no real object of life (if you do not consider classes for one purpose) outside the textbook or the paradigm of zealot cellars is 100% unchanged or stateless. Those who preach immutability at exhibitions every time they get a chance are usually just like the sound of their voice. There is always a certain degree of variability, but the key here does it in a safe and non-violent manner, and does not mutate objects just because. If you can explain why a certain part of an object should be volatile, and you know all the possible side effects before hand, it is perfectly normal if it is volatile. As you said, the logical isConnected must have a setter, getter, or some other mechanism to reflect the current state of the connection. And since it is basically a binary switch that has only two states, the effect of its variability is minimal. You can achieve the same result in some other way, but you have to go through the hoops and loops to achieve this using the outsider method from another class.

TL; DR: The motivated state is not evil itself, but it can be used incorrectly to cause great evil.

-one
source share

All Articles