4

我之前在 SO 上问过这个问题。这与它有关。我们有类似这样的代码库:

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

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

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

List<IRecipe> WhatAllCanBeCooked(IEnumerable<Ingredients> stuff, Cook cook);
4

4 回答 4

3

我会将这个逻辑委托给各个 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));
于 2010-01-28T21:26:00.013 回答
1

一些想法:

  • 使用决策表

  • 使用策略模式。这有助于您封装一组属于不同具体类的动作或参数。一旦您决定使用哪种策略,您就不再需要任何“ifs”来在策略之间进行调度。

编辑:一些额外的想法:

  • 从“小”开始:大多数情况下,只需简单地重构为更小、命名良好、可重用的函数即可帮助您减少 if-else-if-else-soup。有时,一个简单的、命名良好的布尔变量就可以解决问题。两者都是重构的示例,您可以在Fowler 的《重构》一书中找到。

  • 考虑“大”:如果您确实有很多复杂的业务规则,那么构建“特定于领域的语言”是一种选择,有时可能是降低复杂性的正确方法。只需通过谷歌搜索,您就会找到很多关于这个主题的材料。引用 David Wheeler计算机科学中的所有问题都可以通过另一个层次的间接性来解决

于 2010-01-28T21:17:32.333 回答
1

原始帖子——Martin Fowler 已经为您解决了这个问题……它被称为规范模式。
http://en.wikipedia.org/wiki/Specification_pattern

更新后的帖子——

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

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

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

Eric Evan 关于领域驱动设计的书有一个很好的例子来说明这种模式(运输清单)

这是维基链接:

http://en.wikipedia.org/wiki/Specification_pattern

在 wiki 链接的底部是这个 PDF 链接:

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

于 2010-01-28T21:18:55.103 回答
0

我认为该代码块本质上试图完成的是将食谱链接到该食谱中的成分。一种方法是在配方类本身中包含一个成分列表,然后将其与传入的成分列表进行比较,如下所示:

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 查询会查找所有在材料中传递的成分都存在于成分列表中的任何食谱(或者,如前所述,成分中没有不在材料中的成分)。这也可能会减少对 Recipes 的子类的需求,这似乎有点奇怪(尽管据我所知,您需要这个还有其他原因)

于 2010-01-28T21:11:21.200 回答