Pregunta

esta pregunta anteriormente en SO. Esto se relaciona con ella. Tenemos base de código similar al siguiente:

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

Me quieren deshacerse de este lío y tomar una estructura de datos y más OO ruta. Incluso el ejemplo de código que he proporcionado no es tan terrible como es. Miré el patrón de especificación y encontré aplicable. Alguna idea de cómo mejorar el código.

EDIT: Ahora que me doy cuenta de que, yo podría incluso como para poner en práctica un método de esta firma:

List<IRecipe> WhatAllCanBeCooked(IEnumerable<Ingredients> stuff, Cook cook);
¿Fue útil?

Solución

Me delegar esta lógica a las clases individuales 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;
    }

}

Editar en respuesta al comentario

Para encontrar todas las recetas que se pueden cocinar, simplemente hacer algo como esto:

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

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

Editar 2 Alternativamente, si almacena una lista de todas las recetas posibles en algún lugar, puede activar CanBeMadeBy en un método de instancia en lugar de una estática, y hacer esto:

List<IRecipe> allRecipes = // all possible recipes
...
return allRecipes.Where(r => r.CanBeMadeBy(stuff, cook));

Otros consejos

Algunas ideas:

  • tablas de decisión

  • estrategia patrón . Esto le ayuda a encapsular un conjunto de acciones o parámetros que pertenecen juntos en diferentes clases concretas. Una vez que haya decidido qué estrategia para su uso, no es necesario ningún 'si' más a la expedición entre las estrategias.

Edit: algunas ideas adicionales:

  • start "pequeña": lo más a menudo, simplemente refactorización simple de bien llamada, funciones reutilizables pequeños, ayudará a reducir el else-sopa si-else-if. A veces, un simple, así nombrado variable booleana hace el truco. Ambos son ejemplos de refactorizaciones que encontrará en Fowler "Refactoring" .

  • pensar "grande": si usted tiene realmente muchas reglas de negocio complejas, la construcción de un "lenguaje específico de dominio" es una opción que a veces puede ser la manera correcta de conseguir la baja complejidad. Se encuentra gran cantidad de material sobre este tema simplemente buscando en Google para ello. Citando a David Wheeler Todos los problemas de la informática se pueden resolver mediante otro nivel de indirección .

origiinal Post -   Martin Fowler ha resuelto este problema para usted ... su llamado el patrón de especificación.
   http://en.wikipedia.org/wiki/Specification_pattern

Mensaje actualizado -

considerar el uso del patrón Especificación Composite cuando:

  • Es necesario seleccionar un subconjunto de objetos en base a unos criterios,
  • Es necesario comprobar que sólo los objetos adecuados se utilizan para una determinada función, o
  • Es necesario describir lo que un objeto puede hacer, sin explicar los detalles de cómo el objeto lo hace

El verdadero poder del patrón está en la capacidad de combinar diferentes especificaciones en materiales compuestos con AND, OR y NOT relaciones. La combinación de las diferentes especificaciones juntos se puede hacer en tiempo de diseño o en tiempo de ejecución.

libro de Eric Evan en Domain Driven Design tiene un excelente ejemplo de este patrón (el manifiesto de embarque)

Este es el enlace Wiki:

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

En la parte inferior del enlace wiki es este enlace PDF:

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

Creo que lo que ese bloque de código es esencialmente tratando de lograr es la vinculación de las recetas a los ingredientes dentro de esa receta. Una posibilidad sería incluir una lista de ingredientes en la receta propia clase, y luego compararlo con la lista de ingredientes en el pasado, así:

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

Esto supone que tiene una colección de algún lugar todas sus recetas. Debe ser lo suficientemente fácil de configurar una lista estática de esos o de extracción desde una base de datos sin embargo.

La consulta LINQ en las miradas de pregunta para cualquier recetas en las que todos los ingredientes aprobadas en la materia están presentes en la lista de ingredientes (o, como se ha dicho, no hay ingredientes de ingredientes que no están en la materia). Esto también podría disminuir la necesidad de tener subclases para las recetas, que parece un poco extraño (aunque por lo que sé que hay razones adicionales te exigen esto)

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top