Creating a Value Object that Developers Can Use
You can dramatically simplify your code by using value objects, but to create a value object that makes sense to the developer who uses it, you need to redefine what the equals sign means. Along the way, Peter points out some problems when you don't use classes to define your data.
In a previous column, I discussed how you could simplify your application by making more use of read-only/immutable objects. I followed that up with a column on how to actually create those objects by using structs. Using a struct allows you to create two objects with identical values, compare them using the Equals method that's built into every Microsoft .NET Framework object and get True as the result of the test:
Dim adjAbsolute1 As Adjustment
Dim adjAbsolute2 As Adjustment
adjAbsolute1 = AdjustmentFactory.GetAdjustmentByName("PayingCash")
adjAbsolute2 = AdjustmentFactory.GetAdjustmentByName("PayingCash")
If adjAbsolute1.Equals(adjAbsolute2) Then
That last test is critical to me because I want two value objects with the same values in their properties to be treated as identical (just like 1 equals 1, regardless of where those two numbers come from). Fortunately for me, that's the way the Equals operator works when comparing structs.
Unfortunately for me, no developer in his right mind is going to write his comparison code using the Equals method. Any normal developer is going to want to compare the two value types using the equals sign (=), like this:
If adjAbsolute1 = adjAbsolute2 Then
The bad news is that the equals sign will return False, even if the two Adjustments have identical values. The good news is that I can redefine what the equals sign means for my Adjustment classes to get the test result I want. Unfortunately, I'll also reveal some problems with my chosen implementation.
Redefining the equals sign is easy to do with structs: I just need to create an Operator method that defines the meaning of the equals sign for my Adjustment struct. In Visual Basic I have to do it twice because, if I define the equals sign, I must also define the not equals sign (<>). While I may have to do it twice, the actual code inside my Operator methods is trivial: I just need to call the Equals method, which already does what I want.
I've discussed redefining operators in C# and Visual Basic in more detail in an earlier column. To define an operator you must add Public Shared Operator methods to your class (in C#, you use static rather than shared) and follow the keyword Operator with the operator you're redefining (in this case, that's the equals or the not equals signs). Your methods must accept two parameters: one for the values on either side of the operator. For my Adjustment struct, the code looks like this:
Public Structure Adjustment
Public Shared Operator =(adj1 As Adjustment, adj2 As Adjustment) As Boolean
Public Shared Operator <>(adj1 As Adjustment, adj2 As Adjustment) As Boolean
Return Not adj1.Equals(adj2)
In my previous column, I suggested that redefining operators is something you don't want to do because you're often creating a conflict with how the developer expects the language to behave. With value objects, however, redefining the equals sign makes sense to me because all I'm really doing is providing a wrapper for the already existing Equals method. I'm not adding or removing functionality -- I'm just making the Equals method more accessible to the developer by supporting how the developer expects the language to behave.
The Wheels Fall Off
But now I have a problem. In my case study these Adjustment value types represent changes to be made to the price of a sales order: They're used to move the price up or down depending on whether the client is entitled to a discount or has incurred some extra charge. Some discounts are percentage-based ("5 percent off for paying cash") while others are absolute amounts ("$5.00 off your second purchase"). A 5 percent discount is not equal to a $5.00 discount … at least, not most of the time. To deal with this difference, I really need two kinds of Adjustment objects: a PercentageAdjustment and an AbsoluteAdjustment and to not permit comparisons between the two different kinds of Adjustments.
The obvious solution is to have a base Adjustment that defines what any Adjustment looks like and holds any code that's common to all Adjustments. I would then create PercentageAdjustment and AbsoluteAdjustments that inherit from that base Adjustment. In those two new types, I could define their equals sign operator to only support comparison between classes of the same type (for example, AbsoluteAdjustment-to-AbsoluteAdjustment, but not AbsoluteAdjustment-to-PercentageAdjustment).
Here's where my problem appears: Structs don't support inheritance. You can only use inheritance with classes. Of course, if you don't need inheritance this isn't a problem. But, still …
So I'm going to return to this topic in a later column, show how to implement value objects using classes and solve this problem with inheritance. But I can deal with this problem using structs, also, and I'll discuss that solution in yet another column. In the meantime, you have everything you need to create simple value objects.
Peter Vogel is a system architect and principal in PH&V Information Services. PH&V provides full-stack consulting from UX design through object modeling to database design. Peter tweets about his VSM columns with the hashtag #vogelarticles. His blog posts on user experience design can be found at http://blog.learningtree.com/tag/ui/.