4

I started working as a iOS developer about a year and a half ago, and I'm having some trouble with software architecture and organization. I use Apple's recommended Model-View-Controller paradigm, and my code is generally very hierarchical: if a screen has (for example) a HUD, a control panel, and a display area, I have a main controller for the screen and sub-controllers for the HUD, control panel, and display area. The sub-controllers generally have no knowledge of their neighboring controllers and use methods in the main controller to interact with them.

However, especially in games, I often run into hierarchy-breaking problems that just can't be elegantly solved with this model. For instance, let's say I have a coin in the control panel area that I want to animate flying to the HUD. I can either animate the original coin to the new position, which would require a method like animateCoinToPosition: in the control panel sub-controller and a method like getPositionForFinalCoinPositionInHUD in the main controller; or, I can hide the original coin and create a duplicate coin either in the main controller or the HUD controller, which would require a delegate method like animateCoinToHUDFromStartingPosition:. I don't like having such oddly-specific methods in my controllers, since they only really exist to solve one problem, and additionally expose the hierarchy. My ideal solution would be to have a single method called animateCoinToHUD, but this would require flattening the entire hierarchy and merging the three controllers into one, which is obviously not worth it. (Or giving the sub-controllers access to their siblings — but that would essentially have the same effect. The sub-controllers would then have dependencies with each other, creating a single messy spiderweb controller instead of a main controller and three mostly independent sub-controllers.)

And it often gets worse. What if I want to display a full-screen animation or particle effect when moving the coin? What if my coin is a lot more complicated than a simple sprite, with many subviews and details, to the point where creating a duplicate coin using animateCoinToHUDFromStartingPosition: is inefficient? What if the coin flies to the HUD but then returns to the control panel? Do I "lend" the coin view to the main controller and then take it back when the animation completes, preserving the original position/z-order/etc. in temporary variables so that they can be restored? And another thing: logically, code that concerns multiple sub-controllers belongs in the main controller, but if these interactions are common, the main controller grows to be many thousands of lines long — which I've seen happen in many projects, not just my own.

Is there a consistent way to handle these hierarchy-breaking effects and actions that don't require duplicate code or assets, don't bloat my controllers, and elegantly allow me to share objects between sub-controllers? Or am I using the wrong approach entirely?

Alex
  • 21,273
  • 10
  • 61
  • 73
Archagon
  • 2,470
  • 2
  • 25
  • 38
  • That's why we need a whiteboard :) – A-Live Sep 06 '12 at 23:41
  • I've read your question a few times now over the last couple of days but not really grasping a few things. Particularly what role does your 'root' view controller have in all this? It sounds like you're trying to insulate it from knowing about the hierarchy as well, or am I wrong on that? – Carl Veazey Sep 09 '12 at 21:18
  • No -- as per Apple's guidelines, the parent controller has direct access to all of its sub-controllers, but not vice versa. The sub-controllers usually know about their parent controller through a delegate, which provides a small number of methods that the parent controller has to implement. The sub-controllers can only interact with their sibling controllers by going through the parent controller. – Archagon Sep 10 '12 at 16:57

1 Answers1

1

So, I think you may be thinking about the "never go up" the hierarchy a little too literally.

I think the idea is that you don't know specifically what the parent is, but you can define a protocol and know that whatever your parent object is it responds to said protocol. You ideally test in code to confirm that it responds to that protocol. Then use the protocol to send the message in a generic way wherein you pass the coin object to the parent object and let the parent object animate it off the screen and into the HUD.

The sub-controllers then have a id<parent_protocol> parent; instance variable and their initialization method takes one of those as a parameter. Given your description you already have something like this in place, or at least enough to implement "sub-controllers generally have no knowledge of their neighboring controllers and use methods in the main controller to interact with them" as you say.

So the idea, from a design perspective is that the coin pickup happens in the Display panel and all it knows is that it's parent object has a pickupCoin: method that will do whatever is appropriate with a picked up coin. The Display panel doesn't know it goes to the HUD, or whatever, just that picked up coins get handled by the parent controller's pickupCoin: method.

The OOP design philosophy here is that all knowledge of the parent is encapsulated in the protocol definition. This makes the child & parent more loosely coupled so that you could swap in any parent that implemented that protocol and the children would still work fine.

There are looser couplings you could use (globally posted notifications say), but in the cases you describe I think something like what I've outlined is probably more appropriate & likely more performant.

does that help?

Dad
  • 6,388
  • 2
  • 28
  • 34