jeudi 25 juillet 2019

Building a factory that returns class instances instantiated with different parameters?

I'm building a small policy system for passwords and usernames. These policies can be configured based on a variety of different factors, but for the most part they're relatively straight forward. The policies all implement IPolicy which looks something like:

public interface IPolicy
{
    (bool, ErrorResponse) Verify(string input);
}

Some of the policies require certain parameters to be passed to them during instantiation, such as minimumLength. An example policy may look something like:

public class LowerCasePolicy : IPolicy
{
    private const string _defaultTitle = "LowerCaseCount";
    private readonly int _minimumLength;
    private readonly string _errorMessage;
    private readonly string _errorTitle;

    public LowerCasePolicy(int minimumLength)
    {
        _minimumLength = minimumLength;
        _errorMessage =
            $"Password does not meet the lower case character count requirement set by the password policy ({_minimumLength})";
        _errorTitle = _defaultTitle;
    }

    public LowerCasePolicy(int minimumLength, string errorMessage, string errorTitle = _defaultTitle)
    {
        _minimumLength = minimumLength;
        _errorMessage = errorMessage;
        _errorTitle = errorTitle;
    }

    public (bool, ErrorResponse) Verify(string input)
    {
        var enoughUpper = Regex.Matches(input, "[a-z]").Count >= _minimumLength;
        return !enoughUpper ?
            (false, new ErrorResponse(_errorTitle, _errorMessage))
            : (true, null);
    }
}

I'm trying to build some sort of factory that's capable of returning all of my different policies with their different constructors, but I'm not too sure where to go from here. One potential option I came up with was to create a base PolicyArgs class to pass the parameters, and I could use derived classes for each. Like so:

public class PolicyArgs
{
    public string Title { get; set; }
    public string ErrorMessage { get; set; }
}

public class LowerCaseArgs : PolicyArgs
{
    public int MinimumLength { get; set; }
}

And the constructor for the policy would now look like:

public LowerCasePolicy(PolicyArgs args)
{
    if (args == null)
        throw new ArgumentException();

    if (!(args is LowerCaseArgs lowerCaseArgs))
        throw new ArgumentException();

    _minimumLength = lowerCaseArgs.MinimumLength;
    _errorTitle = lowerCaseArgs.Title ?? _defaultTitle;
    _errorMessage = lowerCaseArgs.ErrorMessage ??  $"Password does not meet the lower case character count requirement set by the password policy ({_minimumLength})";
}

And the factory would look like:

public class PolicyFactory
{
    private readonly Dictionary<Policy, Func<PolicyArgs, IPolicy>> _policyDictionary = new Dictionary<Policy, Func<PolicyArgs, IPolicy>>
    {
        [Policy.LowerCase] = (args) => new LowerCasePolicy(args)
    };

    public IPolicy Create(Policy policy, PolicyArgs args)
    {
        return _policyDictionary[policy](args);
    }
}

I'm not sure if this is truly the best approach, or if there's some better option for handling different policies with different constructor needs. The goal is to be able to simply put these configurations in a database and have my PolicyProvider effectively return an array of IPolicy.

Aucun commentaire:

Enregistrer un commentaire