Title: A simple interface based versioning of objects
Slug: a-simple-interface-based-versioning-of-objects
Date: 2009-03-07 09:29:00
Author: Kartones
Lang: en
Tags: Development, .NET, C#
Description: A simple interface-based versioning of objects in .NET.

 <p>By using reflection and assembly loading, we can create robust plugin-based architectures. It can be very very robust (we can check that a class implements a specific interface, then search for its methods, then even check that the parameters are ok...), but in some scenarios that performance is vital this might not be the most optimum approach.</p>  <p>So here it is a very very simple interface based versioning of objects (even compatible any .NET version), so you can maintain compatibility (for example in web services exposed to third parties) while avoiding too much code.</p>  <p>First of all let's define a basic interface (for the 1.0 version) of a simple text logger:</p>  <p><font face="Courier New">    <font color="#0000ff">public interface </font></font><font face="Courier New"><font color="#000000">ITextLogger       <br>    {        <br>        </font><font color="#0000ff">void </font><font color="#000000">OutputText(</font><font color="#0000ff">string </font><font color="#000000">Text)</font></font><font face="Courier New"><font color="#0000ff">;       <br>    </font><font color="#000000">}</font></font></p>  <p>And we implement both a 1.0 and a 1.1 version (that actually do the same, just to see how when using it the highest version is called):</p>  <p><font face="Courier New">    <font color="#0000ff">public class </font></font><font face="Courier New"><font color="#000000">TextLogger10 : ITextLogger       <br>    {        <br>        </font><font color="#0000ff">public void </font><font color="#000000">OutputText(</font><font color="#0000ff">string </font></font><font face="Courier New"><font color="#000000">Text)       <br>        {        <br>            Console.WriteLine(</font><font color="#808080">"v:1.0 Text: {0}"</font><font color="#000000">, Text)</font></font><font face="Courier New" color="#0000ff">;     <br>        </font><font face="Courier New" color="#000000">}     <br>    }</font> </p>  <p><font face="Courier New">    <font color="#0000ff">public class </font></font><font face="Courier New"><font color="#000000">TextLogger11 : ITextLogger       <br>    {        <br>        </font><font color="#0000ff">public void </font><font color="#000000">OutputText(</font><font color="#0000ff">string </font></font><font face="Courier New"><font color="#000000">Text)       <br>        {        <br>            Console.WriteLine(</font><font color="#808080">"v:1.1 Text: {0}"</font><font color="#000000">, Text)</font></font><font face="Courier New" color="#0000ff">;     <br>        </font><font face="Courier New" color="#000000">}     <br>    }</font></p>  <p>Then, a request comes to make another logging method, so we define a new interface:</p>  <p><font face="Courier New">    <font color="#0000ff">public interface </font></font><font face="Courier New"><font color="#000000">ITextLogger20 : ITextLogger       <br>    {        <br>        </font><font color="#0000ff">void </font><font color="#000000">OutputMoreText(</font><font color="#0000ff">string </font><font color="#000000">Text)</font></font><font face="Courier New"><font color="#0000ff">;       <br>    </font><font color="#000000">}</font></font></p>  <p>And the new class, let's increase it to 2.0 to match the highest interface version:</p>  <p><font face="Courier New">    <font color="#0000ff">public class </font></font><font face="Courier New"><font color="#000000">TextLogger20 : ITextLogger20       <br>    {        <br>        </font><font color="#0000ff">public void </font><font color="#000000">OutputText(</font><font color="#0000ff">string </font></font><font face="Courier New"><font color="#000000">Text)       <br>        {        <br>            Console.WriteLine(</font><font color="#808080">"v:2.0 Text: {0}"</font><font color="#000000">, Text)</font></font><font face="Courier New" color="#0000ff">;     <br>        </font><font face="Courier New"><font color="#000000">}       <br>        <br>        </font><font color="#0000ff">public void </font><font color="#000000">OutputMoreText(</font><font color="#0000ff">string </font></font><font face="Courier New"><font color="#000000">Text)       <br>        {        <br>            Console.WriteLine(</font><font color="#808080">"v:2.0 MoreText: {0}"</font><font color="#000000">, Text)</font></font><font face="Courier New" color="#0000ff">;     <br>        </font><font face="Courier New" color="#000000">}     <br>    }</font> </p>  <p>Note than although C# supports multiple interface inheritance, I prefer to make the interfaces inherit previous ones to keep a simple inheritance at class level. This way if a junior developer joins the team he can easily understand how to increment versions.</p>  <p>Finally, to have flexibility (like defining the version to use in a .config file), let's make a very simplified implementation of a Factory design pattern (I like using enums to force people to use only existing ones and avoid errors, but you could change it to a simple string parameter):</p>  <p><font face="Courier New" color="#0000ff">    public enum </font><font face="Courier New"><font color="#000000">TexTLoggerVersions       <br>    {        <br>        Version10 </font><font color="#0000ff">= </font><font color="#800000">1</font></font><font face="Courier New"><font color="#000000">,       <br>        Version11 </font><font color="#0000ff">= </font><font color="#800000">2</font></font><font face="Courier New"><font color="#000000">,       <br>        Version20 </font><font color="#0000ff">= </font></font><font face="Courier New" color="#800000">3     <br>    </font><font face="Courier New"><font color="#000000">}       <br>        <br>    </font><font color="#0000ff">public static class </font></font><font face="Courier New"><font color="#000000">TextLoggerFactory       <br>    {        <br>        </font><font color="#0000ff">public static </font></font><font face="Courier New"><font color="#000000">ITextLogger GetInstance(TexTLoggerVersions TextLoggerVersion)       <br>        {        <br>            </font><font color="#0000ff">switch </font></font><font face="Courier New"><font color="#000000">(TextLoggerVersion)       <br>            {        <br>                </font><font color="#0000ff">case </font></font><font face="Courier New"><font color="#000000">TexTLoggerVersions.Version10:       <br>                    </font><font color="#0000ff">return new </font><font color="#000000">TextLogger10()</font></font><font face="Courier New" color="#0000ff">;     <br>                case </font><font face="Courier New"><font color="#000000">TexTLoggerVersions.Version11:       <br>                    </font><font color="#0000ff">return new </font><font color="#000000">TextLogger11()</font></font><font face="Courier New" color="#0000ff">;     <br>                case </font><font face="Courier New"><font color="#000000">TexTLoggerVersions.Version20:       <br>                    </font><font color="#0000ff">return new </font><font color="#000000">TextLogger20()</font></font><font face="Courier New" color="#0000ff">;     <br>                default</font><font face="Courier New" color="#000000">:     <br>                    </font><font face="Courier New" color="#0000ff">return null;     <br>            </font><font face="Courier New" color="#000000">}     <br>        }      <br>    }</font> </p>  <p>And now we can build a simple console application to test how this all works...</p>  <p> </p>  <div class="code"><font face="Courier New">    <font color="#0000ff">class </font></font><font face="Courier New"><font color="#000000">Program       <br>    {        <br>        </font><font color="#0000ff">static void </font><font color="#000000">Main(</font><font color="#0000ff">string</font></font><font face="Courier New" color="#000000">[] args)     <br>        {      <br>            <font color="#69b6ac">ITextLogger</font> myTextLog</font><font face="Courier New"><font color="#0000ff">;       <br>        <br>            </font><font color="#000000">myTextLog </font><font color="#0000ff">= </font><font color="#000000"><font color="#69b6ac">TextLoggerFactory</font>.GetInstance(<font color="#69b6ac">TexTLoggerVersions</font>.Version10)</font></font><font face="Courier New"><font color="#0000ff">;       <br>            </font><font color="#000000">myTextLog.OutputText(</font><font color="#808080">"Log 001"</font><font color="#000000">)</font></font><font face="Courier New"><font color="#0000ff">;       <br>        <br>            </font><font color="#000000">myTextLog </font><font color="#0000ff">= </font><font color="#000000"><font color="#69b6ac">TextLoggerFactory</font>.GetInstance(<font color="#69b6ac">TexTLoggerVersions</font>.Version11)</font></font><font face="Courier New"><font color="#0000ff">;       <br>            </font><font color="#000000">myTextLog.OutputText(</font><font color="#808080">"Log 002"</font><font color="#000000">)</font></font><font face="Courier New"><font color="#0000ff">;       <br>        <br>            </font><font color="#000000">myTextLog </font><font color="#0000ff">= </font><font color="#000000"><font color="#69b6ac">TextLoggerFactory</font>.GetInstance(<font color="#69b6ac">TexTLoggerVersions</font>.Version20)</font></font><font face="Courier New"><font color="#0000ff">;       <br>            </font><font color="#000000">myTextLog.OutputText(</font><font color="#808080">"Log 003"</font><font color="#000000">)</font></font><font face="Courier New"><font color="#0000ff">;       <br>        <br>            </font><font color="#000000">((<font color="#69b6ac">ITextLogger20</font>)myTextLog).OutputMoreText(</font><font color="#808080">"Log 004"</font><font color="#000000">)</font></font><font face="Courier New"><font color="#0000ff">;       <br>        <br>            </font><font color="#000000"><font color="#69b6ac">ITextLogger20</font> myOtherTextLog </font><font color="#0000ff">= </font><font color="#000000">(<font color="#69b6ac">ITextLogger20</font>)<font color="#69b6ac">TextLoggerFactory</font>.GetInstance(<font color="#69b6ac">TexTLoggerVersions</font>.Version20)</font></font><font face="Courier New"><font color="#0000ff">;       <br>        <br>            </font><font color="#000000">myOtherTextLog.OutputText(</font><font color="#808080">"Log 005"</font><font color="#000000">)</font></font><font face="Courier New"><font color="#0000ff">;       <br>            </font><font color="#000000">myOtherTextLog.OutputMoreText(</font><font color="#808080">"Log 006"</font><font color="#000000">)</font></font><font face="Courier New"><font color="#0000ff">;       <br>        <br>            </font><font color="#000000">Console.ReadLine()</font></font><font face="Courier New" color="#0000ff">;     <br>        </font><font face="Courier New" color="#000000">}     <br>    }</font>     <p> </p>    <p>This would be the output of running the console app:</p>    <p><font face="Courier New">v:1.0 Text: Log 001       <br>v:1.1 Text: Log 002        <br>v:2.0 Text: Log 003        <br>v:2.0 MoreText: Log 004        <br>v:2.0 Text: Log 005        <br>v:2.0 MoreText: Log 006</font></p> </div>  <div class="code"> </div>  <p>The console application shows two ways of handling higher versions, one by casting to the higher interface when calling the new methods, another by casting when obtaining the instance.    <br>I prefer the second one because you get Intellisense of the higher version ;)</p>  <p>I could surely be improved but this was done in less than 10 minutes and even a monkey could use it!</p>
