利用开闭原则 (SOLID)
Posted
技术标签:
【中文标题】利用开闭原则 (SOLID)【英文标题】:Utilizing Open Closed Principle (SOLID) 【发布时间】:2018-04-28 09:16:44 【问题描述】:我已经看过几个关于 SOLID 开闭原则的示例。这些解释通常很清楚。
但我心中还有一个问题,那就是我们如何在不使用条件语句的情况下初始化那些不同的类?
这里是示例代码:
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 方法来做到这一点? :)
【问题讨论】:
【参考方案1】:我们如何在不使用条件语句的情况下初始化这些不同的类?
条件语句并不邪恶。当我们需要将一些条件(在您的示例中为 PreferredMeal)映射到相应的实现(IMealGenerator 接口)时,这是必要的,switch
语句也是如此。
您的代码中的问题是您正在使用 IMealGenerator
的方法构建实现。这是不正确的,因为在大多数情况下,您将拥有一些方法,例如GenerateCustomerMeals
。这些方法不应该知道如何将PreferredMeal
映射到IMealGenerator
的实现。唯一知道映射是 MealGeneratorFactory
的类是这样的:
class MealGeneratorFactory : IMealGeneratorFactory
IMealGenerator GetMealGenerator(Customer customer)
// if/switch here
而你所有的方法,比如GenerateCustomerMeals
都依赖于IMealGeneratorFactory
,得到一个IMealGenerator
并使用它。
依赖注入会让事情变得更简单,但结论是一样的。
【讨论】:
【参考方案2】:如果您有多个实现,并且需要在它们之间切换,则一种选择是提供一个额外的实现,让您可以在它们之间切换。通过这种方式,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);
更好的选择是使用支持implementation selection based on keys 的依赖注入框架,例如Autofac。
这可以允许针对每个键单独注册服务,然后进行服务查找安排,例如:
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);
【讨论】:
以上是关于利用开闭原则 (SOLID)的主要内容,如果未能解决你的问题,请参考以下文章
如何应用 SOLID 原则在 React 中整理代码之开闭原则