關(guān)鍵字: MapReduce模式, 關(guān)注點(diǎn)分離
MapReduce是一種編程模型,用于大規(guī)模數(shù)據(jù)集(大于1TB)的并行運(yùn)算。概念"Map(映射)"和"Reduce(歸約)",是它們的主要思想,都是從函數(shù)式編程語(yǔ)言里借來(lái)的,還有從矢量編程語(yǔ)言里借來(lái)的特性。它極大地方便了編程人員在不會(huì)分布式并行編程的情況下,將自己的程序運(yùn)行在分布式系統(tǒng)上。 當(dāng)前的軟件實(shí)現(xiàn)是指定一個(gè)Map(映射)函數(shù),用來(lái)把一組鍵值對(duì)映射成一組新的鍵值對(duì),指定并發(fā)的Reduce(歸約)函數(shù),用來(lái)保證所有映射的鍵值對(duì)中的每一個(gè)共享相同的鍵組。
上面是一般認(rèn)識(shí)的MapReduce, 主要應(yīng)用于大數(shù)據(jù)領(lǐng)域. 但是MapReduce的思想,在我們的日常編程中也是有很大的應(yīng)用, 特別是利用LINQ所帶來(lái)的處理方式改變,可以寫(xiě)出容易理解的代碼.
我們?nèi)粘L幚淼拇a很多時(shí)候都是對(duì)一個(gè)集合進(jìn)行處理,轉(zhuǎn)化(MAP),聚合(Reduce)來(lái)獲得最后的結(jié)果.
當(dāng)然我們可以利用if/foreach來(lái)達(dá)到同樣的效果, 但是太多的if/foreach 混合的計(jì)算機(jī)基礎(chǔ)邏輯和業(yè)務(wù)邏輯, 很難做到關(guān)注點(diǎn)分離. 使用MapReduce后,可以讓頂層代碼,更加面向需求方, 可以用自然語(yǔ)言和客戶解釋邏輯,確認(rèn)邏輯.
需求:####
我們有一個(gè)規(guī)則記錄節(jié), 里面每條規(guī)則有一個(gè)源表(及字段ID)和一個(gè)目標(biāo)表(及字段ID). 現(xiàn)在需要把所有規(guī)則按照源表生成一個(gè)分組結(jié)構(gòu)返回前端處理.
代碼初稿:####
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>();
//找出該分組下的規(guī)則
var rules = _DbContext.StudyStandardMappingRule
.Where(p => p.MappingSectionID == section.MappingSectionID && p.DataStatus != DataStatus.刪除);
var ruleViewModels = Mapper.Map<IEnumerable<StudyStandardMappingRuleViewModel>>(rules);
if (rules != null)
{
//將規(guī)則按表分類
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;//(此處設(shè)置表名不好,待改進(jìn))
//將多選變量分類
var options = table.ToLookup(s => s.ParentID);
foreach (var option in options)
{
//判斷是否為多選變量規(guī)則,若是0代表不是多選變量規(guī)則
if (option.Key == 0)
{
tmp.Rules.Add(rule);
}
else
{//將選項(xiàng)值規(guī)則加入對(duì)應(yīng)的多選變量規(guī)則中
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);
}
}
}
應(yīng)用MapReduce模式后代碼:####
var sections = _DbContext.StudyStandardMappingSection
.Include("Rules")//取出子規(guī)則
.Where(p => p.MappingID == mappingID)//只要某個(gè)映射下的所有節(jié)
.ExcludeDeleteData()//排除刪除的節(jié)
.ToList()//排除數(shù)據(jù)庫(kù)的影響,轉(zhuǎn)化為list,以便后續(xù)處理
.Select(p => RenderOneSectionViewModel(p, p.Rules));//構(gòu)建一個(gè)分組節(jié)
private StudyStandardMappingSectionViewModel RenderOneSectionViewModel(StudyStandardMappingSection section, IEnumerable<StudyStandardMappingRule> rules)
{
var ret = Mapper.Map<StudyStandardMappingSectionViewModel>(section);
ret.Tables = rules.ExcludeDeleteData()//排除刪除的規(guī)則
.GroupBy(p => new { p.SourceTableID, p.SourceTableName })//按照源表ID和表名分組
.Select(p => CreateOneMappingTableViewModel(p.Key.SourceTableID, p.Key.SourceTableName, p))//按照表名,表ID依次為每張表構(gòu)建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() //排除已刪除的規(guī)則
.UpdateRuleChildOption() //更新規(guī)則的選項(xiàng)值列表
.Where(p => p.ParentID <= 0) //選擇非選項(xiàng)的規(guī)則
.Select(p => Mapper.Map<StudyStandardMappingRuleViewModel>(p)) //轉(zhuǎn)化為ViewModel
.ToList(),
};
}
public static IEnumerable<StudyStandardMappingRule> UpdateRuleChildOption(this IEnumerable<StudyStandardMappingRule> rules)
{
return rules.Select(p => p.UpdateOptions(rules.Where(p => p.ParentID == mappingRuleID)));
}
點(diǎn)評(píng):
哪一種方法更好了? 直接看,初稿代碼似乎更好,每個(gè)邏輯判斷都直接在代碼里面,似乎很清楚. 但是,每次看代碼的時(shí)候, 都要不停的混合代碼邏輯(if,else,foreach)和業(yè)務(wù)邏輯(如何構(gòu)造表分組).每次看懂這個(gè)代碼都要10幾分鐘.
第二種方法,基本上都是業(yè)務(wù)的語(yǔ)言來(lái)構(gòu)建邏輯, 不用一分鐘就知道代碼在干什么,很容易發(fā)現(xiàn)業(yè)務(wù)邏輯是否有問(wèn)題, 也容易跟隨業(yè)務(wù)變化而變化. 但是底層算法都被保證在外部代碼或者小函數(shù)里面, 調(diào)試起來(lái)還是挺麻煩的. 當(dāng)然,這里還有一個(gè)非常大的好處,就是代碼復(fù)用, 原來(lái)的代碼很難復(fù)用,導(dǎo)致在幾個(gè)地方重復(fù),改造后,只需要調(diào)用相關(guān)邏輯算法就好.