文章目錄
背景
在公司專屬應用程式中單據編號的自訂是一個很常見的需求,能不能抽象一個通用的架構呢?之前寫個一篇自訂密碼強度的博文,感覺他們兩個思路應該很相似。就讓我們試試吧。
思路這裡的痛點在於實現"解譯器",比如將"首碼_<日期: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 EntityCodeRuleDemo10 {11 public sealed class CodeRuleGenerator : ICodeRuleGenerator12 {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 EntityCodeRuleDemo12 {13 public static class CodeRuleInterpreter14 {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 literals42 .Where(x => !string.IsNullOrEmpty(x))43 .Select(GetProvider)44 .ToList();45 }46 47 private static ICodeRuleProvider GetProvider(string literal)48 {49 var providerFactory = _providerFactorys50 .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 Program10 {11 static void Main(string[] args)12 {13 var employeeCode = CodeRuleInterpreter14 .Interpret("首碼_<日期:yyyy_MM_dd>_<屬性:NamePinYin>")15 .Generate(new Employee { NamePinYin = "DUANGW" });16 17 Console.WriteLine(employeeCode);18 }19 }20 21 class Employee22 {23 public string NamePinYin { get; set; }24 public string EmployeeCode { get; set; }25 }26 }運行效果
備忘
按照這種思路,基本上能滿足公司專屬應用程式的多數編碼規則要求。在真實的項目中,這些規則是要持久化到資料庫的,這樣就可以做到運行時動態修改規則了。