Thursday, August 30, 2012

My new Development Pattern and why it works


I’m generally not one in the past who has studied other’s development patterns, so I generally just go with whatever works best for me.  I found not real long ago, that doesn’t really work… So, I did actually put some thought into how I worked on my projects.  This has been heavily exercised in the restructure of RuneEngine V2 over the past year.  The code base is the most organized and the most stable, but also the largest it has ever been.  Therefore, I am confident this is working well for me.  So, I decided to share some details in my new design process towards development.

Ok, I’ve always done this, but I’ve taken it more seriously.  Whether I write it down or just visualize it, or even go ahead and set it up in Visual studio.  The first thing I do is define objects that I need in a given area.  I also ensure that I know an approximation of what members I need.  Like I said, I’ve always done this; this is a pretty standard starting point for planning phases.  

The next part is where I determine which methods are used commonly across the objects and/or if there are methods/properties that may come in handy across other parts of the project.  Then, I place all of these common methods and properties in groups and apply them to interfaces accordingly.  Ideally, the first code I write is at this point.  If you’re not overly fond of interfaces and/or believe that they aren’t a necessary part of development you may want to re-evaluate that statement and review MSDN and observe the inheritance trees of .NET objects.  I would agree they’re not required, but when you practice this way you get a few amazing advantages you may not have thought of.

First of all, one example is extension methods for interfaces.  These are a great addition to an interface driven design pattern.  You can define behaviors with these interfaces and avoid rewriting the same logic in every implemented object.  This is more appropriate than inheritance; because it is flat there is no call stack for this.  So, this does become an optimization later.  One major con is that interface casting is expensive, you can control how much time you spend casting interfaces in many ways, so this is not overly an issue.  An example of a major pro would be extensibility of your code base.  Along with the extension methods, other methods that are geared to accept the interfaces and not objects can act on any object that implements them.  This gives phenomenal ability to add whatever you need to adhering to the rules in the interface, and all of your code base will support it.  This is my purpose in designing this way.  One issue that you run into that is not really a pro or a con is that you do have to define everything, every time.  So, it does take more work to add new objects than deep inheritance trees and abstract objects, however this does promote flatter code overall, so again it runs faster.

As I am designing the interfaces, I put a lot of thought into a few things.  Will it return an error code or throw exception?  Do these methods require specific implementation that is not optional?  Can I confine them into fewer methods?  Should this be publically accessed?  These are good questions to ask because they guide the process into making useful interfaces that are not too tedious and remain useful.  It is easy to make an interface that is a total waste.  The most useful interfaces in RuneEngine V2 are the IContentReference and derived interfaces.  The reason these are so useful is because they define rules that allow the RenderManager to use them without the need of any further information about the content.  IMeshContentReference for example, is used for custom geometry, models, animated models, and will be used for terrain as well.  One interface can handle all types of rendering.  To make it even more useful, this interface can be defined by a 3rd party and game specific code, and guess what?  Now, that custom rendering logic is available to the RenderManager as well.  

After I do all of this and start rolling with the project and ensuring that my interfaces work in their desired environments and the objects I’m building can get their full behavior from this set up.  I go through again and make sure the interfaces don’t have any methods that across the board aren’t used.  The IMeshContentReference did have a few methods that weren’t being used by the RenderManager or the other objects so they were removed.  The objects that relied on them kept these methods, but the interface does not define them so they became object specific.

The last big key to the puzzle is actually one of the more important, that is done from the beginning, but mentioned last for its importance.  CHUNK IT, do not try to list out every interface and all of its methods from the beginning.  Build in chunks.  This gives you time to focus on each individual area closely.  It also allows you to overtime realize a more appropriate location for existing and new interfaces/classes.  I avoid moving things in too big of a hurry, but I’d rather do it now than later.  The longer you wait the more work it will require to do.  I generally define a chunk as a new feature or system.  Anytime I create a new DLL I tried to handle its major features right off the bat. 

A good example of how I work in chunks is this.  When I was starting the restructure of the content system, I knew I wanted to work in ContentReferences as before, but they were going to become interfaces.  So, that was an immediate.  I also, knew already from prior that the ContentReferences did require some uniqueness so I created the Mesh, Material and Texture reference interfaces.  These were the most important and I believed there was a chance I could confine all my rendering code to use them exclusively (I was right).  I didn’t start working on the implementation of any of the reference object implementations until after I had developed them into the RenderManager and the RuneContentManager objects.  I had to confirm their use and entry, what methods they would require before every implementing a single object.  I did find in implementing the objects that the methods required a little tweaking, but there was little to no change during this stage.

The more general stance on this is as follows, start with a list.  Use the list to define rules in the form of an interface.  Start the implementation at the most system level objects that use the interfaces as parameters.  Ensure the interfaces will work then implement the objects.  Make minor changes if necessary as the objects are implemented.  Only work in chunks of implementation as opposed to the entire code base at once.  If an object doesn’t apply in any of these systems and uses no methods that would be required in other objects, there is no purpose for an interface.  Basically, if you define IContentShaderReference when ContentShaderReference is the one and only object that will use it.  Well get rid of the interface.  IContentReference should be enough.  However, if there is any chance IContentShaderReference could be used in another case.  Yes I would include it.  Only exclude an interface definition if you know there is no chance another object will exist in need of those methods that will be passed into the same methods this object will.  Simpler put, if this object could be singing “I’m the only one” Then you probably don’t need an interface.

Most of this may seem pretty general, but as usual this may help you.  It’s just an approach I’ve been using.  You may want to review your approach and see if you do any of this and whether or not the ideas may benefit you.  If you ever find yourself cussing because you don’t have enough flexibility or you have to write large extensions to your entire code base every time a new object is added.  Then this is a good approach for you.

Happy coding.