随着面向接口可扩展框架的继续开发,有些功能开发出现了"瓶颈",有太多的东西要写死才好做。但写死的代码扩展性是非常的不好,迷茫中寻找出入...

进而想到我以前开发的好几个项目,都已有一定的可配置能力,想想怎么把这些地方的代码抽象提取出来。进而想到"业务规则引擎",网上找了几个都不太入"眼",就抽时间再造个"轮子"

业务规则引擎在很多成熟的工作流引擎中都有相应的模块,是工作流的核心之一。但是除了工作流很多业务都需要业务规则引擎,所以它非常有必要独立作为一个模块。

在现实中很多项目开发都需要一些定制化的分支需求,为了这些需求把项目搞的鸡飞狗跳。使用业务规则引擎来做逻辑分支路由、参数“矫正”、拦截等,说是如虎添翼应该不为过。


这里使用社区发文章来做个例子

1、先看文章相关模型

A:ArticleRepository是文章数据仓储,实际是把文章存放在内存的List中

B:User是文章作者,Article就是文章了,ArticlePublish是DTO,包含文章和作者的信息

2、文章有不同的分类

        ArticleRepository TopArticles = new ArticleRepository { Name = "置顶" };ArticleRepository PerfectArticles = new ArticleRepository { Name = "精华" };ArticleRepository NetArticles = new ArticleRepository { Name = ".Net" };ArticleRepository OtherArticles = new ArticleRepository { Name = "其他" };

A:其中分为.net文章和其他文章,另外还有“置顶”和“精华”两个推送分类

B:文章现在分为.net文章和其他文章两个分类,以后很难说不用增加Java、PHP等其他分类,所以这个地方需要可扩展

C:不是每个人发的文章都置顶和精华,要不就没法愉快的玩耍了

3、可以按授权进行推送

先看授权相关建模

为了简单明了,这里都是用内存对象来模拟存储

A:Role是角色,角色有个权重的字段(Sort),Sort值越大权限越大

(有人说角色不应该是平等的吗?通过绑定资源来控制权限?我想问国家主席和你们村主任能平等吗?角色特权和资源绑定等手段应该综合来用。再者我们是演示业务规则引擎的,不是专门讨论权限系统的,先打住)

B:RolePermission用于授权(Grant)和获取权限(判断权限),维护者一个用户和权限的关联关系

    public class RolePermission : IEntityAccess<User, Role>, IComparer<Role>{private Dictionary<User, Role> _permission = new Dictionary<User, Role>();public bool Grant(User user, Role role){Role role0 = Get(user);if (role0 != null && role0.Sort >= role.Sort)return true;_permission[user] = role;return true;}public Role Get(User user){Role role = null;_permission.TryGetValue(user, out role);return role;}int IComparer<Role>.Compare(Role x, Role y){return Comparer<Role>.Default.Compare(x, y);}}

RolePermission

4、继续场景设置

        Role manager = new Role { Id = 1, Name = "管理员", Sort = 9999 };Role expert = new Role { Id = 2, Name = "专家", Sort = 999 };

        User user1 = new User { Id = 1, Name = "张三", Year = 3 };User user2 = new User { Id = 2, Name = "李四", Year = 10 };User user3 = new User { Id = 3, Name = "王二", Year = 0 };

5、现在可以开始发文章了

            RolePermission permission = new RolePermission();ConfigRole(permission);ArticlePublish post1 = new ArticlePublish(user1, new Article { Content = ".Net" });ArticlePublish post2 = new ArticlePublish(user2, new Article { Content = "Java" });ArticlePublish post3 = new ArticlePublish(user3, new Article { Content = "Php" });Engine<ArticlePublish, int> engine = new Engine<ArticlePublish, int>();ConfigCategory(engine);Post(engine, post1, post2, post3);Show(TopArticles, PerfectArticles, NetArticles, OtherArticles);

        private void ConfigRole(RolePermission permission){permission.Grant(user3, expert);}

授权代码

        private static void Post(Engine<ArticlePublish, int> engine, params ArticlePublish[] articles){foreach (var item in articles){int id = 0;if (engine.Run(item, ref id))Console.WriteLine(string.Concat("文章处理成功,Id=", id.ToString()));}}

发表文章代码

        private static void Show(params ArticleRepository[] repositorys){foreach (var repository in repositorys){Console.WriteLine(new string('-', 80));List<Article> list = repository.ListAll();if (list.Count < 1){Console.WriteLine(string.Concat(repository.Name, " 无"));continue;}Console.WriteLine(repository.Name);foreach (var item in list){Console.WriteLine(string.Concat("Article{Id=", item.Id, ",Content=", item.Content, "}"));}Console.WriteLine(new string('-', 80));}}

显示所有文章代码

文章处理成功,Id=1
文章处理成功,Id=2
文章处理成功,Id=3
--------------------------------------------------------------------------------置顶 无
--------------------------------------------------------------------------------精华 无
--------------------------------------------------------------------------------.Net
Article{Id=1,Content=.Net}
----------------------------------------------------------------------------------------------------------------------------------------------------------------其他
Article{Id=2,Content=Java}
Article{Id=3,Content=Php}
--------------------------------------------------------------------------------

三篇文章发表成功,一篇.net,两篇其他,效果非常不错

先等等,Engine<ArticlePublish, int>是什么鬼,要发文章不应该是ArticleRepository吗?

Engine就是大名鼎鼎的业务规则引擎了,ArticleRepository发表文章不假,但是都是由Engine决定发不发,用谁发,这些就是业务规则,

(ArticleRepository就是只负责存储和读取,职责非常单一)

6、把业务规则定义看一下

        private void ConfigCategory(Engine<ArticlePublish, int> engine){engine.When(post => post.Article.Content.Contains(".Net")).Then(post => NetArticles.Add(post.Article));engine.Then(post => OtherArticles.Add(post.Article));}

非常简单,如果文章包含.net关键字,使用NetArticles存储,否则使用OtherArticles存储(分表就是这么简单!!!)

7、继续推送的例子

            Engine<ArticlePublish, int> pushEngine = new Engine<ArticlePublish, int>();ConfigPush(permission, pushEngine);ConfigYear(pushEngine);Post(pushEngine, post1, post2, post3);Show(TopArticles, PerfectArticles, NetArticles, OtherArticles);

文章处理成功,Id=2
文章处理成功,Id=3
--------------------------------------------------------------------------------置顶
Article{Id=3,Content=Php}
----------------------------------------------------------------------------------------------------------------------------------------------------------------精华
Article{Id=2,Content=Java}
----------------------------------------------------------------------------------------------------------------------------------------------------------------.Net
Article{Id=1,Content=.Net}
----------------------------------------------------------------------------------------------------------------------------------------------------------------其他
Article{Id=2,Content=Java}
Article{Id=3,Content=Php}
--------------------------------------------------------------------------------

这次在置顶和精华都各有一篇了

8、我们看一下推送规则是怎么定义的

        private void ConfigPush(RolePermission permission, Engine<ArticlePublish, int> engine){int topNum = 0;int topLimit = 1;engine.When(post => topNum < topLimit && Comparer<Role>.Default.Compare(permission.Get(post.User), expert) >= 0).Then(post => { topNum++; return TopArticles.Add(post.Article); });engine.When(post => Comparer<Role>.Default.Compare(permission.Get(post.User), expert) >= 0).Then(post => PerfectArticles.Add(post.Article));}

        private void ConfigYear(Engine<ArticlePublish, int> engine){engine.When(post => post.User.Year >= 8).Then(post => PerfectArticles.Add(post.Article));}

解读一下

A:if 专家及以上权限且可以发置顶,推送到置顶(先来先得)

B:else if 专家发的文章推送到精华

C:else if 8年以上会员发的文章推送到精华

D:else 什么都不做

注:先不要和我掰扯以上业务规则的合理性,只是个测试例子而已

就是这么简单,老板再也不用担心我不会写业务规则了

9、业务规则引擎(Engine<TArg, TResult>)主要源码解析

    public class Engine<TArg, TResult> : ArgInstance<TArg, TResult>, IDefinition<TArg, TResult>{private List<IDefinition<TArg, TResult>> _definitions = new List<IDefinition<TArg, TResult>>();#region IDefinition<TArg, TResult>/// <summary>/// 条件/// </summary>/// <param name="condition"></param>/// <returns></returns>public IDefinition<TArg, TResult> When(IVerifyRule<TArg> condition){if (condition == null)return this;Definition<TArg, TResult> definition = new Definition<TArg, TResult> { Rule = condition };_definitions.Add(definition);return definition;}/// <summary>/// 执行/// </summary>/// <param name="action"></param>public void Then(IArgInstance<TArg, TResult> action){Instance = action;}#region IVerifyRule<TArg>/// <summary>/// /// </summary>/// <param name="entity"></param>/// <returns></returns>public bool Check(ref TArg entity){return true;}#endregion#endregion/// <summary>/// 执行/// </summary>/// <param name="arg"></param>/// <param name="result"></param>/// <returns></returns>public override bool Run(TArg arg, ref TResult result){foreach (var definition in _definitions){if (definition.Check(ref arg))return Operate.Run<TArg, TResult>(definition, arg, ref result);}return Operate.Run<TArg, TResult>(Instance, arg, ref result);}}

Engine

A:规则引擎逻辑很简单,就是When、Then和Run

B:另外包含一个IDefinition列表,IDefinition是规则定义;Engine本身也是一个IDefinition,其一是为了实现链式语法,使得规定定义非常优美,其二可以实现Engine的分支嵌套

C:Engine的Run就是规则适配,匹配上哪条规则(If/ElseIf)就执行哪条规则的Then,都匹配不上就执行自己的Then(也就是Else)

10、使用Engine的分支嵌套,优化一下推送的例子

        private void ConfigPush2(RolePermission permission, Engine<ArticlePublish, int> engine){int topNum = 0;int topLimit = 1;Engine<ArticlePublish, int> perfectEngine = new Engine<ArticlePublish, int>(post => Comparer<Role>.Default.Compare(permission.Get(post.User), expert) >= 0);perfectEngine.When(post => topNum < topLimit).Then(post => { topNum++; return TopArticles.Add(post.Article); });perfectEngine.Then(post => PerfectArticles.Add(post.Article));engine.Add(perfectEngine);}

解读一下:

A:先定义一个专家(分支)规则引擎(perfectEngine) 条件是if 专家

B:在专家规则引擎中增加置顶逻辑 if 没有置顶 设置置顶(先到先得)

C:else 推送到精华

D:把专家规则引擎(perfectEngine)添加到规则引擎(engine)的分支中

E:以上看上去逻辑更复杂了一点。但代码上没有重复出现专家判断逻辑,实际执行也没有,所以性能会更好,基本逻辑如下

if 专家

if 没有置顶 推送到置顶

else 推送到精华

以上就是演示复杂逻辑分支的例子

11、为了更好了解Engine,再看一下规则定义(IDefinition<TArg, TResult>)

IDefinition接口又继承了IVerifyRule和IArgInstance接口也一起看一下

    /// <summary>/// 逻辑分支定义/// </summary>public interface IDefinition<TArg, TResult> : IVerifyRule<TArg>, IArgInstance<TArg, TResult>{/// <summary>/// /// </summary>/// <param name="condition"></param>/// <returns></returns>IDefinition<TArg, TResult> When(IVerifyRule<TArg> condition);/// <summary>/// /// </summary>/// <param name="action"></param>void Then(IArgInstance<TArg, TResult> action);}

IDefinition

    /// <summary>/// 验证规则/// </summary>public interface IVerifyRule<TEntity>{/// <summary>/// /// </summary>/// <param name="entity"></param>/// <returns></returns>bool Check(ref TEntity entity);}

IVerifyRule

    /// <summary>/// 参数化工作单元/// </summary>/// <typeparam name="TArg">参数类型</typeparam>/// <typeparam name="TResult">结果类型</typeparam>public interface IArgInstance<in TArg, TResult>{/// <summary>/// 执行操作/// </summary>/// <param name="arg"></param>/// <param name="result"></param>/// <returns></returns>bool Run(TArg arg, ref TResult result);/// <summary>/// 成功回调/// </summary>/// <param name="arg"></param>void OnSuccess(TArg arg);/// <summary>/// 失败回调/// </summary>/// <param name="arg"></param>void OnFail(TArg arg);/// <summary>/// 异常回调/// </summary>/// <param name="arg"></param>/// <param name="ex"></param>void OnException(TArg arg, Exception ex);}

IArgInstance

A:IVerifyRule是判断逻辑接口,根据参数得到True/False,非常简单

B:IArgInstance是执行接口,按一个参数得到一个结果,并定义了三个“事件”,成功回调、失败回调、异常回调

C:IDefinition就简单多了,只是把判断逻辑IVerifyRule和执行对象使用When和Then组合起来

但是这里有一个问题,上面的例子是使用Linq实现的,看上去很高大上,Engine没看到对Linq的支持,其实以上Linq表达式就是生成委托

规则引擎都是面向IDefinition、IVerifyRule和IArgInstance,和委托也没什么关系啊,怎么回事

12、这个简单,使用静态方法扩展就可以得到的

    /// <summary>/// 规则定义(IDefinition)扩展(链式语法)/// </summary>public static class ExtensionDefinition{/// <summary>/// /// </summary>/// <typeparam name="TArg"></typeparam>/// <typeparam name="TResult"></typeparam>/// <param name="definition"></param>/// <param name="condition"></param>/// <returns></returns>public static IDefinition<TArg, TResult> When<TArg, TResult>(this IDefinition<TArg, TResult> definition, Func<TArg, bool> condition){if (condition == null)return definition;IVerifyRule<TArg> rule = new FuncRule<TArg>(condition);return definition.When(rule);}/// <summary>/// /// </summary>/// <typeparam name="TArg"></typeparam>/// <typeparam name="TResult"></typeparam>/// <param name="definition"></param>/// <param name="action"></param>public static void Then<TArg, TResult>(this IDefinition<TArg, TResult> definition, Func<TArg, TResult> action){if (action == null)return;IArgInstance<TArg, TResult> instance = new FuncInstance<TArg, TResult>(action);definition.Then(instance);}}

ExtensionDefinition

就是把委托转化为IVerifyRule和IArgInstance对象了,也是非常简单吧

我这个规则引擎简洁明了,很多爱学习的同学就是“爱造轮子“,现在把核心源码都公布了,大家也都可以定制自己的业务规则引擎了,感兴趣的同学马上动手吧

以上都是使用Fluent代码来做业务规则配置的,以后我还需要做使用文件配置做动态业务规则的例子,以便在容器配置文件中使用

转载于:https://www.cnblogs.com/xiangji/p/5485598.html

Asp.net 面向接口可扩展框架之业务规则引擎扩展组件相关推荐

  1. Asp.net 面向接口可扩展框架之类型转化基础服务

    新框架正在逐步完善,可喜可贺的是基础服务部分初具模样了,给大家分享一下 由于基础服务涉及面太广,也没开发完,这篇只介绍其中的类型转化部分,命名为类型转化基础服务,其实就是基础服务模块的类型转化子模块 ...

  2. Asp.net 面向接口可扩展框架之核心容器

    新框架的容器部分终于调通了!容器实在太重要了,所以有用了一个名词叫"核心容器". 容器为什么那么重要呢?这个有必要好好说道说道. 1.首先我们从框架名称面向接口编程说起,什么是面向 ...

  3. Asp.net 面向接口框架之应用程序上下文作用域组件

    在团队中推广面向接口开发两年左右,成果总体来说我还是挺满意的,使用面向接口开发的模块使用Unity容器配置的功能非常稳定,便于共享迁移(另一个项目使用只需要复制配置和调用接口即可)也很好扩展(操作的数 ...

  4. activiti 5.21工作流规则引擎扩展(businessRuleTask)

    2019独角兽企业重金招聘Python工程师标准>>> 背景介绍: 公司有自己的规则引擎配置平台,执行核心为drools,配置后生成规则脚本,存入数据库,执行的时候调用drools的 ...

  5. UCML一个面向WEB的应用框架开发平台

    前言 互联网的出现改变了人们的某些生活方式,而随着互连网的普及,越来越多的企业准备或已经要把过去传统的应用系统模式转移到互连网上来,这样的转移将极大地减少企业因维护庞大的分布式的应用系统而耗费的人力. ...

  6. Spring框架(IoC、AOP面向接口切面)

    新建一个Maven工程 Spring框架是由于软件开发的复杂性而创建的.Spring使用的是基本的JavaBean来完成以前只可能由EJB完成的事情.然而,Spring的用途不仅仅限于服务器端的开发. ...

  7. 枚举的语义化 - 面向接口的枚举扩展

    枚举的语义化 - 面向接口的枚举扩展 1.枚举的限制 因为所有的枚举类都默认继承于Enum类,因此,实现枚举的子类只能通过接口来实现. 2.不同点 与使用接口组织不同,该形式不在接口内部实现枚举接口. ...

  8. swift 组件化_打造完备的iOS组件化方案:如何面向接口进行模块解耦?

    作者 | 黑超熊猫zuik,一个修行中的 iOS 开发,喜欢搞点别人没搞过的东西,钻研过逆向工程.VIPER 架构和组件化. 关于组件化的探讨已经有不少了,在之前的文章 iOS VIPER架构实践(三 ...

  9. 面向接口编程的优点_为什么我们要面向接口编程

    到底面向?编程 面向过程编程( ProcedureOriented.简称 PO) 和 面向对象编程( ObjectOriented.简称 OO) 我们一定听过,然而实际企业级开发里受用更多的一种编程思 ...

  10. java继续_Java中消除实现继续和面向接口编程

    在匆忙之际理清消除实现继续和面向接口编程这样两个大题目可不是一件轻易的事情,尤其考虑到自身的熟悉水平.坦白的说,这又是一篇"炒冷饭"的文章,但这"冷饭"又确实不 ...

最新文章

  1. YourEclipse—不只是Eclipse开发者社区
  2. Mybatis(20)注解实现二级缓存
  3. Codeforces Round #529 (Div. 3) D. Circular Dance
  4. 《威胁建模:设计和交付更安全的软件》——3.11 小结
  5. docker privileged作用_docker容器性能监控cAdvisor+influxDB+grafana监控系统安装部署
  6. 网络安全——钓鱼邮件和网站克隆
  7. 13.STC15W408AS单片机SPI
  8. Allegro PCB导入网表后,PCB规则变化怎么办?
  9. 在VBA代码中引用Excel工作表中单元格区域的方式小结
  10. 如何坚持看书、跑步、写作?
  11. CPSR 和 SPSR
  12. 使用web设计器制作图表报表
  13. C语言笔记:进制转换与32位二进制IP地址转换十进制问题
  14. 泰坦尼克号预测python_kaggle:泰坦尼克号获救预测_Titanic_EDA##
  15. Python爬虫爬取智联招聘职位信息
  16. mit数据库 matlab,[zz]MIT-BIH开放数据库使用指南
  17. 【秃头系列】-【本科生毕设论文格式Word】自动生成页面布局
  18. ,CFormView::Create()里,_AfxCheckDialogTemplate输出Cannot find dialog template with IDD 0x00AB
  19. 免费PDF拆分合并工具
  20. FLAC3D学习笔记02-断裂构造实例

热门文章

  1. putty-gns3
  2. 类Shiro权限校验框架的设计和实现
  3. 如何把github上fork的项目修改过后再提交到github上
  4. LSOF 安装与使用
  5. java的关闭钩子(Shutdown Hook)
  6. Quartz CronTrigger最完整配置说明
  7. Http 请求处理流程[转]
  8. IOS 学习笔记(2) 视图UINavigationController
  9. 关于C#中动态加载AppDomain的问题
  10. Direct3D光与材质的颜色值