我们在讨论业务逻辑时或分析时,时常会提到“几种情况”,例如,当为A情况时,代码该如何执行;当为B情况时,代码又该如何执行。这是我们习惯的思维模式。但是所谓的“情况”实际上是各种逻辑关系的组合或者是归纳。由于归纳本身就有简化表达的作用,所以将这种归纳映射成代码,必然会起到简化代码的作用,使代码看起来比较简单。
以下面这段代码为例,以下代码片段是一个真实项目中的代码片段,很遗憾,这里面确实包含了如此复杂的业务逻辑,以至于里面的if...else分支看上去让人喘不过气来。下面是代码1
如果将if...else分支销去,又会是什么效果呢?下面是代码2
List<ReservationUnitType> overlappedUnitList = returnItem.OverlappedUnits == null ? new List<ReservationUnitType>() :
new List<ReservationUnitType>(returnItem.Units).FindAll(
n => new List<long>(returnItem.OverlappedUnits).FindAll(m => m == n.UnitID).Count > 0);
if (preferencedUnit.Count > 0 && !allreturnUnits.Exists(
unit => preferencedUnit.Contains(unit.UnitID)))
{
CommonFunction.ShowMessage(Strings.LookBookControl_UnitNotAvailable);
return false;
}
if(preferencedUnit.Count > 0 && allreturnUnits.Exists(unit => preferencedUnit.Contains(unit.UnitID)))
{
foreach (long n in preferencedUnit)
{
if (overlappedUnitList.Exists(unit => unit.UnitID == n)
&& CommonLibrary.Right.RightManagement.CheckRight(Contract.Reservation.Right.AllowUnitOverbooking, mParameter.RevenueCenter.RevenueCenterId)
&& CommonFunction.ShowMessage(string.Format(Strings.LookBookControl_UnitOverLapped, allreturnUnits.Find(unit => unit.UnitID == n).UnitDescription), MessageBoxButtons.YesNo) == DialogResult.No)
return false;
if(overlappedUnitList.Exists(unit => unit.UnitID == n)
&& !CommonLibrary.Right.RightManagement.CheckRight(Contract.Reservation.Right.AllowUnitOverbooking, mParameter.RevenueCenter.RevenueCenterId))
{
CommonFunction.ShowMessage(string.Format(Strings.LookBookControl_UnitOverLapped_NotAvailable, allreturnUnits.Find(unit=>unit.UnitID == n).UnitDescription));
return false;
}
}
}
if (preferencedUnit.Count < 0 && allreturnUnits.Count - overlappedUnitList.Count >= numOfUnit)
continue;
if (preferencedUnit.Count < 0 && !CommonLibrary.Right.RightManagement.CheckRight(Contract.Reservation.Right.AllowUnitOverbooking, mParameter.RevenueCenter.RevenueCenterId))
{
CommonFunction.ShowMessage(Strings.OrgTapeChart_UnitAvailability);
return false;
}
return !(preferencedUnit.Count < 0 && CommonFunction.ShowMessage(Strings.OrgTapeChartPanel_UnitOverlap, MessageBoxButtons.YesNo) == DialogResult.No);
}
代码1中存在较深的if...else分支,代码2中则只是一般性的判断。我不得不承认,处于演示目的,代码2被转换得有点畸形,但是代码1和代码2是等价的。代码2给出了一个典型的在什么“情况”下做什么事情的例子。讨论到这里必须进入这段代码,从具体的业务逻辑来分析这个问题。
到了周末,有花了一些时间来分析这段代码,分析的结果让我汗颜。
(手稿1)
(手稿2)
(手稿3)
原始的if...else分支确实是存在问题的。现在我将分析后的代码重新粘贴在下面,还是同样的业务逻辑,代码却简单了很多。以至于我自己都有些犹豫这个答案是否正确。
}
}
在这个分析过程中,数据的包含关系显现了出来。
在这段代码中,有4个数据
numOfUnit:需要的unit数量
allreturnUnits:返回的所有的Unit
preferencedUnit:请求的Unit
overlappedUnit:被其它订单占用的Unit
它们之间的关系如图:
有了上面的关系图,第一个条件判断就很明显了。如果preferencedUnit不在allReturnUnits中或者numOfUnit的数量大于allReturnUnits的数量,那么allReturnUnits肯定是无效的。反之则是有效的。
在allReturnUnits有效的情况下,如果preferencedUnit和overlappedUnitList不相交,说明allreturnUnits是有效的;相交,则需要进一步通过权限来判断。
这里,if...else分支的意义就突显出来了。它可以对应集合的划分,并将划分的方法和过程都直观地展现出来。每当进入一个分支,实际上就是进入到了一个数据集中,而不同的数据集对应于不同的业务逻辑,不同的业务逻辑对应于不同的响应代码,if...else的代码分支就这样从推理到实现,一气呵成。对,我向强调的就是if...else分支能够直接体现业务逻辑从推理到实现的过程。
回到根据各种“情况”来决定如何实现,这只是将实现部分的推理归纳成“情况”,而推理的过程则不再明显或被隐藏起来。但他们从代码上来说是等价的。
以下是转换后的代码
if(overlappedUnitList.Exists(unit=>preferencedUnit.Contains(unit.UnitID))
&& CommonLibrary.Right.RightManagement.CheckRight(Contract.Reservation.Right.AllowUnitOverbooking, mParameter.RevenueCenter.RevenueCenterId))
{
long n = overlappedUnitList.Find(unit=>preferencedUnit.Contains(unit.UnitID)).UnitID;
return CommonFunction.ShowMessage(string.Format(Strings.LookBookControl_UnitOverLapped, allreturnUnits.Find(unit => unit.UnitID == n).UnitDescription), MessageBoxButtons.YesNo) == DialogResult.Yes);
}
if(overlappedUnitList.Exists(unit=>preferencedUnit.Contains(unit.UnitID))
&& !CommonLibrary.Right.RightManagement.CheckRight(Contract.Reservation.Right.AllowUnitOverbooking, mParameter.RevenueCenter.RevenueCenterId))
{
CommonFunction.ShowMessage(string.Format(Strings.LookBookControl_UnitOverLapped_NotAvailable, allreturnUnits.Find(unit=>unit.UnitID == n).UnitDescription));
return false;
}
return true;
}
但是,当我们要维护这个业务逻辑时,问题就没有那么简单了。这两种实现方式会给我们的代码分析带来截然不同的后果。
由于第一种方式本身给我们直观地展现了数据集的划分,当新的业务逻辑--数据集的划分方式加入时,我们可以直接做出以下判断。
1. 新的数据集的划分方式是否可以在现有的子集上进行?
2. 如果新的数据集的划分方式存在冲突,那么该冲突是存于对什么数据集的划分上?
讨论了这么多,这两个判断才是我最终想说而之前一直感觉很朦胧东西,直到有一天我在一本科普读物上看到关于集合讨论。其实程序的设计和数据集的划分紧密相关,这在我今天下午处理一个bug时感觉非常明显。
这个讨论在这里就告一段落,但这对于我来说是分析数据集在程序设计中的应用的一个开始。
谢谢大家!