Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to make pluggable static classes

I know how to make pluggable things in c#. Define an Interface, Activator.CreateInstance(<class>), etc. Or I can have a code path that explicitly creates an instance of the plugged class. Many ways.

But what if the service I want to make pluggable is static (I know I could refactor so that it's not but that's not the point of the question)

Concrete example. I have a class that provides disk I/O abstraction (Read File, List Directory,....). Now I want different implementations of this abstraction that serves up files from , say, a real FS, a database.

Based on Olivier Jacot-Descombes reply, I will have a FileSystem class (that is real) like this

public static class FileSystem
{
    static IFSImplemenation s_imple;
    static FileSystem()
    {
        if(<some system setting>)
            // converted to singleton instead of static
            s_imple = new OldFileSystem() 
        else
            s_imple = new DbFileSystem();
    }

    public static byte[] ReadFile(string path)
    {
        return s_imple.ReadFile(path);
    }

    ...
}

To reiterate - I have a large body of code that I dont want to change so it was important to keep the calling signature the same - this solution achieves that

like image 329
pm100 Avatar asked May 12 '26 14:05

pm100


2 Answers

You can't, and that's a limitation of the type system in .NET and most object-oriented systems/languages.

The reason being that "pluggable things" (as you refer to them) require polymorphism in order to be effective.

You define a contract (an interface, in .NET-speak), and then you implement that contract. Your system only ever deals with the contract.

Static members are not polymorphic in .NET, so you can never get them to implement a contract.

For your example of disk I/O abstraction, you wouldn't create a static class for this, you'd create an interface, and implement the interface, passing around the interface.

Of course, the benefits of using an interface are that it's easier to test both sides of the implementation of your contract:

  • Where the contract is a client, you can mock the interface easily and pass the mock to the consumers of the contract (this is where you might start thinking about dependency injection)
  • You can test the implementation separately from the rest of your system.

In the case of your disk I/O abstraction, for everything that calls your contract, you would never have to worry about actually touching the file system, you simply have to make the contract behave (through proper mock setup) as if it were touching the file system.

If you have an existing service that is exposed through static members, and you want the ability to swap that out with another implementation, then you'll have to do the following:

  • Create an abstraction (contract/abstract type) which defines the operations on your service.
  • Create an implementation that forwards the calls from the instance of the implementation of the abstraction to the static methods.
  • Rewire all of the static calls to the service with instances of the implementation. At this point, you really must use dependency injection of some sort; if you don't, you're just creating the concrete implementation at the call site, and that's pretty much the same (you'd have to change every call site again if you wanted to use another implementation).
like image 175
casperOne Avatar answered May 14 '26 05:05

casperOne


Use your static class as facade for non-static implementations

public static class DB
{
    private static IDbInterface _implementation;

    public static void SetImplementation(IDbInterface implementation)
    {
        _implementation = implementation;
    }

    public static Customer GetCustomerByID(int custId)
    {
        return _implementation.GetCustomerByID(custId);
    }

    ...
}
like image 22
Olivier Jacot-Descombes Avatar answered May 14 '26 04:05

Olivier Jacot-Descombes



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!