C# 利用开闭原理(固体)

C# 利用开闭原理(固体),c#,solid-principles,open-closed-principle,C#,Solid Principles,Open Closed Principle,我看过两个固体开闭原理的例子。这些解释通常都很清楚 但我脑子里还有一个问题,那就是我们如何在不使用条件语句的情况下初始化这些不同的类 以下是示例代码: public enum PreferredMeal { Vegetarian = 1, NonVegetarian = 2 } public class Customer { public string Name { get; set; } public PreferredMeal PreferredMeal {

我看过两个固体开闭原理的例子。这些解释通常都很清楚

但我脑子里还有一个问题,那就是我们如何在不使用条件语句的情况下初始化这些不同的类

以下是示例代码:

public enum PreferredMeal
{
    Vegetarian = 1,
    NonVegetarian = 2
}

public class Customer
{
    public string Name { get; set; }
    public PreferredMeal PreferredMeal { get; set; }
}

public interface IMealGenerator
{
    List<Meal> GenerateMeals(Customer customer);
}

public class VegetarianMealGenerator : IMealGenerator
{
    public override List<Meal> GenerateMeals(Customer customer)
    {
        // Some codes here
    }
}

public class NonVegetarianMealGenerator : IMealGenerator
{
    public override List<Meal> GenerateMeals(Customer customer)
    {
        // Some codes here
    }
}
我们不也要使用if语句来确定实现MealGenerator的类是根据客户实例化的吗,如下所示

// Let's assume this function is called after all customers data has been read
// And those data is passed here
public void GenerateCustomerMeals(List<Customer> customers)
{
    foreach (var customer in customers)
    {
        if (customer.PreferredMeal == PreferredMeal.Vegetarian)
            new VegetarianMealGenerator().GenerateMeals(customer);
        else if (customer.PreferredMeal == PreferredMeal.NonVegetarian)
            new NonVegetarianMealGenerator().GenerateMeals(customer);
    }
}
//假设在读取所有客户数据后调用此函数
//这些数据在这里传递
公共void generatecustomermedins(列出客户)
{
foreach(客户中的var客户)
{
if(customer.preferredmeat==preferredmeat.素食者)
新的MealGenerator().生成项(客户);
else if(customer.preferredmeat==preferredmeat.non-egeratian)
新的非遗传计量生成器()。生成项(客户);
}
}

如果是这样,那么generatecustomermedins似乎不满足开闭原则。有没有更好的可靠方法可以做到这一点?:)

如果您有多个实现,并且需要在它们之间切换,一个选项是提供一个额外的实现,允许您在它们之间切换。通过这种方式,SOLID仍然被保留,因为路由机制对消费代码是隐藏的

public class RoutingMealGenerator : MealGenerator
{
   public override List<Meal> GenerateMeals(Customer customer)
   {
      if (customer.PreferredMeal == PreferredMeal.Vegetarian)
         return new VegetarianMealGenerator().GenerateMeals(customer);
      else if (customer.PreferredMeal == PreferredMeal.NonVegetarian)
         return new NonVegetarianMealGenerator().GenerateMeals(customer);
   }
}
公共类路由MealGenerator:MealGenerator
{
公共覆盖列表生成项(客户)
{
if(customer.preferredmeat==preferredmeat.素食者)
返回新的MealGenerator()。生成项(客户);
else if(customer.preferredmeat==preferredmeat.non-egeratian)
返回新的非通用MealGenerator()。生成项(客户);
}
}
一个更好的选择是使用依赖注入框架,例如,支持

这可以允许针对每个密钥分别注册服务,然后进行服务查找安排,例如:

public class PreferenceRoutingMealGenerator : MealGenerator
{
   IIndex<PreferredMeal, MealGenerator> _serviceLookup;

   public PreferenceRoutingMealGenerator( IIndex<PreferredMeal, MealGenerator> serviceLookup )
   {
      _serviceLookup = serviceLookup;
   }

   public override List<Meal> GenerateMeals(Customer customer)
   {
      MealGenerator gen = _serviceLookup[customer.PreferredMeal];

      return gen.GenerateMeals(customer);
   }
}
公共类首选路由MealGenerator:MealGenerator
{
IIndex\u服务查找;
公共首选路由MealGenerator(IIndex服务查找)
{
_serviceLookup=serviceLookup;
}
公共覆盖列表生成项(客户)
{
MealGenerator gen=\u serviceLookup[customer.PreferredMean];
返回发电商(客户);
}
}
如何在不使用条件语句的情况下初始化这些不同的类

条件语句不是邪恶的。当我们需要将一些条件(在您的示例中为preferredmein)映射到相应的实现(IMealGenerator接口)时,这是必要的,
switch
语句也是如此

代码中的问题是,您正在使用的方法中构建
IMealGenerator
的实现。这是不正确的,因为在大多数情况下,您将有一些方法,如
generatecustomermedins
。这些方法不应该知道如何将
preferredfine
映射到
IMealGenerator
的实现。唯一知道映射的类是
MealGeneratorFactory
,如下所示:

class MealGeneratorFactory : IMealGeneratorFactory 
{
    IMealGenerator GetMealGenerator(Customer customer)
    {
        // if/switch here
    }
}
所有方法,如
generatecustomermedins
都依赖于
IMealGeneratorFactory
,获取一个
IMealGenerator
,并使用它

依赖注入将使事情变得更容易,但结论是一样的

class MealGeneratorFactory : IMealGeneratorFactory 
{
    IMealGenerator GetMealGenerator(Customer customer)
    {
        // if/switch here
    }
}