Object graph dependency analysis
February 26, 2012 -- How to generate the graph
Generating and reviewing a graph of the object decencies in your app is a super handy way to be aware of your app layout, spot problems in your architecture, and make better decisions when adding new code in the future. With very simple applications, you could certainly imagine the graph in your head easily enough, but as your project grows, you are more likely to run out of the mental space necessary to hold the entire data-set in memory.
For objective-c or c projects, objc_dep is a handy tool to quickly generate the graph for you. The script can be run like so:
python objc_dep /path/to/your/project > projectname.dot
This dot file you generate can be opened in apps like Omnigraffle or the free alternative (not as amazing, but if you’re desperate) Graphviz. Omnigraffle is far superior and has better tools for helping you try to figure out what you’re looking at and arrange it. Once you have the graph, see if it matches your mental model of the project and follow along as I explain how to get some value out of your picture.
- Unused code
Spot
Once you have your graph and look around, the most obvious thing you should be able to spot is entirely unused code. If an object or group of objects in your graph aren’t connected to anything else then you probably found some unused classes that were never deleted.
Another handy tool to use to spot unused code is the application Appcode from Jetbrains. It’s code-inspection tool can point out a number of issues, one of which is the unused code. Do not blindly delete everything it points out though because it has issues with a number of false-positives such as:
- Methods that get used via bindings.
- Methods connected in Interface Builder
- Methods inside #if statements.
- Probably others
With any of these refactorings, you should be using version control and commit before any step. Things like this are exactly what refactoring is for. If you’re using Git, the git bisect
command is super handy here too.
Refactor
First step to reorganizing your code is to ditch all this mess that doesn’t do anything. If you have commented-out code: delete it. If you have unused code or even entire classes: delete them. If you’re using version control like any sane individual would, all of that code will live on in your records. There’s no need to keep old junk around in the present when it is also safely stored in your history. Burn it all down, it’ll be ok.
- How to spot MVC violations
Spot
Correct MVC dependencies look like this:
In this example, solid lines represent direct dependencies while the dotted lines represent indirect dependencies via observation (or bindings if you’re on OS X). You should be able to arrange all items on the graph to clearly show which classes in your project are models, views, and controllers. Your views should only directly depend on your model, your controller should depend on your model and your views, and your models should be dependent on nobody. This is the point of the MVC architecture.
Refactor
Cleaning this part of the graph up can prove to be a simple quick step or something far more complicated.
In the best cases, the dependencies in your graph are simply typos. Unnecessary #include
statements and the sort can generate false-alarms that can make it look like your architecture is more broken than it really is. If this is the case, simply delete the unnecessary statements and celebrate.
Problems get worse when you have more serious problems such as models that depend on your views and/or controllers. Objective-C (and more specifically Cocoa/Cocoa Touch) offer a few mechanisms to help you break these dependencies and spread the MVC love. An explanation for each of these mechanisms can be found in the book Cocoa Design Patterns Eric B. and Donald Y and in Apple’s own documentation (no login required even). A few notable patterns include:
- Chain of Responsibility: Views don’t need to know or care about what controller is going to respond to the actions they send, simply that someone in the chain of responsibility will answer the message. Thus eliminating dependencies of views upon controllers.
- Observer: Views can observer controllers or models or the global notification center rather than directly depending upon a given controller.
- Conclusion:
Generating and analyzing your object graph can help to ensure that the awesome architecture you imagined in your head is actually reflected in your code. It can also help you and/or your team discuss how to improve the architecture of your code and make a plan for the future. Nothing is ever perfect, certainly not on the first try. The point is to get better and use tools like this to get closer.