Consider the following code.
public class Customer {
.
.
.
public bool IsPreferredCustomer {
get {
return (this.TotalSales > 10000 || this.TotalVisits > 50) || (this.TotalSales > 5000 && this.TotalVisits > 25);
}
}
In this example a common pattern is being followed where an attribute IsPreferredCustomer has been exposed directly from the Customer object. The attribute in terms of language is being used to abstract the concept of a customer who has spent ten thousand dollars, had over fifty visits, or has over five thousand dollars in sales and twenty-five visits to the location.
There are other options in how to implement this however, namely by using the Specification Pattern
public class IsAPreferredCustomer : Specification<Customer> {
public bool Matches(Customer customer) {
if(customer == null) return false;
return customer != null && (customer.TotalSales > 10000 || customer.TotalVisits > 50) || (customer.TotalSales > 5000 && customer.TotalVisits > 25);
}
So the question quickly becomes which of these is correct under what circumstances? In order to answer this question one must look at the strengths and weaknesses of each.
Language
The main difference between the two examples can be seen in how they are represented in terms of the ubiquitous language.
When dealing with the attribute approach in the ubiquitous language the attribute is applied to every instance when discussing the entity. It is treated just like any other attribute, the name of the customer as an example. By simply listening to the language it is unspecified whether this represents a calculation being performed or is a value associated with the entity.
The Specification maintains a subtly different linguistic representation. The specification applies a name to a constraint. Since it is defining a name to a constraint it is known based on its usage that it is a calculation as opposed to a denormalization. It is also made clear that because it is defined as a specification, that it’s purpose is to constrain. When an attribute is available upon an entity, constraints can be but do not necessarily have to be made upon it. In other words, the attribute is available to constraining code but may have another purpose in general. The specification makes the intent of constraining inherent in the language which is a primary goal of the ubiquitous language, making the implicit explicit.
Code Locality
When an attribute is added to the entity it being part of the entity is necessarily located near the rest of the code associated with that entity. This can be an advantage in terms of maintenance as the code associated with the entity is localized and can be easier to find/follow by a developer traversing the source base.
The localization of the validation logic can also be a hindrance. Because the code maintains its locality one can run into code explosion when there are many of these attributes. The area continues to grow as new code gets added eventually reaching a point where it has crossed the threshold of being able to be kept straight in a single location. Refactoring away from attributes at this point can be a pain.
Another problem with maintaining the locality is that we can only reasonably have a single implementation of the attribute. If one were faced with a multi-tenant situation or having multiple deployments with varying rules the specification would be preferred as it provides a seam for replacement.
The C# programming language offers some interesting ways of dealing with the possible explosion of code within the original entity. Partial classes allow a class to be defined across multiple files and extension methods allow static methods stored elsewhere to “appear” to be a part of the entity while they are in fact just static methods. In the case of constraints this is actually a good thing as the constraints don’t really “belong” in the entity but are describing it. These types of syntactical sugar can make things much easier to deal with in terms of dealing with possible code explosion and should be considered as options if they are available in a given language. As an example consider the following C# code that uses both the specification pattern and extension methods to create what some may consider a more concise API around the previous example.
Encapsulation
Many bring up encapsulation as a key difference between the two patterns shown. The attribute-based example encapsulates the logic representing the constraint within it. One must question whether the entity is in fact the proper place to put this logic. At first smell it would seem that it is as in order to write a constraining specification the specification needs access to the internal state of the entity.
The need of a constraining specification often leads to the exposing of the state globally from the entity that as illustrated previously [see Getter/Setter Anti-Pattern] can cause other problems within the domain. The solution to this issue is to adjust the tools being used as opposed to the way modeling is performed; some languages have constructs such as friend classes that handle this case in a more elegant manner.
Analysis
Upon analyzing the various usages it becomes apparent that the use of an attribute to expose the constraint is usually an anti-pattern. A specification will always better model a constraint in the ubiquitous language. The intention of the modeler to constrain is also made explicit while remaining implicit with the attribute version. While the [Programmer Pornography] of encapsulation or especially code locality may end up with an advantage to the attribute based version it is also a long term risk as it can easily explode and is difficult to refactor.
Rule of Thumb: Avoid the use of attributes on entities that are constraints. Prefer to use a specification as it makes the intent to constrain explicit.
Posted
Sun, Dec 21 2008 2:42 PM
by
Greg