Click here to Skip to main content
15,867,686 members
Articles / General Programming

nBaclava

Rate me:
Please Sign up or sign in to vote.
4.71/5 (15 votes)
5 Jun 2013CPOL10 min read 46.3K   352   30   23
Base classes for value objects.

Table of Contents 

Introduction

While Baklava is delicious for everybody, its technical pendant nBaclava might be delicious for you as a developer. nBaclava stands for "BAse CLAsses for VAlue objects" and gives you - well - base classes for value objects. If you are not familiar with value objects I recommend to read this article from Richard A. Dalton here on CodePrpject but I will give you a short introduction as well.

Within the domain driven design (DDD) a value object is describes as "an object that describes some characteristic or attribute but carries no concept of identity". For example the Point structure of the .net framework is a value object. It is defined by its x- and y-coordinate.

It might be obvious to you to define a point as its own type because the x- and y-coordinate seem very inseparable. So it is very common to combine them in a class or struct - that is what object oriented programming is for Wink. But what about primitive values? Ever thought about defining a class for a string or an decimal? For example an email address is usually defined by a string and an amount is defined by a decimal. This seems to be good enough and a class which looks as followed seems to be oversized without adding something special to it:

C#
public class EMailAddress {
    public string Value { get; private set; }
 
    public EMailAddress(string value) {
        Value = value;
    }
}

But read on...

Advantages of value objects

  • By definition, value objects should be immutable. This means that their values cannot be changed if set. This is a very important fact in functional programming as described by Marc Clifton here but the advantages for functional programming are more or less transferable to OOP.
  • Defining your own class - even for primitive values like string as shown above - gives you the potential to validate the used values. For example, you can throw an exception if your string does not represent a valid email address:
  • C#
    public class EMailAddress {
        public string Value { get; private set; }
     
        public EMailAddress(string value) {
            if(value == null) {
                throw new ArgumentNullException("value");
            }
            // your tricky validation logic comes here...
            if(!value.Contains("@")) {
                throw new ArgumentException("Not a valid email address.");
            }
            Value = value;
        }
    }

    In combination with the concept of immutability you can now be sure to always have a valid email address. And it is a perfect central place for validation logic code which would otherwise be scattered in some utility classes.

  • Having your own type you are much more open to new functionality. Imagine that you will have to grab the top level domain of an email address within your program. Having email address just hanging around as a string you hopefully will not copy and paste the needed string operations every time you need them. Obviously you would end with a utility class again. But check out the possibility of a value object:
  • C#
    public class EMailAddress {
        public string Value { get; private set; }
     
        public TopLevelDomain TopLevelDomain {
            get {
                int topLevelDomainStartIndex = Value.LastIndexOf('.') + 1;
                string topLevelDomain = Value.Substring(topLevelDomainStartIndex);
                return new TopLevelDomain(topLevelDomain);
            }
        }
     
        public EMailAddress(string value) {
            // your tricky validation logic comes here...
            Value = value;
        }
    }
     
    public class TopLevelDomain {
        public string Value { get; private set; }
     
        public TopLevelDomain(string value) {
            Value = value;
        }
    }
  • You can define plausible null values. For example String.Empty so that you do not have to check against null within your entire application.
  • And for free, you will get a much more structured, readable and expressive design of your software.

I hope you can see that there is much more potential within your usage of primitive values if you look twice and switch to value objects. Define "amount" instead using decimal directly and you automatically can round any value to a precision of two decimal places. Define "age" instead using an int and offer a calculated "YearOfBirth" property if required...

Disadvantages of value objects

Beside the fact that you will have to do some work in order to define your own types - even more if it is just for a primitive value - you might find it cumbersome to wrap values into classes and back again in order to use them in third party APIs (including the .net framework) because these APIs do not know your types and just offer the primitive values as method arguments or properties.

nBaclava

That is what nBaclava is for. It offers you base classes for value objects in general (as Point mentioned above) and for primitive values (as string and decimal) in particular. Furthermore there are some Visual Studio class templates to make its usage even simpler. Read on to learn about how nBaclava will support you on each concept of value objects.

ValueObject vs. PrimitiveValueObject

nBaclava offers two main types: ValueObject and PrimitiveValueObject. ValueObject is the base class for your complex types with more than one attributes (e.g. Point) whereas PrimitiveValueObjects encapsulate a single simple type (as string, decimal, etc.). For the last one nBaclava offers dedicated subclasses like StringValueObject or DecimalValueObject. PrimitiveValueObjects have a property named Value which holds the actual value (e.g. string). That is the same as on nullable types types like int? or DateTime?. So a simple starting point for your email address class would look like this:

C#
public class EMailAddress : StringValueObject<EMailAddress> {
    public EMailAddress(string value)
        : base(value, FreezingBehaviour.FreezeOnAssignment, ValidityBehaviour.EnforceValidity) {
    }
}

PrimitiveValueObjects offer you support for a lot of instance methods of the encapsulated type. For example you can call StartsWith on your EMailAddress value object out of the box:

C#
EMailAddress eMailAddress = new EMailAddress("someone@somewhere.com");
if (eMailAddress.StartsWith("someone")) {
    // do something
}

So there is no need for using the inner value for this kind of operations. And the using of value objects is readable in a more natural manner.

Immutability

As said above value types should be immutable. This is mostly done by taking over the values within the constructor and just defining get on properties without their set pendants. But there are scenarios where this is not suitable. For example there are OR-Mappers which rely on parameterless default constructors. And for more complex objects it might be a good thing to set each property within a special building process instead of using a constructor with a lot of parameters.

So I decided to implement the freeze pattern. This means that an instance is mutable as long as the Freeze() method is called. So you can decide when it is time to freeze your objects.

Because PrimitiveValueObject has just one value you can shorten the freezing process by calling the base constructor with the concrete value and FreezingBehaviour.FreezeOnAssignment (have a look at the code snippets above).

This way the instance will automatically freeze on its first value assignment.

You will see that PrimitiveValueObject does not offer a setter for its Value property by default. Immutability was preferred by design. If you need a Value setter for your own types you will have to overwrite the property as follows:

C#
public new string Value {
    get { return base.Value; }
    set { SetValueTo(value);}
}

If you implement your own complex ValueObject in contrast you will have to implement your properties in this way:

C#
private string mStringValue = String.Empty;
 
public string StringValue {
    get { return mStringValue; }
    set { Set(() => mStringValue = value); }
}

All other freezing stuff is done by the base classes!

To fulfill the freeze pattern there are some useful properties:

CanFreeze (could be overwritten by you for your own types), IsFrozen and IsMutable.

Along with two event methods:

OnFreeze() and OnFrozen() which could be handled by you if you have to.

Once an object is frozen there is no way back!

Validity

Value objects should be valid by default or at least they should give you the possibility to check their validity. So you can check its IsValid property. Or you can call the Validate() method which throws an exception if the accordingly instance is in an invalid state. In order to just get the exception you can call TryValidate().

You will have to overwrite TryValidate() in order to define your own validation logic. By default all value objects are valid at all times.

To enforce validity you will have to call the base constructor with ValidityBehaviour.EnforceValidity (as shown above). This way Validate() is automatically called when a property changes its value.

For PrimitiveValueObjects you can overwrite OnValueChanging() in order to adapt a value before it is set (e.g. returning String.Empyt if null). So you can avoid an invalid state if suitable.

For ValueObject you should implement your properties as mentioned under Immutability.

Equality

As mentioned at the beginning, value objects are just described by their attributes. This leads to equality if all attributes of two value objects are equal. Compared to default classes which are only equal if their references point to the same memory location! Picking up Point again as an example you will agree that all points having x = 8 and y = 5 are the same point - regardless of their memory pointers. The base classes of nBaclava overwrite Equals() in order to fulfill this strategy. So you do not have to do anything special here – it’s all done! This was easy for PrimitiveValueObject because it just has one value to compare. For ValueObject all fields of your classes are collected per reflection and then their values are checked against each other. It is nearly the same as it is done by the .net framework within the ValueType class.

Null

Value objects give you the chance to define a suitable replacement for null by changing the given value before it is set (for example use OnValueChanging() for PrimitiveValueObjects). So there is no need for checking against null within the rest of your application.

If you need to store null as a valid value you will be glad to read that PrimitiveValueObjects which encapsulate a struct (e.g. decimal, int, DateTime, etc.) follow the nullable pattern so that you can use decimal? or int?, too.

PrimitiveValueObject offers IsNull (respectively HasValue) as a property.

Implicit conversion

PrimitiveValueObject provides implicit conversion to its encapsulated type. This way you can use your value object in any scenario where the base type is requested - e.g. as a method parameter or for variable assignments:

C#
EMailAddress emailAddress = new EMailAddress("someone@somewhere.com");
string email = emailAddress;

So there is no need to use the Value property in this way:

C#
string email = emailAddress.Value;

If you make use of the provided Visual Studio class templates you will get implicit conversion for the other way as well:

C#
EMailAddress emailAddress = "someone@somewhere.com";

More conversions

PrimitiveValueObject implements IConvertible.

Operators

There are some other operators defined for PrimitiveValueObject – not just the implicit conversion! So you can use the arithmetic operators like +, -, *, /, <, <=, >, >=, !=, == on numeric value types like DecimalObjectValue or IntValueObject.

Lets assume a simple class Amount as follows:

C#
public class Amount : DecimalValueObject<Amount> {
    public Amount(decimal value)
        : base(value, FreezingBehaviour.FreezeOnAssignment, ValidityBehaviour.EnforceValidity) {
    }
    public static implicit operator Amount(decimal value) {
        return new Amount(value);
    }
}

Then you can use it like this:

C#
Amount value1 = new Amount(12.34m);
Amount value2 = new Amount(2.10m);
Amount result = value1 + value2;

Or in combination with raw decimal values:

C#
Amount result = value1 + 2.10m;

The result will be a new instance of the concrete object value type!

In order to get this to work you will have to provide a constructor with one parameter – the value (as shown in the code above). And that is why your class needs itself as a generic parameter. This way nBaclava can construct your types on demand.

Visual Studio class templates

You can download a template package for nBaclava. It contains class templates for the PrimitiveValueObject definitions like DateTimeValueObject or StringValueObject and for the general ValueObject. So you are really fast in setting up your own value objects. In order to use the templates you will have to extract the package into your template folder. Look here if you do not know where to find or how to define it. After having these templates installed you will have a new category nBaclava when you call "Add – Class" within your project.

A new type created by these templates may look like this:

C#
/// <summary>
/// Encapsulates a <see cref="System.String">String</see> defined as 'EMailAddress'.
/// </summary>
public class EMailAddress : StringValueObject<EMailAddress> {
 
    /// <summary>
    /// Initializes a new instance of the <see
    /// cref="EMailAddress">EMailAddress</see> class with the specified value.
    /// </summary>
    /// <param name="value">Value.</param>
    public EMailAddress(string value)
        : base(value, FreezingBehaviour.FreezeOnAssignment, ValidityBehaviour.EnforceValidity) {
    }
 
    #region [ Operators ]
    /// <summary>
    /// Converts the specified value to an instance of the <see cref="EMailAddress">EMailAddress</see> class.
    /// </summary>
    /// <param name="value">The value which is converted to an
    /// instance of the <see cref="EMailAddress">EMailAddress</see> class.</param>
    /// <returns>A new instance of the <see cref="EMailAddress">EMailAddress</see> class.</returns>
    public static implicit operator EMailAddress(string value) {
        return new EMailAddress(value);
    }
    #endregion
}

NuGet

nBaclava is also available as a NuGet package. Just search after nBaclava within the NuGet Package Manager.

Conclusion

If you can see the advantages of value objects but you are more or less too lazy to define them consistently within your applications then I hope that you will be much more consequent when you get help using nBaclava. If you are not of the lazy type I hope you will find it useful all the same Wink

I would like to hear what you think about it? If you find it useful I will add support for the missing types like Int16, Int64, bool, etc. as well!

History

  • 01 Oct 2012: Layout fixed
  • 30 Sep 2012: First version

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer
Germany Germany
--------------------------------
Thank you for voting
Best "Everything Else" Article
of September 2014

Comments and Discussions

 
QuestionCreating a nullable type from Int32ValueObject Pin
CBoland25-Mar-14 8:11
CBoland25-Mar-14 8:11 
I'm working up a proof-of-concept to use nBaclava in our application. I've built a series of tests to verify our requirements for such an implementation, and am having trouble implicitly converting an Int32ValueObject-based type to a nullable integer. Running the code below results in:

NullReferenceException in nBaclava.PrimitiveStructValueObject'2.op_Implicit(PrimitiveStructValueObject'2 value)
C#
MyIntValueType a = null;
int? b = a;  // NullReferenceException here


This implicit conversion isn't required, it's more of a curiosity why the exception occurs. Is this unsupported, or do I need to do something special in MyIntValueType?
AnswerRe: Creating a nullable type from Int32ValueObject Pin
Thorsten Bruning26-Mar-14 8:19
Thorsten Bruning26-Mar-14 8:19 
GeneralRe: Creating a nullable type from Int32ValueObject Pin
CBoland26-Mar-14 9:47
CBoland26-Mar-14 9:47 
GeneralMy vote of 5 Pin
Christoph Keller5-Jun-13 22:36
Christoph Keller5-Jun-13 22:36 
GeneralRe: My vote of 5 Pin
Thorsten Bruning6-Jun-13 9:57
Thorsten Bruning6-Jun-13 9:57 
GeneralMy vote of 5 Pin
Prasad Khandekar5-Jun-13 10:26
professionalPrasad Khandekar5-Jun-13 10:26 
GeneralRe: My vote of 5 Pin
Thorsten Bruning6-Jun-13 9:57
Thorsten Bruning6-Jun-13 9:57 
GeneralMy vote of 5 Pin
Florian Rappl4-Jun-13 10:27
professionalFlorian Rappl4-Jun-13 10:27 
GeneralRe: My vote of 5 Pin
Thorsten Bruning5-Jun-13 5:38
Thorsten Bruning5-Jun-13 5:38 
QuestionSimilar idea Pin
chandmk2-Oct-12 0:45
chandmk2-Oct-12 0:45 
AnswerRe: Similar idea Pin
Thorsten Bruning2-Oct-12 1:33
Thorsten Bruning2-Oct-12 1:33 
GeneralMy vote of 5 Pin
Marc Clifton1-Oct-12 3:33
mvaMarc Clifton1-Oct-12 3:33 
GeneralRe: My vote of 5 Pin
Thorsten Bruning1-Oct-12 3:40
Thorsten Bruning1-Oct-12 3:40 
GeneralMy vote of 4 Pin
Paulo Zemek1-Oct-12 3:24
mvaPaulo Zemek1-Oct-12 3:24 
GeneralRe: My vote of 4 Pin
Thorsten Bruning1-Oct-12 3:59
Thorsten Bruning1-Oct-12 3:59 
GeneralRe: My vote of 4 Pin
Paulo Zemek1-Oct-12 5:47
mvaPaulo Zemek1-Oct-12 5:47 
GeneralRe: My vote of 4 Pin
Thorsten Bruning1-Oct-12 5:55
Thorsten Bruning1-Oct-12 5:55 
GeneralRe: My vote of 4 Pin
bvsms1-Oct-12 13:24
bvsms1-Oct-12 13:24 
GeneralRe: My vote of 4 Pin
Thorsten Bruning1-Oct-12 20:14
Thorsten Bruning1-Oct-12 20:14 
QuestionMy vote of 5 Pin
Frederico Barbosa1-Oct-12 0:58
Frederico Barbosa1-Oct-12 0:58 
AnswerRe: My vote of 5 Pin
Thorsten Bruning1-Oct-12 3:10
Thorsten Bruning1-Oct-12 3:10 
GeneralMy vote of 2 Pin
abdurahman ibn hattab30-Sep-12 23:49
abdurahman ibn hattab30-Sep-12 23:49 
GeneralRe: My vote of 2 Pin
Thorsten Bruning1-Oct-12 0:52
Thorsten Bruning1-Oct-12 0:52 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.