The parts of our application (a long-term service and support system for the state of Maryland) that follow the DRY principle best tend to start with a combination of generic interfaces inherited by an abstract class that implements common functionality. The end result–specific implementations that consist solely of a constructor. I was able to accomplish this as well in one of my more recent domain implementations. I’ve created a sample (using fantasy football as a domain) to demonstrate the ideas in a way that may be applied to future designs.
Let’s take the idea of a team roster. A roster consists of players with a wide variety of roles that can be grouped this way:
- QBs
- offensive linemen
- skill position players
- defensive linemen
- linebackers
- defensive backs
- special teams
Since I want specific implementations that are (very) small, I’ll need to find common attributes between these different groups. Each roster grouping above is just a list of players. Things that are common to all players (regardless of position) include attributes like:
- first name
- last name
- team name
- position
The first three attributes are just strings of text, so I treat them as such in my implementation. Position could be treated that way too, but instead I’ll implement an enumeration with all the positions and leave the implementation of it to position-specific classes I’ll create later. Going back to the roster grouping idea as a list of players, we can use a generic interface implemented by an abstract class so that implementations of the groups above will differ only by constructor. Now, when I implement a Quarterbacks group, the only differences between it and the implementation of an OffensiveLinemen group are the class names and types. The RosterGroup class contains all the important functionality, including the IEquatable implementation that enables comparison of groups. I followed a ReSharper suggestion to make IRosterGroup explicitly covariant.