A Simplistic Custom Classes Example
So, how can we write an implementation of the custom classes to get a solution that is somewhat comparable to the examples shown so far? Of course, there are numerous solutions to choose from. I'll show you a very simplistic solution here. In Figure 1, you can see an UML diagram of this solution.
Figure 1 UML diagram for the custom classes example.
What is simplistic with this solution, you might wonder? Well, many things. For example:
How to fill the objects with data from the storage: I am just using public properties for both getting and setting the values. In a real application, you typically need another way of setting the internal fields when grabbing values from the database than by using public property setters.
Method parameters should often be objects: As you see in Figure 2, I use primitive parametersfor example, the order ID. This is because it is a similar solution to the previously discussed options. In a more advanced scenario, we might find that we get pretty long parameter lists. In a real situation, it would probably be more natural to send complex objects instead as parameters.
Richer behavior is "expected": In Figure 1, only Validate() methods are used. That is all behavior that is exposed. The biggest value of custom classes as data containers comes when you can add a lot of behavior also. In a real application, you typically will find a lot of custom methods and, of course, a lot of code in the property setters.
Subcollection is too little encapsulated: As you will see, I expose too much of the implementation of the Order class because the consumer can get directly to the OrderLines (which is the collection class that holds OrderLine instances). A better approach is most often to use the encapsulate collection refactoring.
The focus of this article series is to give you an idea of how different data container solutions behave compared to each other, not to show you production-ready frameworks. You will also find that even though I am showing you a simplistic solution, there is plenty of room to add what you need and still get better performance than that for DataSets.
To create the type-safe collection class for holding OrderLine objects (that class is called OrderLines), I inherited from the CollectionBase class. That is a simple solution that is okay for this specific example, but it has its own pros and cons, as usual. I will get back to this subject in Part 5, when I discuss several other options.
Furthermore, each of the classes (Order, OrderLine, and OrderLines) has the <Serializable()> attribute, and no custom serialization is usedjust the plain serialization that comes for free.
To follow Microsoft's naming conventions, OrderLines technically should be called OrderLineCollection.
Also note that, unlike previous options that I discussed (except for the wrapped DataSet), this time there is custom behavior within the classes that also hold the data.
Maybe you think that I'm retro and old-fashioned because I'm talking a lot about object orientation. I've heard that OO is from the 1980s. Nowadays the trend is that we should be message- or document-oriented. (By this, I think it's emphasizing passing state around, typically in a portable format between heterogeneous systems. Data is sent in a coarse granular formatthat is, complete "documents.")
I can agree on message and document orientation. My point is that, for building those applications, under the surface, I think object orientation is still going very strong. Period.