Say we have a user interface with Forms, Buttons and such. Each item has some properties (such as
Enabled, etc.). Who should check on these properties and decide whenever to render the item or not?
for I := 0 to Count - 1 do if Child[I].Visible then Child[I].Paint
Or each item itself:
if not Self.Visible then Exit else <<PaintSelf>>
Here, Visibility is just an example – same choice arises with many other properties (GUI, application logic, etc.) when some parent needs to do something with its childs.
The answer is obvious in event-driven model, where event issuer could not possibly know about handlers properties. But what about non event-driven model, where there’s a Parent and a list of Childs it manages?
I’m looking for a general guidelines/solution to this case.
At least in OO + event driven interfaces:
- GUI objects, including the window pane itself, have event listeners who are triggered with user actions. Those event listeners call a method which then send messages to other GUI elements (or to themselves).
- But… once a GUI element receives an message (like
myButton.setVisible(false);) it renders itself (obviously lower level calls to the windowing system are made to made this possible).
- So the “controlling/orchestrating” is on behalf of the main windows and delegated to other controsl as well, for example a radio botton
onclickevent listener can send a message to a textbox to set itself invisible.
- The code of actually rendering is inside every item, being each one an instance of a class. Obviusly they are issuing low level calls to a windowing system / rendering engine.
Each item itself
This all should work much alike event-driven system. Parent entity should give orders to its Items, and Items should check with their state to see if and how they can perform that action.
Even if it is as simple as reading 1-2 public properties for the Parent, it is still better for encapsulation to not do so and let Items check their state (which at times can be much more complicated than a single Boolean flag).
TDA principle (Tell-Dont-Ask) backs this up:
Tell-Don’t-Ask is a principle that helps people remember that object-orientation is about bundling data with the functions that operate on that data. It reminds us that rather than asking an object for data and acting on that data, we should instead tell an object what to do. This encourages to move behavior into an object to go with the data.
Visibility is often something you want to aggregate so you can tell a whole group of objects to hide or show themselves together. Even so it’s still best let each object test and paint itself.
I’m a big believer in tell don’t ask. To me the object oriented world is divided into behavior objects that tell you nothing about their state, and data objects that are all about the getters.
Behavior objects have no getters. They are encapsulated. They talk to the world by telling other objects to do things and by outputting.
Data objects have getters. They need to exist when behaviors that depends on this data are scattered among multiple objects and those behaviors can’t be moved into one object.
An object that can paint itself sounds like a behavior object. So how to share visibility?
Group objects that need to share visibility changes together into collections. Tell them to all change visibility together. Events are a common way to do that. The observer pattern can be used with the assumptions I’ve made here but is usually depicted observing dataobjects .