Home > database >  C# refactoring Facade for API
C# refactoring Facade for API

Time:03-11

I create a dll for my application. I am using Facade design pattern for encapsulate API who makes some programmers (not my organization) because they decision is uncomfortable.

Their API works like this:

  1. Initialize object of DiadocApi
  2. Auth for getting token

For initialize DiadocApi object I need developerKey (get for a subscription). For authorization I need login, password.

My decision badly because it's a singleton and I need make unit-tests. That can I change in my code?

//I GET IT FROM NUGET PACKAGE
using Diadoc.Api;

public sealed partial class DiadocApiFacade
{
    private static readonly object _mutex = new object();

    private static DiadocApiFacade _instance;
    private string _token;
    private DiadocApi _api;

    private DiadocApiFacade() { }

    public static string DefaultUrl => "url was here";
    public string DefaultFromBoxId { get; set; }
    public DiadocApi Api { get => _api; private set => _api = value; }
    
    public static DiadocApiFacade GetInstance()
    {
        if (_instance == null)
        {
            lock (_mutex)
            {
                if (_instance == null)
                {
                    _instance = new DiadocApiFacade();
                }
            }
        }
        return _instance;
    }

    public string Authenticate(string login, string password, string privateDeveloperKey)
    {
        if (string.IsNullOrEmpty(login) || string.IsNullOrEmpty(password) || string.IsNullOrEmpty(developerKey))
            throw new ArgumentNullException();

        Api = new DiadocApi(developerKey, DefaultUrl, new WinApiCrypt());

        return _token = _api.Authenticate(login, password);
    }

    //method for example, >60% methods like that
    public Document GetDocument(string messageId, string documentId, string boxId = null)
    {
        return _api.GetDocument(_token, boxId ?? DefaultFromBoxId, messageId, documentId);
    }
}

CodePudding user response:

I assume you want to make unit tests for your code that's consuming DiadocApiFacade.

Dependency inversion to the rescue! Abstract your DiadocApiFacade with an interface. Make use of the Factory Pattern to create the object. Below is a simple example but you can expand the factory if you need the facade to behave a certain way (like a settings class to change where it's connecting to).

GetOrCreate Will return the same instance but if you're using a dependency injection framework in your project (there are many that might suite your needs), use that instead.

public interface IDiadocApiFacade
{
    string Authenticate(string login, string password, string privateDeveloperKey);
    public Document GetDocument(string messageId, string documentId, string boxId);
    public Document GetDocument(string messageId, string documentId);

}

public class DiadocApiFacadeFactory
{
    private static IDiadocApiFacade? _instance = null;

    public IDiadocApiFacade Create() //Add parameters 
    {
        return new DiadocApiFacade();
    }

    public IDiadocApiFacade GetOrCreate()
    {
        if(_instance == null)
            _instance = Create();

        return _instance;
    }
}

public sealed partial class DiadocApiFacade : IDiadocApiFacade
{
    internal DiadocApiFacade() { }

    public string Authenticate(string login, string password, string privateDeveloperKey) => string.Empty;
    

    public Document GetDocument(string messageId, string documentId)
        => GetDocument(messageId, documentId, null);

    public Document GetDocument(string messageId, string documentId, string? boxId) => new Document();
    
}

Then you can just make use of [moq)[https://www.nuget.org/packages/moq/] for your unit testing and fake the implementation of IDiadocApiFacade

  • Related