2

I have a colleciton of objects which need to maintain several time-stamps for that last time certain properties within the object was updated (one time-stamp per property).

I would just implement the time-stamp update in the setter except that the deserialization library being used first creates an object, then updates all of its properties (using the object's setter). This means that all my time-stamps would be invalidated every time my program deserializes them.

I'm thinking I need a singleton class or some update method which handles updating the properties and also controls the time-stamp update. Is there a better way to implement this behavior? Does a design pattern exist for this behavior?

Ibrahim Najjar
  • 19,178
  • 4
  • 69
  • 95
Joel B
  • 12,082
  • 10
  • 61
  • 69
  • 2
    Separate out your serialization concerns from your business layer. Have one business class (which has the timestamps) that 99% of your API uses, and a second DTO class (without the timestamps) which is used _only_ for serialization/deserialization. When you need to serialize/deserialize, you convert to/from your business object and your DTO object. – Chris Sinclair Aug 26 '13 at 14:22
  • @ChrisSinclair - This is a great idea except that I need to serialize the time-stamp too. – Joel B Aug 26 '13 at 15:18
  • 2
    So? Then add timestamp properties to your DTO object. – Chris Sinclair Aug 26 '13 at 15:31
  • @ChrisSinclair - Done. Thanks for helping me think this through! This should be an answer so I can mark it as a solution. – Joel B Aug 26 '13 at 17:21
  • Glad you got this working! I'll be happy to write up an answer for it on my break later today. :) – Chris Sinclair Aug 26 '13 at 17:48

1 Answers1

2

If you separate your serialization concerns from your business layer, it should help find you some flexibility to hammer out a solution. Have 99% of your API work with your business object (which updates timestamps when properties update), then only convert to/from some data-transfer-object (DTO) for serialization purposes only.

For example, given some business object like this:

public class MyObject
{
    public DateTime SomeValueUpdated { get; private set; }

    private double _SomeValue;
    public double SomeValue
    {
        get
        {
            return _SomeValue;
        }
        set
        {
            SomeValueUpdated = DateTime.Now;
            _SomeValue = value;
        }
    }

    public MyObject()
    {

    }

    //for deserialization purposes only
    public MyObject(double someValue, DateTime someValueUpdated)
    {
        this.SomeValue = someValue;
        this.SomeValueUpdated = someValueUpdated;
    }
}

You could have a matching DTO like this:

public class MyObjectDTO
{
    public DateTime SomeValueUpdated { get; set; }
    public double SomeValue { get; set; }
}

Your DTO can be specially adorned with various XML schema altering attributes, or you can manage the timestamps however you see fit and your business layer doesn't know and doesn't care.

When it comes time to serialize or deserialize the objects, run them through a converter utility:

public static class MyObjectDTOConverter
{
    public static MyObjectDTO ToSerializable(MyObject myObj)
    {
        return new MyObjectDTO {
            SomeValue = myObj.SomeValue,
            SomeValueUpdated = myObj.SomeValueUpdated
        };
    }

    public static MyObject FromSerializable(MyObjectDTO myObjSerialized)
    {
        return new MyObject(
            myObjSerialized.SomeValue, 
            myObjSerialized.SomeValueUpdated
        );
    }
}

If you wish, you can make any of the properties or constructors of MyObject to be internal so only your conversion utility can access them. (For example, maybe you don't want to have the public MyObject(double someValue, DateTime someValueUpdated) constructor publicly accessible)

Chris Sinclair
  • 22,858
  • 3
  • 52
  • 93
  • Even making the constructor (for deserialization) internal would not avoid construction process becoming difficult when you have say more than four parameters (In future one may add new properties). In those cases it is recommended to separate out the construction process using [Builder Pattern](http://stackoverflow.com/a/1953567/1531157) to ensure your created object is always be in consistent state. – Imran Aug 27 '13 at 09:16
  • Instead of exposing internals of `MyObject` to a converter utility, let `MyObject` expose method like `GetMyObjectDTO` and a constructor `MyObject`(`MyObjectDTO` fromDTO). – Imran Aug 27 '13 at 09:33
  • @Imran: Yes, you could do either of those. I prefer having a class dedicated to doing those concerns, and those concerns _only_. It provides the interfacing/separation layer between the business and DTO layers. It allows it so if there are _other_ serialization methods beyond XML (say, a database access object) then you can _add_ that to your application without changing/affecting anything. It also simplifies testing as you have a nicely contained, testable class to perform the conversion. It also makes sure that your business object and DTO object do not mix serialization/business concerns. – Chris Sinclair Aug 27 '13 at 09:53
  • @Imran: That said, I _strongly_ recommend Joel B implements whatever method of conversion he feels is the most maintainable/simplest/easiest/best for his specific usage. The conversion process itself is _relatively trivial_, and (I feel, mostly irrelevant to the question) so I didn't want to into detail about the several ways of doing so and instead focus on the _separation of concerns_ which was relevant in the question. – Chris Sinclair Aug 27 '13 at 09:55
  • @Imran - I think extensibility for either pattern takes some work, but you are right that the Builder Pattern helps maintain a consistent internal state (which is paramount) and is the way to go for scalability. Chris Sinclair's answer concisely points out how to use DTOs which is the heart of what my question is asking for. – Joel B Aug 27 '13 at 19:21
  • @JoelB Yep, I understood I gave additional information (although one can keep note of that) which is indeed not necessary here. – Imran Aug 27 '13 at 19:30