代码之家  ›  专栏  ›  技术社区  ›  Perpetualcoder

实现业务逻辑的代码改进

  •  4
  • Perpetualcoder  · 技术社区  · 15 年前

    我问过 this 之前的问题。这与它有关。我们有类似的代码库:

    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*/
    }
    

    我想摆脱这种混乱,采取更多的数据结构和面向对象的路线。甚至我提供的代码示例也没有现在这么可怕。我查看了规范模式,发现它是适用的。任何改进代码的方法。

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

    List<IRecipe> WhatAllCanBeCooked(IEnumerable<Ingredients> stuff, Cook cook);
    
    4 回复  |  直到 11 年前
        1
  •  3
  •   Gabe Moothart    15 年前

    我将把这个逻辑委托给各个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;
        }
    
    }
    

    根据评论编辑

    要找到所有可以烹饪的菜谱,只需执行以下操作:

    List<IRecipe> results = new List<IRecipe>();
    
    if (Pancake.CanBeMadeBy(stuff, cook)) {
        results.Add(new Pancake("Yum");
    }
    ....
    

    编辑2 或者,如果你在某处存储了所有可能的食谱列表,你可以 CanBeMadeBy 执行以下操作:

    List<IRecipe> allRecipes = // all possible recipes
    ...
    return allRecipes.Where(r => r.CanBeMadeBy(stuff, cook));
    
        2
  •  1
  •   Doc Brown    15 年前

    一些想法:

    • 使用 decision tables

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

    编辑:一些额外的想法:

    • 开始“小”:大多数情况下,只需简单地重构更小的、命名良好的、可重用的函数,就可以帮助您减少if-else-if-else汤。有时,一个简单的、命名良好的布尔变量就能做到这一点。两者都是重构的例子,您可以在 Fowler's book "Refactoring" .

    • 想想“大”:如果你真的有很多复杂的业务规则,那么构建一种“特定于领域的语言”是一种选择,有时这是降低复杂性的正确方法。你只需在网上搜索就能找到很多关于这个话题的资料。引用大卫惠勒 计算机科学中的所有问题都可以通过另一个层次的间接解决。 .

        3
  •  1
  •   fremis    15 年前

    原桩-- 马丁·福勒帮你解决了这个问题…它被称为规范模式。
    http://en.wikipedia.org/wiki/Specification_pattern

    更新的帖子--

    在下列情况下,请考虑使用复合规范模式:

    • 您需要根据某些条件选择对象的子集,
    • 您需要检查是否只有合适的对象用于特定的角色,或者
    • 你需要描述一个对象可能做什么,而不需要解释该对象是如何做的

    模式的真正威力在于能够将不同的规格组合成具有或不具有关系的复合材料。将不同的规范组合在一起可以在设计时或运行时完成。

    eric evan关于领域驱动设计的书中有一个很好的例子(shipping manifest)

    这是wiki链接:

    http://en.wikipedia.org/wiki/specification\u模式

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

    http://martinfowler.com/apsupp/spec.pdf

        4
  •  0
  •   Ryan Brunner    15 年前

    我认为这段代码的主要目的是将菜谱与菜谱中的成分联系起来。一种方法是在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();
    }
    

    这假设你在某个地方收集了你所有的食谱。但是,它应该足够简单,可以设置这些元素的静态列表或从数据库中提取。

    问题中的linq查询查找所有传入的成分都在成分列表中的任何配方(或者,如前所述,成分中没有不在成分中的成分)。这也可能减少了对菜谱子类的需求,这看起来有点奇怪(尽管我知道有一些额外的原因你需要这个)

    推荐文章