我正在为密码和用户名构建一个小型策略系统。这些策略可以基于各种不同的因素进行配置,但在大多数情况下,它们都是相对简单的。这些策略都实现了IPolicy
,如下所示:
public interface IPolicy
{
(bool, ErrorResponse) Verify(string input);
}
某些策略需要在实例化过程中向其传递某些参数,例如minimumLength
。示例策略可能如下所示:
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);
}
}
我正在尝试建立某种类型的工厂,它能够通过不同的构造函数返回我所有不同的策略,但我不太确定从哪里开始。我想到的一个可能的选择是创建一个PolicyArgs
基类来传递参数,我可以为每个参数使用派生类。如下所示:
public class PolicyArgs
{
public string Title { get; set; }
public string ErrorMessage { get; set; }
}
public class LowerCaseArgs : PolicyArgs
{
public int MinimumLength { get; set; }
}
策略的构造函数现在将如下所示:
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})";
}
工厂看起来会像这样:
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);
}
}
我不确定这是否真的是最好的方法,或者是否有更好的选择来处理具有不同构造函数需求的不同策略。我们的目标是能够简单地将这些配置放在数据库中,并让我的PolicyProvider
有效地返回一个IPolicy
数组。
正如所建议的,另一种选择是使用类似于字典的东西来处理附加参数,例如:
public class PolicyArgs
{
public string Title { get; set; }
public string ErrorMessage { get; set; }
// There's almost definitely a better way to handle this;
public Dictionary<string, object> AdditionalParameters { get; set; } = new Dictionary<string, object>();
}
public LowerCasePolicy(PolicyArgs args)
{
if (args == null)
throw new ArgumentException();
if (!args.AdditionalParameters.ContainsKey("MinimumLength"))
throw new ArgumentException("Minimum Length is not provided.");
if (!int.TryParse(args.AdditionalParameters["MinimumLength"].ToString(), out var minimumLength))
throw new ArgumentException("Minimum Length is invalid.");
_minimumLength = minimumLength;
_errorTitle = args.Title ?? _defaultTitle;
_errorMessage = args.ErrorMessage ?? $"Password does not meet the lower case character count requirement set by the password policy ({_minimumLength})";
}
这种方法非常棒,因为它使得在数据库或应用程序配置文件中以JSON格式存储数据变得非常容易。然而,它有一些明显的缺点。首先,任何字符串中的类型都可能是灾难性的。它还需要我手动处理类型转换和检查。
我不反对这种方法,但我觉得肯定有更好的选择。
最初的用法(每个策略都有强类型的类)如下所示:
public void TestPolicyFactory()
{
var args = new LowerCasePolicy.LowerCaseArgs {MinimumLength = 1};
var factory = new PolicyFactory();
var policy = factory.Create(Policy.LowerCase, args);
Assert.IsNotNull(policy);
}
更改后的字典如下所示:
public void TestPolicyFactory()
{
var args = new PolicyArgs();
args.AdditionalParameters.Add("MinimumLength", 1);
var factory = new PolicyFactory();
var policy = factory.Create(Policy.LowerCase, args);
Assert.IsNotNull(policy);
}
发布于 2019-07-25 16:04:11
老实说,你的问题会得到非常固执己见的答案,所以它可能不符合堆栈溢出规则。
我的观点是:
我会给我的所有策略一个相同的参数:一个字符串形式的json。我会避免继承,因为在将数据库反序列化为正确的类型时会遇到麻烦(数据库不适合多态集合)。
一旦进入你的类,你就可以将你的json反序列化为你的特定对象,如果不适合就抛出。
https://stackoverflow.com/questions/57205813
复制相似问题