Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Policy Based Design Implemented in C#

0.00/5 (No votes)
6 Oct 2011 1  
Policy based design implemented in C#

Introduction

This is a simple policy based design sample. They are File Name Policy classes that I use the policy based analysis to design policies. In policy based design, the most important thing is analysis. At first, we must define the policy combined roles and implement them in the policy host classes. Then we must decompose the classes to small policy classes.

C# does not support multiple inheritance, but the multiple inheritance is the key technique in policy based design, so we must use interface to solve it.

What is Policy Based Design?

We can find some explanation on Wiki. And one day, I got a feel about it.

I love to eat food, so I thought about the policy recipe of cuisine. Maybe I can define a role to make a cuisine as follows:

Cuisine = Food + Flavor + Cooking
  • The cuisine is the host combinable role. Food, Flavor and Cooking are policy groups.
  • Food: Beef, Pork, Chicken, Spaghetti ...
  • Flavor: Acid, Sweet, Hot, Curry ...
  • Cooking: Roast, Cook, Fry, Stew ...
  • Cuisine(Steak) = Food(Beef) + Flavor(None) + Cooking(Roast)
  • Cuisine(Roast chicken) = Food(Chicken) + Flavor(Curry) + Cooking(Roast)

The recipe of cuisine is a combination of food, flavor and cooking.

A different policy can combine a different cuisine.

I think this explanation is easiest to understand.

Background

I loved the template in C++ and I love the design patterns with Object-Oriented Programming. So I found a book called Modern C++ Design.

After I studied policy based design in this book, I loved it and tried to implement it in C#.

Class Diagrams

1.JPG

The base interface is CBINamePolicy, it just has a property Name.

All of FileNamePolicy classes will implement these interfaces including CBIFileNamePolicy, CBIFilenameExtensionPolicy and CBIFileAnmeControl.

2.JPG - Click to enlarge image

These are policy host classes. The base class is CBFileName implemented.

3.JPG

These classes implement the CBIFileNamePolicy interface.

4.JPG

These classes implement CBIFilenameExtensionPolicy interface.

5.JPG

These classes implement CBIFileNameControlPolicy and CBILargeFileSplitPolicy interface.

Using the Code

/// <summary>
/// Name property interface policy
/// </summary>
public interface CBINamePolicy
{
    /// <summary>
    /// Name Property
    /// </summary>
    string Name { get; set; }
} 
/// <summary>
/// The base policy interface for file name
/// </summary>
public interface CBIFileNamePolicy : CBINamePolicy
{
} 
/// <summary>
/// Basic implement for CBIFileNamePolicy
/// </summary>
public class CBFileNamePolicy : CBIFileNamePolicy
{
    /// <summary>
    /// File Name
    /// </summary>
    public string Name { get; set; }
}

/// <summary>
/// Get full path file name.
/// EX: C:/Test/MyFileName
/// </summary>
public class CBFullFileNamePolicy : CBIFileNamePolicy
{
    /// <summary>
    /// File Name
    /// </summary>
    string m_name;

    /// <summary>
    /// Setter: set value to name
    /// Getter: get full path with name
    /// </summary>
    public string Name
    {
        get
        {
            return CBGeneral.GetFullPath(m_name);
        }
        set
        {
            m_name = value;
        }
    }
}

/// <summary>
/// Readonly policy, to get yyyyMMdd file name
/// EX: 20110923
/// </summary>
public class CBDateNowNamePolicy : CBIFileNamePolicy
{

    #region CBINamePolicy Members

    public string Name
    {
        get
        {
            return DateTime.Now.ToString("yyyyMMdd");
        }
        set
        {
            throw new NotSupportedException
		("CBDateNowNamePolicy.Name is a readonly property.");
        }
    }

    #endregion
}

/// <summary>
/// File name with date yyyyMMdd
/// EX: MyFileName20110923
/// </summary>
public class CBPreDateNowNamePolicy : CBIFileNamePolicy
{
    string m_name;

    #region CBINamePolicy Members

    public string Name
    {
        get
        {
            return m_name + DateTime.Now.ToString("yyyyMMdd");
        }
        set
        {
            m_name = value;
        }
    }

    #endregion
}

/// <summary>
/// Full path with date yyyyMMdd
/// EX: C:/Test/20110923
/// </summary>
public class CBFullDateNowNamePolicy : CBIFileNamePolicy
{

    #region CBINamePolicy Members

    public string Name
    {
        get
        {
            return CBGeneral.GetFullPath(DateTime.Now.ToString("yyyyMMdd"));
        }
        set
        {
            throw new NotSupportedException
		("CBFullPathDateNowNamePolicy.Name is a readonly property.");
        }
    }

    #endregion
}

/// <summary>
/// Full file name with date yyyyMMdd
/// EX: C:/Test/MyFileName20110923
/// </summary>
public class CBFullPreDateNowNamePolicy : CBIFileNamePolicy
{
    string m_name;

    #region CBINamePolicy Members

    public string Name
    {
        get
        {
            return CBGeneral.GetFullPath(m_name + DateTime.Now.ToString("yyyyMMdd"));
        }
        set
        {
            m_name = value;
        }
    }

    #endregion
}

In these classes, the Name property will get a string for file name.

/// <summary>
/// The base policy interface for file extension name.
/// </summary>
public interface CBIFilenameExtensionPolicy : CBIFileNamePolicy
{
}

/// <summary>
/// txt extension policy
/// </summary>
public class CBTextExtensionPolicy : CBIFilenameExtensionPolicy
{

    #region CBIFilenameExtensionPolicy Members

    public string Name
    {
        get
        {
            return "txt";
        }
        set
        {
            throw new NotImplementedException();
        }
    }

    #endregion
}

/// <summary>
/// xml extension policy
/// </summary>
public class CBXmlExtensionPolicy : CBIFilenameExtensionPolicy
{

    #region CBIFilenameExtensionPolicy Members

    public string Name
    {
        get
        {
            return "xml";
        }
        set
        {
            throw new NotImplementedException();
        }
    }

    #endregion
}

/// <summary>
/// log extension policy
/// </summary>
public class CBLogExtensionPolicy : CBIFilenameExtensionPolicy
{

    #region CBIFilenameExtensionPolicy Members

    public string Name
    {
        get
        {
            return "log";
        }
        set
        {
            throw new NotImplementedException();
        }
    }

    #endregion
}

/// <summary>
/// int extension policy
/// </summary>
public class CBIniExtensionPolicy : CBIFilenameExtensionPolicy
{

    #region CBIFilenameExtensionPolicy Members

    public string Name
    {
        get
        {
            return "ini";
        }
        set
        {
            throw new NotImplementedException();
        }
    }

    #endregion
} 

These are readonly classes, just return extension name.

/// <summary>
/// The base policy interface for file name control.
/// </summary>
public interface CBIFileNameControlPolicy : CBIFileNamePolicy
{
    void Control();
}

/// <summary>
/// File split size
/// </summary>
public interface CBIFileSplitSizePolicy
{
    long MaxSplitSize { get; set; }
}

/// <summary>
/// Use for large file split
/// </summary>
public interface CBILargeFileSplitPolicy
{
    int Index { get; set; }
}

/// <summary>
/// File split policy
/// If file exist and file size large more than setting it will split file.
/// EX: 01, 02 or 03
/// </summary>
public class CBFileNumSplitPolicy : CBILargeFileSplitPolicy, CBIFileNameControlPolicy
{
    public CBFileNumSplitPolicy()
    {
        Index = 1;
    }
    public int Index { get; set; }

    public virtual string Name
    {
        get
        {
            return Index.ToString("00");
        }
        set
        {
            throw new NotImplementedException();
        }
    }

    public void Control()
    {
        Index++;
    }

}

/// <summary>
/// File split policy
/// If file exist and file size large more than setting it will split file.
/// EX: AA, AB or AC
/// </summary>
public class CBFileTextSplitPolicy : CBFileNumSplitPolicy
{
    public CBFileTextSplitPolicy() : base()
    {
        Index = 0;
    }

    public override string Name
    {
        get
        {
            int ch1 = (Index / 26) + 0x41;
            int ch2 = (Index % 26) + 0x41;
            return string.Format("{0}{1}",Convert.ToChar(ch1), Convert.ToChar(ch2));
        }
        set
        {
            throw new NotImplementedException();
        }
    }
}

These are file names split with split size property.

Sometimes the log file will split with file size, so I use this policy to control file name.

/// <summary>
/// Abstract base class for file name policy host.
/// How to use:
/// CBFileName filename = new CBFileName<CBIFileNamePolicy>();
/// CBFileName filename = new CBFileName<CBIFileNamePolicy,CBIFilenameExtensionPolicy>();
/// CBFileName filename = new CBFileName<CBIFileNamePolicy,
/// CBIFileNameControlPolicy,CBIFilenameExtensionPolicy>();
/// Just call fliename.Name to get file name
/// filename.MaxSplitSize will use for CBIFileNameControlPolicy
/// </summary>
public abstract class CBFileName : CBIFileNamePolicy, CBIFileSplitSizePolicy
{
    const long DEFAULT_SPLIT_SIZE = 1024 * 1024 * 5;
    //const long DEFAULT_SPLIT_SIZE = 100;
    public virtual string Name { get; set; }
    public long MaxSplitSize { get; set; }

    public CBFileName()
    {
        MaxSplitSize = DEFAULT_SPLIT_SIZE;
    }
} 

The CBFileName is an abstract class and just has two properties:

/// <summary>
/// This policy will get name from CBIFileNamePolicy
/// </summary>
/// <typeparam name="TNamePolicy">Must be CBIFileNamePolicy</typeparam>
public sealed class CBFileName<TNamePolicy> : CBFileName
    where TNamePolicy : CBIFileNamePolicy,new()
{
    TNamePolicy _policy;
    public CBFileName()
        : base()
    {
        _policy = new TNamePolicy();
    }
    public override string Name
    {
        get
        {
            return _policy.Name;
        }
        set
        {
            _policy.Name = value;
        }
    }
} 

This is first policy host class.

It accesses _policy.Name only.

/// <summary>
/// This policy will get name from CBIFileNamePolicy.Name + "." + 
/// CBIFilenameExtensionPolicy.Name
/// </summary>
/// <typeparam name="TNamePolicy">Must be CBIFileNamePolicy</typeparam>
/// <typeparam name="TNameExtensionPolicy">Must be CBIFilenameExtensionPolicy</typeparam>
public sealed class CBFileName<TNamePolicy, TNameExtensionPolicy> : CBFileName
    where TNamePolicy : CBIFileNamePolicy, new()
    where TNameExtensionPolicy : CBIFilenameExtensionPolicy, new()
{
    TNamePolicy _policy1;
    TNameExtensionPolicy _policy2;
    public CBFileName()
        : base()
    {
        _policy1 = new TNamePolicy();
        _policy2 = new TNameExtensionPolicy();
    }
    public override string Name
    {
        get
        {
            return _policy1.Name + "." + _policy2.Name;
        }
        set
        {
            _policy1.Name = value;
        }
    }
} 

This policy host class has two generic arguments.

In this role, CBFileName.Name will return _policy1.Name + "." _policy2.Name.

The _policy1 is CBIFileNamePolicy, _policy2 is CBIFilenameExtensionPolicy.

CBFileName name1 =
    new CBFileName<CBFileNamePolicy, CBTextExtensionPolicy>();
name1.Name = "Test1";
Console.WriteLine(name1.Name);

We can use it like this sample.

The Name property will get Test1.txt.

/// <summary>
/// This policy will get name from
/// CBIFileNamePolicy.Name + "_" + CBIFileNameControlPolicy.Name + 
/// "." + CBIFilenameExtensionPolicy.Name
/// </summary>
/// <typeparam name="TNamePolicy">Must be CBIFileNamePolicy</typeparam>
/// <typeparam name="TNameControlPolicy">Must be CBIFileNameControlPolicy</typeparam>
/// <typeparam name="TNameExtensionPolicy">
/// Must be CBIFilenameExtensionPolicy</typeparam>
public sealed class CBFileName<TNamePolicy, TNameControlPolicy, 
	TNameExtensionPolicy> : CBFileName, CBIFileNameControlPolicy
    where TNamePolicy : CBIFileNamePolicy, new()
    where TNameControlPolicy : CBIFileNameControlPolicy, new()
    where TNameExtensionPolicy : CBIFilenameExtensionPolicy, new()
{
    TNamePolicy _policy1;
    TNameControlPolicy _policy2;
    TNameExtensionPolicy _policy3;

    public CBFileName()
        : base()
    {
        _policy1 = new TNamePolicy();
        _policy2 = new TNameControlPolicy();
        _policy3 = new TNameExtensionPolicy();
    }

    string FileName
    {
        get
        {
            return _policy1.Name + "_" + _policy2.Name + "." + _policy3.Name;
        }
    }

    public override string Name
    {
        get
        {
            Control();
            return FileName;
        }
        set
        {
            _policy1.Name = value;
        }
    }

    public void Control()
    {
        while (true)
        {
            FileInfo info = new FileInfo(FileName);
            if (info.Exists && info.Length > MaxSplitSize)
            {
                _policy2.Control();
            }
            else
            {
                break;
            }
        }
    }
} 

This policy host class has three generic arguments.

In this role, CBFileName.Name will return_policy1.Name + "_" _policy2.Name + "." _policy3.Name.

The _policy1 is CBIFileNamePolicy, _policy2 is CBIFileNameControlPolicy, _policy3 is CBIFilenameExtensionPolicy.

The implemented control will get file name with MaxSplitSize.

CBFileName nameNormal =
   new CBFileName<CBFullPreDateNowNamePolicy, 
	CBFileNumSplitPolicy, CBLogExtensionPolicy>();
nameNormal.Name = "Log\\Event_Normal_";
Console.WriteLine(nameNormal.Name); 

In this sample, the Name property is: D:\CloudDevelop\PolicySample\PolicySample\bin\Debug\Log\Event_Normal_20111003_01.log

References

History

  • V1.1 Added explanation about policy based design
  • V1.0 First done

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here