Given a fairly complex object with lots of state, is there a pattern for exposing different functionality depending on that state?
For a concrete example, imagine a Printer
object.
Initially, the object's interface lets you query the printer's capabilities, change settings like paper orientation, and start a print job.
Once you start a print job, you can still query, but you can't start another job or change certain printer settings. You can start a page.
Once you start a page, you can issue actual text and graphics commands. You can "finish" the page. You cannot have two pages open at once.
Some printer settings can be changed only between pages.
One idea is to have one Printer
object with a large number of methods. If you call a method at an inappropriate time (e.g., try to change the paper orientation in the middle of a page), the call would fail. Perhaps, if you skipped ahead in the sequence and start issuing graphics calls, the Printer
object could implicitly call the StartJob()
and StartPage()
methods as needed. The main drawback with this approach is that it isn't very easy for the caller. The interface could be overwhelming, and sequence requirements aren't very obvious.
Another idea is to break things up into separate objects: Printer
, PrintJob
, and Page
. The Printer
object exposes the query methods and a StartJob()
method. StartJob()
returns a PrintJob
object that has Abort()
, StartPage()
, and methods for changing just the changeable settings. StartPage()
returns a Page
object that offers an interface for making the actual graphics calls. The drawback here is one of mechanics. How do you expose the interface of an object without surrendering control of that object's lifetime? If I give the caller a pointer to a Page
, I don't want them to delete
it, and I can't give them another one until they return the first.
Don't get too hung up on the printing example. I'm looking for the general question of how to present different interfaces based on the object's state.