I’ve come up with this list of ways that programmers tend to deal with problems of code duplication.
1) Run with it - hire 1-50 more people so to write and maintain more of the same code. This seems to be a common practice in large corporations. The problem with this solution is that you need to keep increasing the team size over time. Obviously this only works if you don’t care about money, and generally leads to solutions that are mediocre at the very best.
2) Code Generation - Use tools that help you “write less code”. This includes not only official code generation tools but also a number of pseudo “languages”. The visual studio designer tools are good examples of this, as are aspx, asp, a lot of the tools around ado.net such as typed datasets, etc… Microsoft is hyping a lot more of this stuff with vs2005. I’m less familiar with the java world, but there are tools like jsp, struts, etc, that help you create web server code without actually writing java code. The issue here is that the tools inevitably only generate code for specific preconceived uses. At some point you are going to run into a dead end with the functionality provided by generator. Depending on the framework, and your needs, this normally becomes a real headache, and the complexity can rapidly start impacting the rest of the system.
For example, in the vs2003 designer there are certain ways to configure splitter bars that can not be set in the designer, it just doesn’t work that way. After spending most of a morning twiddling designer properties I finally went out to msdn and found about three lines of code that do job. If I had have known that I had to write the code by hand I would have just done the whole thing in code, and it would have taken less time. Unfortunately by that point I had about 400 lines of designer generated garbage in my form. Trying to refactor back to building the thing in code, I was angry to find that the designer stored dozens of hard coded boolean and integer values as binary resources in the manifest.
When using the designer it becomes near impossible to add dynamic behaviors to the ui, which is unfortunate because thats the whole point of creating a rich client interface. Some people say you can override the properties at runtime, but this creates a confusing dicotomy between things that are specified in the designer, and things that are specified in code. At this point you would be better off if you had written the thing by hand, but refactoring is likely to be impractical.
All code generators seem to have similar limitations, but they are extremely popular because a) they make slick oo ahh presentations and b) its easy to fool managers into thinking that because you built a quick working prototype, that the finished application won’t be far behind. Code Generators both encourage and enforce mediocre design. Once you start using one its very difficult to get off of it, and its likely to come with tons of baggage, dependencies and assumptions. My advice is just say no. Anytime your thinking about automating the process of programming something, there
is a solution that makes the automation unnecessary.
3) Reflection - this has a similar effect to code generation, but slightly more sophisticated. You are just hacking the type system and the runtime to let you treat objects in ways they were not intented to be treated. Reflection is used heavily in O/R mapping and serialization, normally to provide easy ways of working with “business objects”. Reflection has a handful of legitimate uses such as dynamic class loading, but using it to manipulate business objects is problematic because event though you make it easy to do a few things like serialization, you are still missing important capabilities, such as creating new types of objects at runtime, validating user input, comparison, etc. Reflection leads to obscure, fragile code that often places implicit or hidden restrictions on how your classes can be implemented. In .NET for example, serializable classes cannot contain circular references. Serializing classes with circular references is always going to be difficult, but reflection hides the problem, where oo techniques make it clear and explicit.
3) Object-oriented programming with Design Patterns - This is your best bet for solving problems of code duplication and/or scattering. If you think you can’t do it with oop, think harder, because there is a good way. For example, lots of the common problems in business software become simple once you decide to implement your domain objects using the composite pattern, such that each object becomes a tree with leaves that represent primitive types such as Text, Boolean, and Integer. Once you create your own type system, adding behaviors such as serialization or object relational mapping becomes relatively simple. The need to subvert the runtime or generate code vanishes because you have acheived a higher level of programming power. You are no longer subject to the whims of tools vendors, in fact, you will find many standard tools unnecessary.
4) AOP - AOP might be useful to you if you have already mastered and implemented the object-oriented techniques. If you don’t understand what you can do with design patterns such as Composite, I think you should stay away from aop, you probably don’t need it. Most of the textbook uses of AOP use it in ways verys similar to reflection; attempting to add functionality to the native type system. This stems, as far as I can tell, from a confusion between native runtime objects and “business objects”. Native runtime objects don’t have enough features to be used as business objects. You need to create a separate class library for that.
Anyhow, I’ve seen a few palatable uses of AOP lately, all connected to development tasks such as testing and debugging. A good testing example was using AOP to test exception handling code. Since you write your code in such a way that it shouldn’t throw exceptions, how do you test to see what happens when an exception does come up? Another good use of aop would be to determine test coverage. I haven’t seen any good uses of AOP in the application itself.
Geez, big post.