0

I have a somewhat basic design question that I have not been able to find a good answer to (here, on other forums nor the books I've consulted)

I'm creating a dll and is wondering what the best way to expose its content would be. I'm aiming for a single point of entry for the apps using the dll.

The solution should adhere to the Dependency Inversion Principle (DIP) which would imply the use of an interface. But here is the kicker: the functionality of the dll requires an object to be instantiated and there must only be almost one instance at any time (kinda like a singleton though the thought sends shivers down my spine) It is this fact that I would like to spare the users of the DLL from knowing about.

Some code to explain what I would like to be able to do:

The dll:

namespace MyQuestionableDll
{
    interface IMyQuestionableDll
    {
        public static IMyQuestionableDll Instance; // This is not allowed which makes sense
        public void PressTheRedButton();
    }

    internal class QuestionableImplementation : IMyQuestionableDll
    {
        public void PressTheRedButton()
        {
            // Distribute Norwegian Black Metal at local school
        }
    }
}

And the use case:

using MyQuestionableDll;

class UnluckyAppThatIsForcedToUseQuestionableDlls
{
    static void Main(string[] args)
    {
        IMyQuestionableDll questionableInstance = IMyQuestionableDll.Instance; // Again not allowed which still makes sense
        questionableInstance.PressTheRedButton();
        // or simply
        MyQuestionableDll.Instance.PressTheRedButton();
    }
}

An abstract class could be part of the answer but it then starts to feel like not following the DIP anymore.

Any great design insights, knowledge of best practices when making dlls or recommendations regarding Norwegian Black Metal?

If the explanation is too vague I will gladly elaborate on it.

Cheers! - Jakob

  • What about a factory method to obtain the instance? You can make the constructor internal to avoid creation from outside the DLL – ema Jan 27 '22 at 12:17

2 Answers2

0

I could imagine a two-factor approach:

  1. A factory interface (that will create/return an instance of ...)
  2. The API interface

For Example:

public interface IMyApiFactory
{
    IMyAPI GetInstance();
}

public interface IMyAPI
{
   // Whatever your API provides
}

This way you have the complete control inside your dll about how to create and reuse instances (or not).


A similar way would be some kind of Builder-Pattern:

public interface IMyApiBuilder
{
    IMyApi Build();
}

public interface IMyApi 
{
   void PressTheRedButton();

   // Whatever it does else
}

public sealed class MyAPI : IMyApi, IMyApiBuilder
{
    public static IMyApiBuilder Builder = new MyAPI();

    private MyAPI() 
    {
        // CTOR ...
    }

    // vv Notice _explicit_ interface implemenations.
    //           https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/interfaces/explicit-interface-implementation

    IMyApi IMyApiBuilder.Build() { return this; }

    void IMyApi.PressTheRedButton()
    {
         // ...
    }
}

// USAGE:

IMyApi api = MyAPI.Builder.Build();
Fildor
  • 14,510
  • 4
  • 35
  • 67
0

Thank you so much Fildor and ema! The ideas of adding a factory method, a factor interface or even a full-blown builder is definitely good solutions! And it got me thinking about how to incorporate them into my DLL while still sparing its users. They don't need to know that it in fact is a foul smelling factory with polluting smokestack emissions that are creating the instance :)

Then I came across Default Interface Methods and the structure of my approach ended up being

public interface MyQuestionableDll
{
    static readonly MyQuestionableDll Instance = new QuestionableImplementation(); 
    void PressTheRedButtonBelowTheDoNotPushSign();
}

internal class QuestionableImplementation : MyQuestionableDll
{
    public void PressTheRedButtonBelowTheDoNotPushSign()
    {
        // Distribute German Volksmusik at local school
    }
}

// and the use case

var questionableInstance = MyQuestionableDll.Instance;
questionableInstance.PressTheRedButtonBelowTheDoNotPushSign();

Thanks again Fildor and ema!

- Jakob