我见过几个SOLID Open Close Principle的样本。这些解释通常很清楚。
但是我脑子里还有一个问题,那就是我们如何在不使用条件语句的情况下初始化这些不同的类?
以下是示例代码:
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
}
}
如果让我说我得到以下数据,我被要求阅读这些数据并为所有客户提供膳食。
Input(CustomerName, PreferredMeal):
Customer1,1
Customer2,1
Customer3,2
我们是否还要使用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);
}
}
如果是这种情况,那么GenerateCustomerMeals似乎不满足开放闭合原则。有没有更好的SOLID方法来做到这一点? :)
答案 0 :(得分:2)
如何在不使用条件语句的情况下初始化这些不同的类?
条件陈述不是邪恶的。当我们需要将一些条件(在您的示例中为PreferredMeal)映射到相应的实现(IMealGenerator接口)时,这是必要的,switch
语句也是如此。
您的代码中的问题是您正在使用它的方法中构建IMealGenerator
的实现。这是不正确的,因为在大多数情况下,你会有一些方法,如GenerateCustomerMeals
。这些方法不应该知道如何将PreferredMeal
映射到IMealGenerator
的实现。唯一的类知道映射是这样的MealGeneratorFactory
:
class MealGeneratorFactory : IMealGeneratorFactory
{
IMealGenerator GetMealGenerator(Customer customer)
{
// if/switch here
}
}
GenerateCustomerMeals
等所有方法都取决于IMealGeneratorFactory
,获得IMealGenerator
并使用它。
依赖注入会使事情变得更容易,但结论是一样的。
答案 1 :(得分:1)
如果您有多个实现,并且需要在它们之间切换,一个选项是提供允许您在它们之间切换的其他实现。这样,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);
}
}
更好的选择是使用支持Autofac的依赖注入框架,例如implementation selection based on keys。
这可以允许针对每个密钥单独注册服务,然后允许服务查找安排,例如:
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);
}
}