• 基于C#实现的过滤器模式
  • 发布于 1周前
  • 39 热度
    0 评论
一:实际场景介绍
我们在给用户做订单催付通知的时候,会有这样的一种场景,用户在系统后台设置一组可以催付的规则,比如说订单金额大于xx元,非黑名单用户,来自哪个地区,已购买过某个商品,指定某个营销活动的人等等这样的条件,如果这时用户在淘宝上下了一个订单,那程序要判断的就是看一下此订单是否满足这些规则中的某一个,如果满足,我们给他发送催付通知,这种场景是很多做CRM的同学都会遇到的问题,那针对这种场景,如何更好的规划业务逻辑呢?

二:普通的编程代码
在这里我们就不考虑多筛选条件下的性能,而只从代码维护复杂度考虑,如果不清楚设计模式的同学,大概会写出如下的代码:
namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            var regulars = new List<Regulars>();
            // 堆代码 duidaima.com
            regulars.Add(new Regulars() { RegularID = 1, RegularName = "规则1", AnalysisConditons = "xxxx" });
            regulars.Add(new Regulars() { RegularID = 1, RegularName = "规则2", AnalysisConditons = "xxxx" });
            regulars.Add(new Regulars() { RegularID = 1, RegularName = "规则3", AnalysisConditons = "xxxx" });
            regulars.Add(new Regulars() { RegularID = 1, RegularName = "规则4", AnalysisConditons = "xxxx" });
            regulars.Add(new Regulars() { RegularID = 1, RegularName = "规则5", AnalysisConditons = "xxxx" });
 
            var filters = FilterRegularID(regulars);
            filters = FilterRegularName(filters);
            filters = FilterCondtions(filters);
 
            //... 后续逻辑
        }
 
        static List<Regulars> FilterRegularID(List<Regulars> persons)
        {
            //过滤 “姓名” 的逻辑
            return null;
        }
 
        static List<Regulars> FilterRegularName(List<Regulars> persons)
        {
            //过滤 “age” 的逻辑
            return null;
        }
 
        static List<Regulars> FilterCondtions(List<Regulars> persons)
        {
            //过滤 “email” 的逻辑
            return null;
        }
    }
 
    /// <summary>
    /// 各种催付规则
    /// </summary>
    public class Regulars
    {
        public int RegularID { get; set; }
        public string RegularName { get; set; }
        public string AnalysisConditons { get; set; }
    }
}
为了演示,上面的代码是从 regularid,regularname,condition 三个维度对 regulars 这个聚合对象进行AND模式的筛选过滤,当过滤维度比较多的时候,这种写法看的出来是简单粗暴,维护起来也必须简单粗暴, 所以上万行代码也就是这么出来的,设计模式告诉我们一个简单的“开闭原则”,那就是追求最小化的修改代码,这种场景有更好的优化策略吗?对应到设计模式上就是 “过滤器模式”, 专门针对这种场景的解决方案,一个维度一个类,然后通过逻辑运算类将他们进行组合,可以看出这是一种“结构式的设计模式”。

三:过滤器模式
好了,废话不多说,先来看一下优化后的设计图纸如下:

从上面这张图纸中可以看到,我已经将三个维度的过滤方法提取成了三个子类,由此抽象出了一个IFilter接口,当然你也可以定义成抽象类,然后实现了两个运算级 AND 和 OR 子类Filter,用于动态的对原子性的 RegularIDFilter,RegularNameFilter,ReuglarCondtionFilter 进行 AND,OR 逻辑运算,下面我们再看具体代码:
1. IFilter
    public interface IFilter
    {
        List<Regulars> Filter(List<Regulars> regulars);
    }
2. RegularIDFilter
    public class RegularIDFilter : IFilter
    {
        /// <summary>
        /// Regulars的过滤逻辑
        /// </summary>
        /// <param name="regulars"></param>
        /// <returns></returns>
        public List<Regulars> Filter(List<Regulars> regulars)
        {
            return null;
        }
    }
3.RegularNameFilter
    public class RegularNameFilter : IFilter
    {
        /// <summary>
        /// regularName的过滤方式
        /// </summary>
        /// <param name="regulars"></param>
        /// <returns></returns>
        public  List<Regulars> Filter(List<Regulars> regulars)
        {
            return null;
        }
    }
4. RegularCondtionFilter
    public class RegularCondtionFilter : IFilter
    {
        /// <summary>
        /// Condition的过滤条件
        /// </summary>
        /// <param name="regulars"></param>
        /// <returns></returns>
        public  List<Regulars> Filter(List<Regulars> regulars)
        {
            return null;
        }
    }
5. AndFilter
 
    /// <summary>
    /// filter的 And 模式
    /// </summary>
    public class AndFilter : IFilter
    {
        List<IFilter> filters = new List<IFilter>();
 
        public AndFilter(List<IFilter> filters)
        {
            this.filters = filters;
        }
 
        public List<Regulars> Filter(List<Regulars> regulars)
        {
            var regularlist = new List<Regulars>(regulars);
 
            foreach (var criteriaItem in filters)
            {
                regularlist = criteriaItem.Filter(regularlist);
            }
 
            return regularlist;
        }
    }
6. OrFilter
    public class OrFilter : IFilter
    {
        List<IFilter> filters = null;
 
        public OrFilter(List<IFilter> filters)
        {
            this.filters = filters;
        }
 
        public List<Regulars> Filter(List<Regulars> regulars)
        {
            //用hash去重
            var resultHash = new HashSet<Regulars>();
 
            foreach (var filter in filters)
            {
                var smallPersonList = filter.Filter(regulars);
 
                foreach (var small in smallPersonList)
                {
                    resultHash.Add(small);
                }
            }
            return resultHash.ToList();
        }
    }
7. 客户端调用
最后我们客户端调用程序就简单了,只要将“原子性”的过滤条件追加到“逻辑运算类”中就完美了,如下图:
    class Program
    {
        static void Main(string[] args)
        {
            var regulars = new List<Regulars>();
 
            regulars.Add(new Regulars() { RegularID = 1, RegularName = "规则1", AnalysisConditons = "xxxx" });
            regulars.Add(new Regulars() { RegularID = 1, RegularName = "规则2", AnalysisConditons = "xxxx" });
            regulars.Add(new Regulars() { RegularID = 1, RegularName = "规则3", AnalysisConditons = "xxxx" });
            regulars.Add(new Regulars() { RegularID = 1, RegularName = "规则4", AnalysisConditons = "xxxx" });
            regulars.Add(new Regulars() { RegularID = 1, RegularName = "规则5", AnalysisConditons = "xxxx" });
 
            //追加filter条件
            var filterList = new IFilter[3] {
                                              new RegularIDFilter(),
                                              new RegularNameFilter(),
                                              new RegularCondtionFilter()
                                            };
 
            var andCriteria = new AndFilter(filterList.ToList());
            //进行 And组合 过滤
            andCriteria.Filter(regulars);
 
        }
    }

当你仔细看完上面的代码,会不会发现,如果后续有需求变更,比如说增加筛选的维度,我只需要新增一个继承 IFilter 的子类就搞定了,客户端在调用的时候只要在Filters集合中追加该筛选维度,是不是就OK了,所以这种模式几乎达到了无代码修改的地步~~~好了,本篇就说到了这里,希望对你有帮助~
用户评论