4

I've been looking for a solution to a complex problem we're having in my company. This company is part of an alliance of 4 companies that covers our country in four "regions". Our branch developed a WebService in C# and we distribute this project to our fellow developers in the other branches. Everyone hosts the WebService in their own servers.

Now, I've been struggling with something you can expect when the companies don't get along. I have to adapt an existing method to suit our "regional needs".

So I've got this class :

public partial class MyClass{
     public static ComplexReturnType MyMethod(){
          // National code. Everyone uses this block of code.
     }
}

I created a Regional folder that I will exclude from compilation when distributing the DLL to the other branches. Inside this folder I created the file MyClass.cs and went on with this :

public partial class MyClass{
     public static ComplexReturnType MyMethod(){
          // Regional code. Only my commpany will use this.
     }
}

The method MyMethod is called in other files. I understand how partial works, but I can't find a solution that suits my needs without creating a child class and rewriting every call already present in the other files.

Does anyone have an idea on how to deal with this ?

Edit after answer

I decided to go with the Strategy Design Pattern, and when I was done I thought "If a branch decides to overwrite any method, all the other branches have to override the same method with the national code in their regional strategy class".

So that's not really nice. Instead I did this :

public class VStrategy
{
    public virtual ComplexReturnType MyMethod(){
        // National code. Method moved from MyClass
    }
    public virtual AnotherReturnType MySecondMethod(){
        // National code. Method moved from MyClass
    }
}

public class SomeBranchStrategy: VStrategy
{
    public override ComplexReturnType MyMethod() {
        // Regional code for overriding a method
    }
}

public class AnotherBranchStrategy: VStrategy
{
    public override AnotherReturnType MySecondMethod(){ {
        // Regional code for overriding a method
    }
}

public class MyClass
{
    private static VStrategy _strategy = new VStrategy();
    public static VSTrategy Strategy { get {...}; set {...} }
    public static ComplexReturnType MyMethod()
    {
        return Strategy.MyMethod();
    }
    public static ComplexReturnType MySecondMethod()
    {
        return Strategy.MySecondMethod();
    }
}

This way, without the Interface, every branch can override any method they want without an impact for the other branches. You just move the method code to the VStrategy class and override it in your own Regional class.

Hope this helps anyone who could be in this situation.

David Clarke
  • 12,888
  • 9
  • 86
  • 116
shajz
  • 94
  • 3
  • 7
  • 2
    Which method should be chosen as a result? – zerkms Sep 19 '13 at 08:05
  • `if(myCompany){MyCompanyMethod();} else {OtherMethod();}` – Sriram Sakthivel Sep 19 '13 at 08:09
  • possible duplicate of [Override a virtual method in a partial class](http://stackoverflow.com/questions/17241122/override-a-virtual-method-in-a-partial-class) – Anya Shenanigans Sep 19 '13 at 08:10
  • 1
    Looks like you're making the problem even mode complex by using partial classes. Using good ol' Object Composition/Inheritance would be the way to go IMHO. – Cristian Lupascu Sep 19 '13 at 08:11
  • You could use partial methods but I think your concept of globalization and localization is not a luky choice. MS recommands the use of satellite assemblies. – Rene Niediek Sep 19 '13 at 08:16
  • 2
    @Extra: Now you have first-hand experience of why `static` easily leads to development dead-ends. If the method were `virtual` (and/or part of an interface) it would have been a "simple" matter of replacing the instance on which it is called with an instance of a more specialized class (you would not need to rewrite each call). Take the lesson home. – Jon Sep 19 '13 at 08:23
  • Unless you use conditional compilation symbols. This cannot be done. You can add a tag of ur region name and put your code in that tag and add the national code method in another tag. – Akanksha Gaur Sep 19 '13 at 08:39
  • Lots of `static` in there, in which case `MyClass` should be declared `static`. But a better approach is to avoid static classes and use constructor injection to inject the appropriate strategy into the class. You can still have a default `VStrategy()` that implements the virtual methods. – David Clarke Jun 24 '15 at 02:23

4 Answers4

8

Like Maurice Stam said, encapsulate what varies. At first glance, I would use the strategy pattern: http://www.oodesign.com/strategy-pattern.html

public interface IStrategy
{
    ComplexReturnType MyMethod();
}

public class NationalDefaultStrategy : IStrategy
{
    public ComplexReturnType MyMethod() { }
}

public class BostonStrategy: IStrategy
{
    public ComplexReturnType MyMethod() { }
}

public class MyClass
{
    private static IStrategy _strategy = new NationalDefaultStrategy();
    public static ISTrategy Strategy { get {...}; set {...} }
    public static ComplexReturnType MyMethod()
    {
        return _strategy.MyMethod();
    }
}

This way, you can easily change the strategy being used at runtime

MyClass.Strategy = new BostonStrategy();

If you make it an instance method instead of a static one (I probably would) and decide to use an IoC container like Castle Windsor, you could even wire up the strategy in a configuration file.

Edit

Logically, each branch would have their own configuration file. There are two advantages in using this approach:

dcastro
  • 66,540
  • 21
  • 145
  • 155
2

There are a few problems with your current approach.

  1. Static methods cannot be virtual.
  2. You should encapsulate what varies. That implies that the company-specific code should be at least in a separate class (preferably a separate assembly). You will need to provide the base assembly with your company specific code. You can use the strategy pattern for this in combination with IoC / Dependency Injection.
  3. A risk with making the base method virtual while it contains code is that the reused code will be duplicated. A solution for this is to create a sealed method that contains the generic code, that uses abstract (or empty virtual) methods for the company that will be called from within the sealed method. In combination with my previous point, this implies calling a method that has been implemented on a injected instance.
  4. If the problem is restricted localization only you should to use the solutions provided by .NET (i.e. sattelite assemblies) that contain culture specific resources. However you divide the same country in 4 regions therefore I assume you have region-specific logic instead of localization problems.

Sample code that implements the suggestions above:

public class SharedClass
{
    private readonly IRegionCode m_RegionLogic;

    // Constructor with dependency injection
    public SharedClass(IRegionCode mRegionLogic)
    {
        this.m_RegionLogic = mRegionLogic;
    }

    // Method called by the webservice
    public void YourMethod()
    {
        // reused base logic here
        Trace.Write("Somehting");

        // Invoke region specific code
        m_RegionLogic.Foo();

        // reused base logic here
        Trace.Write("Somehting");
    }
}

// Contract for regions to implement
public interface IRegionCode
{
    void Foo();
}

// Example of an injected class
public class FirstRegionCode : IRegionCode
{
    public void Foo()
    {
        Trace.Write("Bar");
    }
}
Myrtle
  • 5,761
  • 33
  • 47
  • Yes. Basically the thing should be solved by Composition and Configuration, not by partial or inheritance. – H H Sep 19 '13 at 08:31
1

I think you misunderstand what a partial class is for! Creating a partial class simply means that the source code for the class is split into separate files. There's not difference to writing all the source code into the same file, so basically what you've written is:

public class MyClass{
     public static ComplexReturnType MyMethod(){
          // National code. Everyone uses this block of code.
     }

     public static ComplexReturnType MyMethod(){
          // Regional code. Only my commpany will use this.
     }
}

See the problem?

Thorsten Dittmar
  • 55,956
  • 8
  • 91
  • 139
1

You need to use compilation tags to avoid For example:

public partial class MyClass
    {
#if National
        public static void NationalMethod()
        {

        }
#endif

    }


    public partial class MyClass
    {
#if Regional
        public static void NationalMethod()
        {

        }
#endif
    }

In Project properties, Build-> Conditional compilation symbols: add Regional or National based on the region of requirement.

Akanksha Gaur
  • 2,636
  • 3
  • 26
  • 50
  • Compilation tags are brittle and not a useful approach when there are existing patterns available to resolve the issue using standard language features. – David Clarke Jun 24 '15 at 01:52