Skip to main content

Where's the state?

Posted by timboudreau on August 7, 2008 at 1:04 PM PDT

Where's the state? This is a small but useful question when
deciding how a problem domain gets carved up into objects:
What things have state? What things have values that
can change? When and how can they change? Can the changes be
observed? Who needs to observe changes in state?

These questions make a good start for figuring out how to carve
up a problem domain into objects, if you observe the principle
keep all related state in one place. I would go so far as to say
that the majority of the appeal of Model-View-Controller (MVC)
architecture is that it encourages you to keep state in one place.

One big reason why statefulness matters is threading.
In practice, it is common to see designs where threading was an
afterthought. This is
to be expected. Concurrency is a not natural way for human
beings to think. So commonly a library is designed with
no thought about threading;
then a problem shows up which multi-threading can solve.
Threading ends up being introduced into a codebase that was never
designed for it. Retrofitting a threading model on something designed
without one is very painful and sometimes impossible.

Some threading models can be simple. For example, any time you are
writing a Swing application, and you have a method that can do long
running I/O, the first line of that method should be
assert !EventQueue.isDispatchThread();
under all circumstances.

But keeping I/O off the event thread is the trivial case. Managing
mutations to a model inside an application
is a more complicated, less obvious problem.

IMG_2949.png

Any time you find yourself writing a setter (or more generally,
mutator) method, it is worth asking yourself
Who does this piece of state really belong to? Am I putting
state in the right place?
Blindly following the beans pattern
(add a getter/setter to whatever type you happen to be editing)
can result in state being distributed all over objects in an
application. The result will be harder to maintain, and if it
has any lifespan, less and less maintainable
over time. Like entropy, or the
no
broken windows theory
, the more state is distributed all
over the place, the more that will masquerade as design and
make it okay for a programmer to further diffuse state throughout
objects in the application. Human beings can only
hold so many
things in their minds
at a time. The more state is diffused,
the more things someone must pay attention to simultaneously to
solve a problem in that code.

Another question above was
Who needs to observe changes in state? There are some
corollaries: Where should those changes be published?
and Does the thing observing the change need a reference to
the thing that changed?

Let's return to the concept of having a model for a minute. A
model gets all related state in one place quite well. It does
nothing for threading issues — these still have to be handled
somehow within the model, or appropriate use of the model in a
multi-threaded environment must be documented. There is a
thing you can do at this juncture that can make life much
easier — and it is all about how the code that uses the
model is designed
. If you're creating an API, there is probably
more to it than just a data model. There is also how the model
is accessed.

Essentially what you can do is this: If you design the
rest of your API so that the only way to get a reference
to the model is to be passed one (i.e. “don't call us,
we'll call you”), then you know the
context (particularly, on what thread, while holding which locks)
in which your code is going to be called.

I'll pick a class at random as a case in point:
SwingWorker.
While useful, it does represent poor
design in a couple of ways:

  • It mixes the concerns of task state (progress, title)
    and performing a task and
    the outcome of that task in a single class.
  • It uses the beans
    pattern in something that is obviously not a JavaBean.

saltlake.png

I mentioned mixing concerns: Avoiding mixing concerns is not just
an academic issue of idealized architectural purity. APIs are
things human beings use. If you avoid mixing concerns in
a public class, you can name things more clearly, and the result
will be easier to understand and need less documentation (which
most people won't read unless they have to anyway).

In a perfect world,
a thing representing a task should probably be stateless
(subclasses can have state if that makes sense for them - but no
setters or getters or mutable state in the interface/superclass).
If the code that
does the work is passed a reference to a model object it can set
task state on, that untangles the task-state concern from it.

It also
removes a bit of psychological weirdness: Someone who is writing a
SwingWorker subclass being is required to think to
themselves
“Now I have to tell
myself what my state is.”
I suspect many developers
anthropomorphize classes they are writing — they think of
the class they are editing, or the program counter, in the first person,
as I, myself. I don't have empirical evidence for this, but I know
I do it, and I have heard many other developers who were discussing
code do it - if there's someone out there who
emphatically does not do anything of this sort, I'd be
interested to know about your thinking style when coding
- please speak up.

Detangling the other concern is
as simple as returning a java.util.concurrent.Future
from the method that enqueues a task.
The result would look something like this:

public interface Task<T> {
    public T runInBackground (TaskStatus status);
    public void runInForeground (TaskStatus status, T backgroundResult);
}

public interface TaskStatus {
    public void setTitle (String title);
    public void setProgress (String msg, long progress, long min, long max);
    public void setProgress (String msg); //indeterminate mode
    public void done();
    public void failed (Exception e);
}
       

and then perhaps you would have a concrete TaskRunner class that runs
tasks

public final class TaskRunner {
    public <T> java.util.concurrent.Future<T> launch (Task<T> task, TaskStatus statusUI) { ... }
}
       

or, perhaps preferably, Task is an abstract class with a static
launch(Task) method (or instance method - this is a matter of
taste) and the TaskRunner class is an implementation
detail. (Further refinements would be to look up an injected
implementation of TaskRunner, and/or a factory for TaskStatuses
on the classpath, etc., not to mention making TaskStatus a final
class that delegates, but that's getting out of scope...)

What's the difference between this and SwingWorker?
First, we've detangled all of the concerns
and made them explicit. While sometimes wadding up a bunch of
related concerns may seem like it's doing someone a favor, consider that
you really need to read the documentation to figure out what a
SwingWorker does. While the above design is simpler, you could
probably figure out how to use the above API without
reading a line of documentation and get it right.

More importantly, by passing in a
TaskStatus instance, we've encouraged healthy client code — no
code can get a reference to the passed
TaskStatus until the Task
is actually being run, and no code can get a reference to the
passed TaskStatus unless the client actually wants it to.
Contrast this with
SwingWorker, which (!!!) allows foreign code to call

firePropertyChange()
and which can set its
status to "done" or "started"
inside its constructor! We haven't
guaranteed correct behavior (a client could pass a
reference to a TaskStatus to some object
that will try to do
something evil with it on another thread, although implementations
could defend against this).
But we have strongly
encouraged proper usage
by designing in such a way that the TaskStatus is only
made available within the scope where it is actually used.
In addition, since
we're the one passing in the reference to the TaskStatus
instance, we can much more safely assume what thread will call the
TaskStatus instance, and what locks are held when it
does. While deadlocks will surely still be possible, we're doing
as much as possible to discourage them on our side.

One of our questions above was Does the thing observing the
change need a reference to the thing that changed?
In this case,
absolutely not. In fact, to make the Task object
available could only do harm.
There is no reason for Task's run*() methods
to be exposed to code that are interested in the status
of a
Task,
not the Task itself. It causes harm not only because
status-reporting code could potentially invoke run*()
methods, but because, to the programmer who is
interested in the status of a Task,
the run* methods are simply
noise. Most developers learn APIs through code-completion in
their IDE; mixing concerns just makes it harder to figure out what
is and is not relevant.

You'll notice that we've completely removed use of the
listener pattern here. The listener pattern is seriously overrated,
but is often used by default because developers have seen it in the
Java core libraries — we use what we know. The listener pattern
has a number of problems. If you've ever had to debug a Swing
application where you have a severe bug that only occurs after you
switch Windows from Classic to XP appearance or vice versa, then
you know this pain — I know I do. What is happening is:

  • Before the look
    and feel change, the look and feel's listeners on components get
    called before the application's listeners
  • After the change they
    get called after the application's listeners

Some code is
unknowingly depending on a side effect of the look and feel's reaction
to a change — in particular, it is depending on that
side effect to happen before the application's
listener is called, and exceptions
get thrown.
While rare, the listener pattern is prone to
ordering bugs which are quite painful to reproduce and fix.
Additionally, in my experience, listeners are the most
potent source of memory leaks in Swing applications. So it is
a pattern to avoid unless you know you really need it.

Especially when you have a situation where it is very
unlikely ever to have
more than one listener, the listener pattern is probably
not a good choice. Listeners monitor changes in state.
It is not good design to assume that multiple things will want
to monitor the status of a Task without any
evidence to back up that assumption
If needed, it
is easy enough to provide a ProxyTaskStatus (and note
here the "listeners" are provided as a constructor
argument - any ordering issues are those of the caller)

public final class ProxyTaskStatus {
    private final TaskStatus[] statii;
    ProxyTaskStatus (TaskStatus... statii) {
        this.statii = statii;
    }
    public void setTitle (String title) {
        for (TaskStatus status : statii) {
            status.setTitle (title);
        }
    }
    //...
}
       

if that's really a need; or leave that up to someone implementing
TaskStatus (it can always be added to the API later
if there are sufficient use-cases).

What we have, instead of the listener pattern, is a situation where
we're passing in the model object (in this case, the thing
that holds state about status), as opposed to having the state
belong to the task itself and having other code listen for changes
in the state. Most likely that model object writes directly
to a UI showing task progress.
I believe I've read of this pattern
being called don't call us, we'll call you somewhere (if
anyone can point me to where I ran across that usage, let me know
so I can give credit where it's due).

To get back to threading for a minute: There are three ways I
typically see threading + statefulness dealt with in code:


  • Close your eyes and pray


    i.e. do nothing with regards to threading and hope there is
    never a problem (or better, document that the code is intended
    to be single-threaded and the caller needs
    to enforce correct threading behavior because you don't)
  • Synchronize everything

    make deadlocks (you hope) somebody else's problem — you pay
    a price with synchronization — even if not so much with
    performance, with maintainability

  • Enforce an actual threading model

This last item brings me to my main point here:

The combination of keep all related state in one
place
plus don't call us, we'll call you
creates the possibility of encouraging and even
actually enforcing a
threading model over a complex data model,

without
(generally) having to document lots of rules about
threading and pray that people obey them. A weakly enforced
example of this from the JDK is Document.render(), where you
pass a Runnable that is guaranteed to block any
other threads writes until the Runnable has
exited (it is weakly enforced because you can read the
document's content outside of this method —
and there is much code out there that does so).


snail.png

Lastly, for anybody about to scream that I have misrepresented
SwingWorker here (it has lots of complexity about batching
up interim results and so forth, and the above is a
simplification), I'll mention that

  1. I
    don't mean to ding the authors of it - they were
    being consistent with
    the library they were working in.
    Nonetheless,
    one could do all of those things with the above
    general design, and have a much simpler, easier-to-use
    result.
  2. I could have chosen many APIs to pick on here. I
    work for Sun. It seems more polite to call your own baby
    ugly than call someone else's baby ugly :-)
  3. Patterns are easier to abstract than anti-patterns.
    Anti-patterns tend only to become really visible after
    code containing them has been in production for a while.
    There's lots of things we know now about what not
    to do that simply weren't known a decade ago.

Related Topics >>

Comments

I'm assuming for low-level existing components, that you do wrap them to provide higher-level functionality. My point is more general: That anything you can do with the listener pattern you can probably also do with the pattern of watching a "space" for some object to enter or exit it, and that the result can often be cleaner code. A typical pattern in Swing applications is to have multiple things implementing nearly identical listener logic; better to have a thin layer that provides the right abstraction, and then observe for the presence or absence of that abstraction. But you *could* do that with Document - for example, observing for a DocumentChanges object to appear. As I said, I'm not sure this is generalizable to all problems - the point here is to explore the idea. There are definite problems with the listener pattern; the question is, are there better patterns? For some issues there definitely are. How far these other patterns can be generalized is an open question.

@timboudreau: In the case of the save() example, doesn't that mean the Document basically knows the sum total of operations that can be performed on it? A DocumentListener tells you when something has happened *to* the document, not what you can do to it as a result. Adding new operations would require modifying whatever it is that provides the operation cookie (to use the NB term), right?

To make myself clearer: I suspect that anything you can express with a listener interface (where often the first line in a handler is to get and cast the source), you can also express as the arrival or disappearance of some known type. You might need more types to do it, but eliminating the layer of indirection that is listeners removes a lot of complexity for a client. But it also depends on the richness of the source object, so I'm sure there are exceptions - and it may not apply for very general-purpose components. But, say you are writing an editor. Something needs to listen when the document is modified, to enable the Save action. Would you rather: A. Attach a DocumentListener to the document, enable the Save action on modification, remember state for multiple editor tabs, and disable it again on save, or B. Subscribe to the presence or absence a of public interface Saveable { public void save(); } owned by the document? B. results in cleaner code, and anything else that needs such state changes (say, appending a * to the tab name) is also much less code. In the case of javax.swing.Document as it is, you do need to do that code once, but it only needs to be done once.

> The subject needs to export an interface to the observer, the observer needs to implement > the interface of the observee. That's a very tight-coupled design, which may work when > applied horizontally within a class library but not really vertically between components, > pulling in dependencies from left and right. Well, true, you need something like a String or other core JDK class if you want to integrate truly heterogenous things; at the same time, at some level, something has to actually know what to *do* about a given event - so some level of coupling is there, like it or not - even if the coupling is based on text parsing - a text format or an event ID is still API. In which case, why not use the type system to our advantage wherever possible? I.e. a generified callback system where you express interest in a particular type and get passed one or more instances of that type. I don't really see this as having much to do with Matisse's guarded blue blocks in the editor - that's simply due to the fact that it's provably impossible to make a bidirectional editor for code that might have state. Consider the very simple class Foo extends JPanel { Foo (boolean bar) { if (bar) add (new JTree()) else add (new JTable()); } } Goofy example, but what should a drag and drop editor render in that case? It *is* possible to do the reverse engineering in a lot of cases and come up with something reasonable, but it can never be 100%, and the potential to really hose up someone's code trying to modify it programmatically under those circumstances is too high. It's not about being deemed unworthy, it's about the computer being unable to guess the intentions behind a complex piece of hand-written UI code sufficiently to provably modify it safely. But that's getting us off topic.

IMHO this is the best blog entry in java.net in the last few months. A great piece of advice indeed. Congratulations, Tim, and thank you very much for it!

BTW: A side-effect of the vurrent interface based approach is that you have to really think hard about your callback design, do you place them all into on interface or define one for each. If you wrap them all up in one as ie. java.awt.event.MouseListener does, but is only really interested in one, i.e. mouseClicked(MouseEvent e), then now you start requiring adaptor classes and such. I do believe all of this is the primary reason why true round-trip engineering in NetBeans (i.e. Matisse design vs. hand-coding) just becomes too much indirection for it to handle, so if you use Matisse to create event handling, you will be locked down and deemed unworthy to edit certain parts.

Tim, yeah it's not that I am advocating we go back to a message loop as in C as much as trying to understand alternatives to the observer we may use instead. Interesting point btw, how a large switch is reminiscent of a missing type i.e. the typical call for polymorphic refactoring. I admittedly had not made that link. However, I also think you can go too far with the typing and that is my primary issue with the observer pattern in Java/Swing. Since we have to rely on an interface rather than method signature callbacks (think type safe C method pointers) we have created a bit of a chicken-and-egg problem. The subject needs to export an interface to the observer, the observer needs to implement the interface of the observee. That's a very tight-coupled design, which may work when applied horizontally within a class library but not really vertically between components, pulling in dependencies from left and right. I tend to think of it in parallel to electronics and integrated circuits. In Java as it is today you can only insert an IC of the exact same type (say Pentium 3's) where the contrast would be having the ability to insert ANY IC which you have pin compatibility with. It may not always make sense (Pentium 2 has pin compatibility, but not electrical compatibility) but what it buys you is more leeway to hook up things you did not originally envision, i.e. a Pentium 4, without actually requiring a new contract (interface). Concrete example: If your logging component can be called with a String as an argument, and your application can register callbacks with String as an argument (say originally envisioned for status bars) then you can wire them together - without requiring a (non-versionable) middle man to facilitate the contract (interface). I expect this to be possible when BGGA get's accepted into the language.

BTW, it's not so much that I don't draw a sharp line between application design and UI design (in the sense of UI *code* design, not graphic design); but that anyone should always code as if one were creating an API because whether they know it or not, they probably are. Even if it's just for their personal reuse, if a library is improved, it's valuable to be able to drop a new JAR into an old application, and you only get that if you keep backward compatibility. Design choices at the beginning have a big impact on how easy or hard it is to keep compatibility.

Karakfa: Thanks! MrMorris: Yes, it is burned into Swing; that doesn't mean everything new should have it burned into it as well. BTW, NetBeans' internal context handling mechanism (the Lookup returned from Utilities.actionsGlobalContext()) *is* more or less a type-safe event bus; just that the "events" in question are changes in the presence/absence/identity of a given type, and you subscribe to changes in the presence/absence/identity of that type in a given Lookup (roughly comparable to slot?). Figure one of the main interesting things about Java is its type system. If you're going to do a sort of global event-bus type thing, you might as well make it type-safe and leverage the type system as much as possible - why have either giant switch statements (an interesting way to look at it is that a switch statement is an indication of a missing type), or alternatively, myriad listener interfaces that add a layer of indirection?

> it is easy for someone to depend on a side effect of listener order and not realize it until the code has been in production for years. Sure, but this is the fundamental event mechanism burned into Swing we have to deal with. If your application revolves around multiple documents, one typically let the model implement SingleSelectionModel such that it can be used directly in a tabbed pane or similar view. You'd typically also extend AbstractBean and other models, making it impossible to avoid this multicasting aspect. I can't help but wonder, with what we've seen over the last years with the Singleton, whether next go-round of pattern bashing belongs to the observer.

> If you rely on a pattern as pervasively as Swing does, it's prudent you understand and respect its laws. It's not so much understanding and respecting its laws, but that it is easy for someone to depend on a side effect of listener order and not realize it until the code has been in production for years. I have no problem with a single observer; I have a problem with the blind assumption that multicast should be the default. I don't have a particular issue with event bus designs if they are type-safe. I'd say the biggest difficulty with Swing is that, while its model-driven design may be useful for its architects, in fact, distributing state across tons of models for tons of components is tantamount to having no model at all. Wicket's component model gets this a little better - there is one model type for all components, that model represents exactly one object, and child components can proxy or adapter the model of the parent if they have none of their own. But that's fodder for another post... -Tim

With "listener pattern" I assume you mean the GoF observer pattern. If so, your L&F example sounds more like a design issue with Swing L&F hooks more than a problem with the observer pattern, which clearly states, you cannot assume anything about calling order. If you rely on a pattern as pervasively as Swing does, it's prudent you understand and respect its laws. I can only guess that you are no fan of event-bus/blackboard/signal-and-slot mechanisms which to me would appear to solve many problems and requires far less complicated (thus error-prone) MVC/MVP design which few people can truly master - probably one of the core reasons why Swing is difficult and for all practical purposes, turned out to be an underachiever in the desktop space. The last statement is not meant to bash Sun or Swing authors, but empirical evidence and the introduction of Beans-Binding/JSR-295, which cultivates an implicit wiring model, would suggest this to be the case. Unless of course you draw a sharp line between API design and application UI design, but it would appear you don't.

Hello, Tim. Great article. Ceparation of concerns is one of the fundamental programming best practices. But Java developers often tend to be extreme and do not learn or forget about these practices. Steve McConnel in his book "Code Complete" asks a question: "After you have read Introduction to Java, Advanced Java, and Advanced Advanced Java, what book do you read to learn more about programming?" If you will look on Microsoft's developer resources, for example, you'll see MSDN, Architecture Journal, Patterns and Practices, etc. I see the lack of such resources from Sun. The SDN covers basically technical but not fundamental approaches and Maksym.