背景
在企业应用中单据编号的自定义是一个很常见的需求,能不能抽象一个通用的框架呢?之前写个一篇自定义密码强度的博文,感觉他们两个思路应该很相似。就让我们试试吧。
思路
这里的难点在于实现"解释器",比如将"前缀_<日期:yyyy_MM_dd>"解释为“工号生成器”,而且“解释器”的“规则”允许动态增加。
实现
代码下载
类图
核心代码
CodeRuleGenerator.cs
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 7 using System.Text.RegularExpressions; 8 9 namespace EntityCodeRuleDemo 10 { 11 public sealed class CodeRuleGenerator : ICodeRuleGenerator 12 { 13 private readonly IEnumerable<ICodeRuleProvider> _providers = new List<ICodeRuleProvider>(); 14 15 internal CodeRuleGenerator(IEnumerable<ICodeRuleProvider> providers) 16 { 17 _providers = providers; 18 } 19 20 public string Generate(object entity) 21 { 22 var sb = new StringBuilder(); 23 24 foreach (var provider in _providers) 25 { 26 sb.Append(provider.Generate(entity)); 27 } 28 29 return sb.ToString(); 30 } 31 } 32 }
CodeRuleInterpreter.cs
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 7 using System.Text.RegularExpressions; 8 9 using EntityCodeRuleDemo.RuleProviders; 10 11 namespace EntityCodeRuleDemo 12 { 13 public static class CodeRuleInterpreter 14 { 15 private static Dictionary<Regex, Func<string, ICodeRuleProvider>> _providerFactorys = new Dictionary<Regex, Func<string, ICodeRuleProvider>>(); 16 17 static CodeRuleInterpreter() 18 { 19 SetProviderFactory(new Regex("^[^<].*?[^>]?$"), LiteralRuleProvider.LiteralRuleProviderFactory); 20 SetProviderFactory(new Regex("^<日期(:(?<格式>.*?))?>$"), DateRuleProvider.DateRuleProviderFactory); 21 SetProviderFactory(new Regex("^<属性(:(?<名称>.*?))?>$"), PropertyRuleProvider.PropertyRuleProviderFactory); 22 } 23 24 public static void SetProviderFactory(Regex regex, Func<string, ICodeRuleProvider> providerFactory) 25 { 26 _providerFactorys[regex] = providerFactory; 27 } 28 29 30 public static ICodeRuleGenerator Interpret(string codeRule) 31 { 32 var providers = GetProviders(codeRule); 33 34 return new CodeRuleGenerator(providers); 35 } 36 37 private static IEnumerable<ICodeRuleProvider> GetProviders(string codeRule) 38 { 39 var literals = codeRule.Replace("<", "$<").Replace(">", ">$").Split('$'); 40 41 return literals 42 .Where(x => !string.IsNullOrEmpty(x)) 43 .Select(GetProvider) 44 .ToList(); 45 } 46 47 private static ICodeRuleProvider GetProvider(string literal) 48 { 49 var providerFactory = _providerFactorys 50 .FirstOrDefault(x => x.Key.IsMatch(literal)) 51 .Value; 52 53 if (providerFactory == null) 54 { 55 throw new FormatException("格式化错误"); 56 } 57 58 return providerFactory(literal); 59 } 60 } 61 }
Program.cs
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 7 namespace EntityCodeRuleDemo 8 { 9 class Program 10 { 11 static void Main(string[] args) 12 { 13 var employeeCode = CodeRuleInterpreter 14 .Interpret("前缀_<日期:yyyy_MM_dd>_<属性:NamePinYin>") 15 .Generate(new Employee { NamePinYin = "DUANGW" }); 16 17 Console.WriteLine(employeeCode); 18 } 19 } 20 21 class Employee 22 { 23 public string NamePinYin { get; set; } 24 public string EmployeeCode { get; set; } 25 } 26 }
运行效果
备注
按照这种思路,基本上能满足企业应用的多数编码规则要求。在真实的项目中,这些规则是要持久化到数据库的,这样就可以做到运行时动态的修改规则了。