关键字: MapReduce模式, 关注点分离
MapReduce是一种编程模型,用于大规模数据集(大于1TB)的并行运算。概念"Map(映射)"和"Reduce(归约)",是它们的主要思想,都是从函数式编程语言里借来的,还有从矢量编程语言里借来的特性。它极大地方便了编程人员在不会分布式并行编程的情况下,将自己的程序运行在分布式系统上。 当前的软件实现是指定一个Map(映射)函数,用来把一组键值对映射成一组新的键值对,指定并发的Reduce(归约)函数,用来保证所有映射的键值对中的每一个共享相同的键组。
上面是一般认识的MapReduce, 主要应用于大数据领域. 但是MapReduce的思想,在我们的日常编程中也是有很大的应用, 特别是利用LINQ所带来的处理方式改变,可以写出容易理解的代码.
我们日常处理的代码很多时候都是对一个集合进行处理,转化(MAP),聚合(Reduce)来获得最后的结果.
当然我们可以利用if/foreach来达到同样的效果, 但是太多的if/foreach 混合的计算机基础逻辑和业务逻辑, 很难做到关注点分离. 使用MapReduce后,可以让顶层代码,更加面向需求方, 可以用自然语言和客户解释逻辑,确认逻辑.
需求:####
我们有一个规则记录节, 里面每条规则有一个源表(及字段ID)和一个目标表(及字段ID). 现在需要把所有规则按照源表生成一个分组结构返回前端处理.
代码初稿:####
var sections = _DbContext.StudyStandardMappingSection
.Where(p => p.MappingID == mappingID && p.DataStatus != DataStatus.删除);
var ret = Mapper.Map<IEnumerable<StudyStandardMappingSectionViewModel>>(sections);
foreach (var section in ret)
{
section.Tables = new List<StudyStandardMappingTableViewModel>();
//找出该分组下的规则
var rules = _DbContext.StudyStandardMappingRule
.Where(p => p.MappingSectionID == section.MappingSectionID && p.DataStatus != DataStatus.删除);
var ruleViewModels = Mapper.Map<IEnumerable<StudyStandardMappingRuleViewModel>>(rules);
if (rules != null)
{
//将规则按表分类
var tables = ruleViewModels.ToLookup(s => new { s.SourceTableID, s.SourceFieldName });
foreach (var table in tables)
{
StudyStandardMappingTableViewModel tmp = new StudyStandardMappingTableViewModel();
tmp.SourceTableID = table.Key.SourceTableID;
tmp.SourceTableName = table.Key.SourceFieldName;
tmp.Rules = new List<StudyStandardMappingRuleViewModel>();
foreach (var rule in table)
{
tmp.SourceTableName = rule.SourceTableName;//(此处设置表名不好,待改进)
//将多选变量分类
var options = table.ToLookup(s => s.ParentID);
foreach (var option in options)
{
//判断是否为多选变量规则,若是0代表不是多选变量规则
if (option.Key == 0)
{
tmp.Rules.Add(rule);
}
else
{//将选项值规则加入对应的多选变量规则中
var optionRule = ruleViewModels.Where(s => s.MappingRuleID == option.Key).FirstOrDefault();
optionRule.OptionValues = new List<StudyStandardMappingRuleViewModel>();
foreach (var item in option)
{
optionRule.OptionValues.Add(item);
}
}
}
}
section.Tables.Add(tmp);
}
}
}
应用MapReduce模式后代码:####
var sections = _DbContext.StudyStandardMappingSection
.Include("Rules")//取出子规则
.Where(p => p.MappingID == mappingID)//只要某个映射下的所有节
.ExcludeDeleteData()//排除删除的节
.ToList()//排除数据库的影响,转化为list,以便后续处理
.Select(p => RenderOneSectionViewModel(p, p.Rules));//构建一个分组节
private StudyStandardMappingSectionViewModel RenderOneSectionViewModel(StudyStandardMappingSection section, IEnumerable<StudyStandardMappingRule> rules)
{
var ret = Mapper.Map<StudyStandardMappingSectionViewModel>(section);
ret.Tables = rules.ExcludeDeleteData()//排除删除的规则
.GroupBy(p => new { p.SourceTableID, p.SourceTableName })//按照源表ID和表名分组
.Select(p => CreateOneMappingTableViewModel(p.Key.SourceTableID, p.Key.SourceTableName, p))//按照表名,表ID依次为每张表构建ViewModel
.ToList();
return ret;
}
private StudyStandardMappingTableViewModel CreateOneMappingTableViewModel(string sourceTableID, string sourceTableName, IEnumerable<StudyStandardMappingRule> rules)
{
return new StudyStandardMappingTableViewModel
{
SourceTableID = sourceTableID, //更新表ID
SourceTableName = sourceTableName, //更新表名
Rules = rules
.ExcludeDeleteData() //排除已删除的规则
.UpdateRuleChildOption() //更新规则的选项值列表
.Where(p => p.ParentID <= 0) //选择非选项的规则
.Select(p => Mapper.Map<StudyStandardMappingRuleViewModel>(p)) //转化为ViewModel
.ToList(),
};
}
public static IEnumerable<StudyStandardMappingRule> UpdateRuleChildOption(this IEnumerable<StudyStandardMappingRule> rules)
{
return rules.Select(p => p.UpdateOptions(rules.Where(p => p.ParentID == mappingRuleID)));
}
点评:
哪一种方法更好了? 直接看,初稿代码似乎更好,每个逻辑判断都直接在代码里面,似乎很清楚. 但是,每次看代码的时候, 都要不停的混合代码逻辑(if,else,foreach)和业务逻辑(如何构造表分组).每次看懂这个代码都要10几分钟.
第二种方法,基本上都是业务的语言来构建逻辑, 不用一分钟就知道代码在干什么,很容易发现业务逻辑是否有问题, 也容易跟随业务变化而变化. 但是底层算法都被保证在外部代码或者小函数里面, 调试起来还是挺麻烦的. 当然,这里还有一个非常大的好处,就是代码复用, 原来的代码很难复用,导致在几个地方重复,改造后,只需要调用相关逻辑算法就好.