C# 实现业务逻辑的代码改进

C# 实现业务逻辑的代码改进,c#,business-logic,business-rules,C#,Business Logic,Business Rules,我以前问过这个问题。这与此有关。我们的代码库与此类似: IRecipie FindRecipiesYouCanMake(IEnumerable<Ingredientes> stuff, Cook cook) { if(stuff.Any(s=>s.Eggs && s.Flour) && cook.DinerCook) { if(s=>s.Sugar) return new Pancake("Yum"); if(s=>

我以前问过这个问题。这与此有关。我们的代码库与此类似:

IRecipie FindRecipiesYouCanMake(IEnumerable<Ingredientes> stuff, Cook cook)
{
 if(stuff.Any(s=>s.Eggs && s.Flour) && cook.DinerCook)
 {
  if(s=>s.Sugar)
   return new Pancake("Yum");
  if(s=>s.Salt)
   return new Omlette("Yay");
 }
 /*.....
 ......
 .....
 loads of ifs and buts and else*/
}
IRecipie FindRecipies你可以制作(IEnumerable stuff,Cook Cook)
{
如果(填充任何东西(s=>s.鸡蛋和s.面粉)和烹调.DinerCook)
{
如果(s=>s.糖)
返回新煎饼(“百胜”);
如果(s=>s.盐)
返回新Omlette(“Yay”);
}
/*.....
......
.....
大量的“如果”、“但是”和“其他”*/
}
我想摆脱这种混乱,采取更多的数据结构和OO路线。即使是我提供的代码示例也没有它那么可怕。我查看了规范模式,发现它是适用的。任何关于如何改进代码的想法

编辑:现在我意识到了这一点,我甚至想实现这个签名的一个方法:

List<IRecipe> WhatAllCanBeCooked(IEnumerable<Ingredients> stuff, Cook cook);
列出可以烹饪的食物(i无数的食物,厨师);

我认为这段代码基本上是想实现将配方与配方中的成分联系起来。一种方法是在recipe类本身中包含一个成分列表,然后将其与传入的成分列表进行比较,如下所示:

public interface IRecipe {
   IEnumerable<Ingredient> Ingredients { get; }
}

public class Omlette : IRecipe {
   public IEnumerable<Ingredient> Ingredients { 
      get {
         return new Ingredient[]{new Ingredient("Salt"), new Ingredient("Egg")};
      }
   }
}

// etc. for your other classes.

IRecipie FindRecipiesYouCanMake(IEnumerable<Ingredientes> stuff, Cook cook)
{
    var query = Recipes.Where(r => !r.Ingredients.Except(stuff).Any());
    return query.First();
}
公共接口IRecipe{
IEnumerable配料{get;}
}
公共类Omlette:IRecipe{
公共IEnumerable配料{
得到{
返回新成分[]{新成分(“盐”)、新成分(“蛋”)};
}
}
}
//等你的其他课程。
我会找到你能做的东西(我是数不清的东西,厨师)
{
var query=Recipes.Where(r=>!r.components.Except(stuff).Any());
返回query.First();
}
这假设你在某个地方收集了你所有的食谱。不过,它应该足够简单,可以设置这些内容的静态列表或从数据库中提取

有问题的Linq查询查找所有传入原料的原料都出现在原料列表中的任何配方(或者,如前所述,原料中没有不在原料中的原料)。这也可能减少对菜谱子类的需求,这似乎有点奇怪(尽管据我所知,您需要它还有其他原因)

一些想法:

  • 使用

  • 使用。这有助于将属于不同具体类的一组操作或参数封装在一起。一旦你决定了使用哪种策略,你就不需要再在策略之间分配任何“如果”

编辑:其他一些想法:

  • 从“小”开始:通常,只需简单地重构到更小、命名良好、可重用的函数,就可以帮助您减少if-else-if-else汤。有时,一个简单的、命名良好的布尔变量就可以做到这一点。这两个都是您将在中找到的重构示例

  • 想想“大”:如果你真的有很多复杂的业务规则,那么构建一种“领域特定语言”是一种选择,它有时是降低复杂性的正确方法。通过谷歌搜索,你会发现很多关于这个话题的材料。引用大卫·惠勒(David Wheeler)的话,计算机科学中的所有问题都可以通过另一种间接方式来解决

原始邮政-- 马丁·福勒已经为你解决了这个问题。。。它被称为规范模式。

更新帖子--

考虑在以下情况下使用复合规范模式:

  • 您需要根据某些条件选择对象的子集
  • 您需要检查是否只有合适的对象用于特定角色,或者
  • 您需要描述一个对象可能会做什么,而不需要解释该对象如何做的细节
模式的真正威力在于能够将不同的规格组合成具有AND、OR和NOT关系的复合材料。可以在设计时或运行时将不同的规范组合在一起

Eric Evan关于域驱动设计的书中有一个很好的例子(运输清单)

这是Wiki链接:

wiki链接的底部是此PDF链接:


我会将此逻辑委托给各个IRecipie类:

if (Pancake.CanBeMadeBy(stuff, cook)) {
    return new Pancake("Yum");
}
....


public class Pancake: IRecipe {
    ...
    public static bool CanBeMadeBy(IEnumerable<Ingredientes> stuff, Cook cook) {
        return stuff.Any(s=>s.Eggs && s.Flour && s.Sugar) && cook.DinerCook;
    }

}

我正要把它标记为垃圾邮件:)@Mehrdad:Ugh!需要有一种方法来否决评论;)不要把面粉放在你的煎蛋卷里。。,你能给我一点关于你想要完成什么的更多信息吗?我知道omlettes没有面粉…但这就是我试图暴露的混淆逻辑的危险..我在示例中做得很糟糕..希望你能理解我的观点:p此模式将业务规则链的编码/更改从开发人员转移到用户,使此类系统具有非常高的灵活性。这可能会降低代码的复杂度,但另一方面,可能会在复合对象链中结束,其复杂度与原始代码相同。所以我怀疑这是一个真正的问题解决方案,它只是改变了维护业务规则的责任。Doc,如果他们选择参数化规范,我同意你的看法。然而,与任何设计模式一样,它的实现也可能有所不同。主要有三种口味的规格:1。硬编码规范(又名GOF策略模式)2。参数化规范,以及3。复合规范(我向用户推荐此路径)此链接支持我的陈述:您提供的第二个链接使用术语“规范”的方式不同于您在回答中提供的维基百科链接(该链接仅涉及“复合规范”)。如果你的意思是更广义的“规范”,你应该在你的答案中改变链接。然而,IMHO使用“复合规范”将问题从编码者转移到用户身上(因此,也许它为编码者解决了问题:-))。让OP来决定这是否是他认为有用的。我认为这在观点上是有区别的,我不是想找出是否可以做煎饼,而是想看看所有的反应都可以创造出来
List<IRecipe> results = new List<IRecipe>();

if (Pancake.CanBeMadeBy(stuff, cook)) {
    results.Add(new Pancake("Yum");
}
....
List<IRecipe> allRecipes = // all possible recipes
...
return allRecipes.Where(r => r.CanBeMadeBy(stuff, cook));