用于实现业务逻辑的代码改进

发布于 2024-08-19 16:29:42 字数 745 浏览 2 评论 0原文

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

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);

I had asked this question previously on SO. This is related to it. We have code base similar to 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*/
}

I want to get rid of this mess and take a more data structure and OO route. Even the code sample i have provided is not as horrendous as it is. I looked at the specification pattern and found it applicable. Any ideas how to improve the code.

EDIT: Now that I realize it, I might even like to implement a method of this signature:

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

莫言歌 2024-08-26 16:29:42

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

I would delegate this logic to the individual IRecipie classes:

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;
    }

}

Edit in response to comment

To find all the recipes that can be cooked, just do something like this:

List<IRecipe> results = new List<IRecipe>();

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

Edit 2
Alternatively, if you store a list of all possible recipes somewhere, you can turn CanBeMadeBy into an instance method instead of a static, and do this:

List<IRecipe> allRecipes = // all possible recipes
...
return allRecipes.Where(r => r.CanBeMadeBy(stuff, cook));
海螺姑娘 2024-08-26 16:29:42

一些想法:

  • 使用决策表

  • 使用策略模式。这可以帮助您封装一组属于不同具体类的操作或参数。一旦您决定使用哪种策略,您就不再需要在策略之间分配任何“if”。

编辑:一些额外的想法:

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

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

Some ideas:

  • use decision tables

  • use the strategy pattern. This helps you to encapsulate a group of actions or parameters belonging together in different concrete classes. Once you have decided which strategy to use, you don't need any 'ifs' any more to dispatch between the strategies.

EDIT: some additional ideas:

  • start "small": most often, just simple refactoring to smaller, well-named, reusable functions will help you to reduce the if-else-if-else-soup. Sometimes, a simple, well named boolean variable does the trick. Both are examples for refactorings you will find in Fowler's book "Refactoring".

  • think "big": if you have really lot of complex business rules, constructing a "domain specific language" is an option that can sometimes be the right way of getting the complexity down. You will find lots of material on this topic just by googling for it. Citing David Wheeler All problems in computer science can be solved by another level of indirection.

如此安好 2024-08-26 16:29:42

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

更新的帖子 -

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

  • 您需要根据某些条件选择对象的子集,
  • 您需要检查是否仅将合适的对象用于特定角色,或者
  • 您需要描述对象可能执行的操作,但不解释详细信息模式

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

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

这是 Wiki 链接:

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

wiki 链接底部是此 PDF 链接:

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

ORIGIINAL POST --
Martin Fowler has solved this problem for you... its called the Specification pattern.
http://en.wikipedia.org/wiki/Specification_pattern

UPDATED POST --

Consider using the Composite Specification Pattern when:

  • You need to select a subset of objects based on some criteria,
  • You need to check that only suitable objects are used for a certain role, or
  • You need to describe what an object might do, without explaining the details of how the object does it

The true power of the pattern is in the ability to combine different specifications into composites with AND, OR and NOT relationships. Combining the different specifications together can be done at design time or runtime.

Eric Evan book on Domain Driven Design has an excellent example of this pattern (the Shipping Manifest)

This is the Wiki link:

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

At the bottom of the wiki link is this PDF link:

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

他不在意 2024-08-26 16:29:42

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

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 查询会查找任何配方,其中传入 stuff 的所有成分均出现在成分列表中(或者,如前所述,Ingredients 中不存在不在 stuff 中的成分)。这也可能会减少对食谱子类的需求,这似乎有点奇怪(尽管据我所知,还有其他原因你需要这个)

I think what that block of code is essentially trying to accomplish is linking recipes to ingredients within that recipe. One approach would be to include a list of ingredients on the recipe class itself, and then compare that against the list of ingredients passed in, like so:

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();
}

This assumes that you have a collection somewhere of all your recipes. It should be simple enough to set up a static list of those or pull from a database however.

The Linq query in question looks for any recipes where all ingredients passed in stuff are present in the list of ingredients (or, as it's stated, there are no ingredients in Ingredients that are not in stuff). This also might decrease the need to have subclasses for Recipes, which seems a bit odd (although for all I know there's additional reasons you'll require this)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文