Click here to Skip to main content
Click here to Skip to main content

nBaclava

By , 5 Jun 2013
Rate this:
Please Sign up or sign in to vote.

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:

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:
  • 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:
  • 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:

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:

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:

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:

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:

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

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

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:

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:

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:

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

Or in combination with raw decimal values:

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:

/// <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)

About the Author

Thorsten Bruning
Software Developer
Germany Germany
No Biography provided

Comments and Discussions

 
QuestionCreating a nullable type from Int32ValueObject PinmemberCBoland25-Mar-14 8:11 
AnswerRe: Creating a nullable type from Int32ValueObject PinpremiumThorsten Bruning26-Mar-14 8:19 
GeneralRe: Creating a nullable type from Int32ValueObject PinmemberCBoland26-Mar-14 9:47 
GeneralMy vote of 5 PinmemberChristoph Keller5-Jun-13 22:36 
GeneralRe: My vote of 5 PinmemberThorsten Bruning6-Jun-13 9:57 
GeneralMy vote of 5 PinprofessionalPrasad Khandekar5-Jun-13 10:26 
GeneralRe: My vote of 5 PinmemberThorsten Bruning6-Jun-13 9:57 
GeneralMy vote of 5 PinmvpFlorian Rappl4-Jun-13 10:27 
GeneralRe: My vote of 5 PinmemberThorsten Bruning5-Jun-13 5:38 
QuestionSimilar idea Pinmemberchandmk2-Oct-12 0:45 
AnswerRe: Similar idea PinmemberThorsten Bruning2-Oct-12 1:33 
GeneralMy vote of 5 PinprotectorMarc Clifton1-Oct-12 3:33 
GeneralRe: My vote of 5 PinmemberThorsten Bruning1-Oct-12 3:40 
GeneralMy vote of 4 PinmvpPaulo Zemek1-Oct-12 3:24 
GeneralRe: My vote of 4 PinmemberThorsten Bruning1-Oct-12 3:59 
GeneralRe: My vote of 4 PinmvpPaulo Zemek1-Oct-12 5:47 
GeneralRe: My vote of 4 PinmemberThorsten Bruning1-Oct-12 5:55 
GeneralRe: My vote of 4 Pinmemberbvsms1-Oct-12 13:24 
GeneralRe: My vote of 4 PinmemberThorsten Bruning1-Oct-12 20:14 
QuestionMy vote of 5 PinmemberFrederico Barbosa1-Oct-12 0:58 
AnswerRe: My vote of 5 PinmemberThorsten Bruning1-Oct-12 3:10 
GeneralMy vote of 2 Pinmemberabdurahman ibn hattab30-Sep-12 23:49 
GeneralRe: My vote of 2 PinmemberThorsten Bruning1-Oct-12 0:52 

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

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

| Advertise | Privacy | Mobile
Web03 | 2.8.140415.2 | Last Updated 5 Jun 2013
Article Copyright 2012 by Thorsten Bruning
Everything else Copyright © CodeProject, 1999-2014
Terms of Use
Layout: fixed | fluid