2

I'm programming a graph object which can be in either a mutable or immutable state. In both states, it has a get method, but only in the mutable state is the set method allowed. The transition occurs when one invokes a method compress which optimizes the storage of the graph.

How do I deal with the fact that some methods are legal in some states but not others?

I can think of two approaches. The blunt way is to use the state pattern. If you invoke set when the graph is mutable, it executes as normally; but, if you invoke set when the graph is immutable, an error is thrown. This looks ugly to me because an immutable object still has a set method.

Alternatively, I can make a graph type with a mutable_graph sub-type; only the second one has a set method. The state change is effected by changing the type of the graph. However, this also looks ugly to me; for every implementation of graph (of which there are several), I now have to make two types instead of just one.

EDIT: I'm using the state pattern and throwing an exception; if neither solution is especially preferable, this one involves writing the least code. Thanks for the advice!

3
  • 2
    You have three choices: (1) Use the state pattern (2) Dynamically change the type of the object (some languages can do this) (3) Use immutable objects and return a new instance on each state-changing operation. All of them suck. What you should choose depends on your language's culture (e.g., the choice is obvious in Java or Haskell).
    – amon
    Jan 27, 2014 at 23:22
  • I hadn't thought of (3), but I think that the objects contain too much internal data to make this practicable. If they're all bad then I'll go with (1). Jan 27, 2014 at 23:26
  • Why can't you just query the object for mutability? if (graph.IsInMutableState) { graph.Set(...); } else { /* ... */ } Jan 28, 2014 at 6:34

4 Answers 4

2

Assuming compiled language from the Java/C#/C++ family.

Do you know if the graph is mutable or not at compile time? If yes - introduce a subtype (C++ also has "const" for objects). Otherwise - throw an exception and the state pattern.

3

If you really don't want to leak details of "privileged" operations then the cleanest answer in popular OO languages like Java and .NET is multiple interface inheritance.

One that I've used semi-frequently goes something like:

  • IFooRepository - contains methods GetFoo, DeleteFoo, SaveFoo
  • IReadOnlyFooRepository - contains only method GetFoo

In the example above, IFooRepository might subclass IReadOnlyFooRepository. Also, if there is a well-defined way to transition between the read-only and writable state, then you might put an Edit method on the IReadOnlyFooRepository that returns an instance of IFooRepository, and/or an AsReadOnly method on the IFooRepository that returns an instance of IReadOnlyFooRepository.

This is not without precedent, at least in .NET. One of the most common interfaces, IList<T>, has a method AsReadOnly that returns a ReadOnlyCollection<T>, which as you might guess, does not expose any kind of write operations.

It's not hard to implement - generally you only need one class to implement both interfaces, and the Edit/AsReadOnly methods can just return this (or self or whatever it is in your chosen language). Of course, if you do this, don't fool yourself into thinking that it's a form of security, as a caller could potentially just cast between types - but assuming you just want to protect against stupid mistakes rather and not intentional abuse, it's good enough.

You can get more granular if you want - for example, you don't necessarily need one interface to derive from the other, you could have IFooReader and IFooWriter with no methods in common. Or you might define an IFooComponent (or something along those lines) at the root if you only want to share a few methods, but have most of them be unique. Sky's the limit, but the basic idea stands - use interfaces to carve out specific blocks of functionality that are specific to a particular state.

This takes a lot more design work and adds a lot more types to the code if your states and transitions are complex, so be sure that it's really worth the effort. It's perfectly fine and in fact common in a lot of architectures (like most MVVM frameworks) to just throw an exception if an operation isn't valid for the current state, and also provide convenience "guard" methods like CanWrite, to avoid strewing try-catch loops all over the place. That's good if you have 2 or 3 discrete states; if you have 10 or 20 then you might want to think about either a state pattern or a design such as the above that makes the transitions more explicit.

1

Have you considered a Builder pattern? The user starts with a FooGraphBuilder, with getters and setters. Calling compress() returns an immutable FooGraph, making the transition fairly clear.

1

I would prefer to throw an exception, because it is:

  • easy to understand
  • easy to implement (and read)
  • easy to test
  • Maybe it looks not to perfect, but the best design is still "As Simple as Possible, as Complex as Necessary"! And one of the most implemented anti-designs is "over engineered and too complicate without a business value"!

E.g. in java the interface List describes multiple methods that could throw exception too (because they have not to be implemented a implementation) - but otherwise (with multiple interfaces for each case) the List interfaces would be over engineered and would definitely be very complicated..

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.