When writing software I’m often forced to at some point to make a decision that involves a design pattern of “by convention” naming and behavioral patterns. This normally makes me feel kind of slimy as a .NET developer because nearly everything is statically typed (arguably even the dynamic type is statically typed, but others have covered this well, go read their blogs, they will thank you for it.)
Naturally I encountered others with opposing views who felt that in many cases, convention based naming and behavioral patterns are actually helpful in that they allow for cleaner, more readable, and more consistent code. It was even stated that depending on how far that convention is taken, for instance into a type reflection name determined behavior scenario, that it might even enforce good naming by breaking functionality if naming in inconsistent with the established conventions.
All of this brought me around to the place where the love and passion for development goes to die, “Testing.” I came up with the following hypothesis:
Using naming conventions and associated behavioral or structural rules associated with those conventions, it should be possible to write tests for the product that enforce that behaviors and structures associated with names are implemented correctly by reflecting the available target binaries. Furthermore it should also be possible to enforce a by convention design that doesn’t allow for the misuse of naming conventions by means of having names that fall under the naming conventions that don’t follow those same behavioral or structural rules to be used inappropriately (i.e something with the word Locator in the type name that doesn’t implement the service locator pattern.)
Has anyone ever investigated this kind of architectural and design testing and provability? What have your experiences been with writing architecture and convention tests? What challenges have you faced and what solutions have you devised? Are there frameworks or tools for doing this? Is there research on the subject done that has been peer reviewed?
4
Yep, this would be great in theory…
Indeed, naming design patterns improves readability. This is actually the goal of design patterns in the first place. It’s much simpler to say:
I used an abstract factory, but maybe we should start switching to a builder for more flexibility.
to your colleague when explaining the structure of your code, rather than:
I made a parent class which is inherited by a few other classes, given that each of those classes have an intent to create complex objects; now, we should start creating those objects by calling different methods which will progressively extend each object, because it will allow us to customize the way we extend them depending on the circumstances.
The first version is clear (unless the person you speak to has no idea what a factory or a builder is). The second version is difficult to understand and is subject to interpretation.
Enforcing naming conventions for design patterns is useful as well. This would permit the beginners to be accustomed more quickly with those patterns. Learning and applying design patterns is hard, and a help from a static checking tool would greatly improve the learning curve. If you tried to implement a wrapper, but instead created an adapter, static checking tool will show you your mistake, with a bonus point of telling you that you’re actually using an adapter.
…but it’s hard to do in practice
So, can you enforce naming conventions for design patterns in practice?
I’m not so sure. Here are a few reasons:
-
A design pattern is easy to understand for an human, but not for a machine. It’s not like a strict rule such as “You should always enclose objects which implement
IDisposable
into ausing
“ : such statement is easy to check for an application like FxCop/Code analysis. A design pattern is… well, a pattern. It doesn’t have a strict structure; there are multiple ways to implement it, while even slightest changes can destabilize a static checker.For example, take a strategy pattern. Outside an academic simplistic example, the static checker would have a hard time knowing if the pattern is used or not. Example:
/// <param name="combine">A function which takes two consecutive entries and combines /// them into a single entry to print.</param> public void GeneratePdf( Consignee consignee, IEnumerable<Entry> entries, Func<Entry, Entry, ComplexEntry> combine) { }
The behavior can be switched at runtime by using a different function as an argument of
combine
parameter. This doesn’t mean that this is a strategy pattern. Maybe in order to combine several entries, some advanced techniques should be used, those techniques being held by the caller library, so using them from the called one is impossible, because of circular dependencies. -
In the same way, definitions are not strict enough.
The
IDisposable
rule above is extremely strict. You have an object with a specific interface? Either you put it inside ausing
, and the rule will be satisfied, or you don’t. Most rules (as well as the one withIDisposable
) have exceptions, but those exceptions are also extremely strict and precise.Design patterns, on the other hand, have definitions, but those definitions can be bent in order to accommodate different situations. For example, would a
static readonly
field be a singleton? What about a property with a backing field and a lock? Those two situations are radically different, and still, they can represent a singleton.Imagine how hard it would be to check if something is an adapter or a fluent interface. For example, would any method of a class which returns an instance of the class itself be considered a fluent interface?
-
Static checking is mostly used by developers who know their job. They mostly don’t need a tool which would tell that they are using
Strategy
keyword, but not using a strategy pattern. Either they are actually using a strategy pattern, and the static checker was wrong, or they are consciously naming the class/method in this way, without any intent of using an actual strategy pattern.Beginners, i.e. persons who can actually benefit from such static checker, wouldn’t use it, because they don’t know how to use static checkers, and even if they know, they see such tools more as a disturbance, an annoying thing which bothers them with lots of hard to understand warnings.
To conclude, enforcing naming conventions for design patterns is a difficult task, and would very probably give a lot of false positives and false negatives. It will not be very useful, but may be very annoying instead.
By the way, there is a sort of very smart static checkers: your peers. If somebody misuses a design pattern, it will be highlighted during a code review. Static checkers are excellent to do basic repetitive work, like checking the conformity of the code to strict rules. Keep this basic checking for them, while the choices requiring thinking and making decisions will remain a task of developers checking the code during a code review.
1