Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C#4.0

Labeled Entities for WhatIf Analysis

0.00/5 (No votes)
17 Aug 2011CPOL3 min read 8.3K  
How to create a labeled entity

Introduction

Recently I was asked to develop and application that supports what-if analysis, i.e., one can set different values and combine these values under one "label" by which that particular instance can be addressed.

Background

Somehow everything that catches my eye is not documented very well. For instance trying amazon.com for object oriented olap lead to a very poor set of references (21 items for me).

Using the Code

The code presented in the article is for illustrating purposes only. I do not expect it to be perfect. I even expect to find better implementations of these ideas.

What it is all About

Lets assume that we need to develop and application that does some calculations based on certain values of different entities. These values can be very real, say, money payed or items sold, or they can be imaginary. The thing is that these imaginary values can be of different sorts. One combination can be a "worst case scenario", another one can be "use only one bank for all the transactions" etc. Some combinations will be planned, i.e., expectations will be made according to them, other combinations will be a raw material which is needed only to choose some optimum. Either way our olap "cube" should be flexible.

In simple terms each entity which can be changed for modelling purposes needs to remember different states simultaneously. For the concivience of a user, one can select a particular state of a particular entity and see how it affects the result. As a solution I propose (for now) to use "labels" that will be tokens to address a particular entity state and below is a simplistic implementation of this idea.

Implementation

C#
// First of all declare an interface for our entity

public interface IBonus
{
    double Amount { get; set; }
    DateTime Date { get; set; }
}
A simple thing: how much (let's say USD) one will receive on a particular day.

Now, why this someone is not a part of the interface? Why doesn't the interface look like this:

C#
public interface IBonusNotImplemented
{
    double Amount { get; set; }
    DateTime Date { get; set; }
    string Person { get; set; }
}

The answer is simple: because we are going to model bonus size and its date, but not the person. After all, if we need to leave someone without a bonus we can always do that by labeling Person entity.

Back to business. Define an interface common for all entities that support labeling.

C#
public interface ILabeled<T>
{
    string GetCurrentLabel();
    void SetCurrentLabel(string label);
    void AddLabel(string label);

    ICollection<string> Labels { get; }

    T GetEntityByLabel(string label);
}

Label can be a much more complex object than a string, but even a string will be very useful. Note that there is no RemoveLabel. That is because I don't want to implement it here as there are certain implications not fully understood.

Here we declare a new interface that implements both previous interfaces.

C#
public interface IBonusLabeled : IBonus, ILabeled<IBonusLabeled>
{
}

Declare concrete classes that will implement the required functionality.

C#
internal class Bonus : IBonus
{
    private double amount_;
    private DateTime date_;

    public double Amount
    {
        get
        {
            return amount_;
        }
        set
        {
            amount_ = value;
        }
    }

    public DateTime Date
    {
        get
        {
            return date_;
        }
        set
        {
            date_ = value;
        }
    }
}

internal class BonusLabeled : Labeled<Bonus>, IBonusLabeled
{
    private string person_;

    public string Person
    {
        get
        {
            return person_;
        }
        set
        {
            person_ = value;
        }
    }

    public double Amount
    {
        get
        {
            return base.Current.Amount;
        }
        set
        {
            base.Current.Amount = value;
        }
    } 

    public DateTime Date
    {
        get
        {
            return base.Current.Date;
        }
        set
        {
            base.Current.Date = value;
        }
    }

    public new IBonusLabeled GetEntityByLabel(string label)
    {
        return base.GetEntityByLabel(label) as IBonusLabeled;
    }
}

Labeled<Bonus> is where all the magic happens.

C#
public class Labeled<T> : ILabeled<T> where T : class, new()
{
    private Dictionary<string, T> entities_;
    private string currentLabel_;

    public Labeled()
    {
        entities_ = new Dictionary<string, T>;
    }

    public string GetCurrentLabel()
    {
        return currentLabel_;
    }

    public void SetCurrentLabel(string label)
    {
        if(!entities_.ContainsKey(label))
        {
            // throw a nasty exception or do nothing or...
        }

        currentLabel_ = label;
    }

    public void AddLabel(string label)
    {
        if(entities_.ContainsKey(label))
        {
            // throw a nasty exception or do nothing or...
        }

        entities_.Add(label, new T());
    }

    ICollection<string> Labels
    { 
        get
        {
            return entities_.Keys;
        }
    }

    public T GetEntityByLabel(string label)
    {
        // This piece of code has to be implemented separately, it is not a simple
        // thing, but is unimportant here. Yeah, I'm being lazy :)

        return MagicFactory.CreateNewLabeledProxy<T>(this, label);
    }

    protected T Current
    {
        get
        {
            return entities_[currentLabel_];
        }
    }
}

BonusLabeled is a proxy to a collection of Bonuses. MagicFactory will create a new BonusLabeled object which will have another label as current, but will share the same entities_ collection. Ta-da!

Unfortunately, this is not all. For instance, we may want to have some fields labeled and some - not. Like Person (see below).

C#
public interface IBonusLabeled : IBonus, ILabeled<IBonusLabeled>
{
    string Person { get; set; }
}

The code above states (or should read): Person will not be labeled. In a real world this may mean that we can either pay a bonus or not. A particular bonus belongs to a particular person. So, what is the problem? The problem is with the "shared" value of Person field which must be implemented in such a way that one cannot suddenly have this situation:

C#
IBonusLabeled bonusA = new BonusLabeled{ Person = "Smith" };

string labelA = "what-if-we-pay-standard";
bonusA.AddLabel(labelA);
bonusA.SetCurrentLabel(labelA);

bonusA.Amount = 10.0;
bonusA.Date = DateTime.Now;

string labelB = "what-if-we-pay-more";

bonusA.AddLabel(labelB);
IBonusLabeled bonusB = bonusA.GetEntityByLabel(labelB);

bonusB.Person = "Clay";

bool equals = bonusB.Person == bonusA.Person;
if(!equals)
{
    // terrible things
}

Points of Interest

It is reltively easy too look for something you can clearly firmulate in a common dialect, but with this particular problem I'm a little bit lost thus there is a lot of fun inventing things "as if never before". There is a lot more to do on the subject.

History

15 AUG 2011 - Initial version.

License

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