策略模式与开闭原则冲突
Strategy Pattern and Open-Closed Principle Conflict
我正在通读策略模式并尝试实施它,但我一直坚持决定我认为违反开闭原则的策略实施。
在策略模式中,我们编写接口并基于客户端交互,我们将传递策略实现。
现在,如果我们有一堆策略,那么我们需要决定使用条件,客户选择哪种策略,例如
IStrategy str;
if(stragety1) {
str = new Strategy1()
} else if (stragety2) {
str = new Strategy2()
} and so on..
str.run()
现在按照开闭原则,上面的扩展是开放的但它不是修改
如果我以后需要添加另一个策略(扩展),我确实需要更改此代码。
有没有办法避免这种情况,或者我们需要如何实施策略模式?
我认为,对 Closed for Modifications 存在误解。
1988 年,梅耶尔说:
当您的应用程序扩展新功能时,可能 不应更改正常工作的软件。
Rober C. Matrin 说:
这个定义显然已经过时了。
非常仔细地考虑一下。如果系统中所有模块的行为都可以扩展,而无需修改它们,那么您就可以向该系统添加新功能而无需修改任何旧代码.这些功能将 仅通过编写新代码 添加。
https://8thlight.com/blog/uncle-bob/2014/05/12/TheOpenClosedPrinciple.html
在不修改旧代码的情况下增加一些新代码不违反开闭原则。
这确实不是禁止修改的,但这是由于您的初始化方式所致。您正在使用一个值(枚举?)来确定应使用哪个 Strategy subclass。正如@bpjoshi 指出他们的,这更像是一种工厂模式。
维基百科讨论了策略模式如何 support the Open/Closed Principle,而不是阻碍它。
在该示例中,他们使用 Car
class 和 Brake
策略。有些汽车使用 ABS 制动,有些则没有。不同的Car
子class子和实例可以被赋予不同的刹车策略
要关闭您的代码以进行修改,您需要 select 不同的策略。您想 select 在定义新行为或 subclass 的地方使用 Strategy。您必须重构您的代码,以便在扩展代码的地方应用特定的 Strategy subclass。
我觉得你说的这个决定应该是工厂的责任class。以下是一些示例代码:
public interface ISalary
{
decimal Calculate();
}
public class ManagerSalary : ISalary
{
public decimal Calculate()
{
return 0;
}
}
public class AdminSalary : ISalary
{
public decimal Calculate()
{
return 0;
}
}
public class Employee
{
private ISalary salary;
public Employee(ISalary salary)
{
this.salary = salary;
}
public string Name { get; set; }
public decimal CalculateSalary()
{
return this.salary.Calculate();
}
}
Employee class 使用策略模式并遵循 Open/Closed 原则,即它对通过构造函数注入的新策略类型(ISalary 实现)开放,但对修改关闭。
缺少的部分是创建 Employee 对象的代码,例如:
public enum EmployeeType
{
Manager,
Admin
}
public class EmployeeFactory
{
public Employee CreateEmployee(EmployeeType type)
{
if (type == EmployeeType.Manager)
return new Employee(new ManagerSalary());
else if (type == EmployeeType.Admin)
return new Employee(new AdminSalary());
etc
}
}
这是一个非常简单的工厂模式。有更好的方法可以做到这一点,但这是解释这个概念的最简单方法。
1) 您必须将 selecting/creating 具体 策略与其用途分开。 IE。使用函数 selectStrategy
,将其作为(构造函数)参数传递等
2) 没有办法完全避免条件创建,但你可以隐藏它(例如使用一些字典来映射状态=>策略)and/or 将它转移到应用程序的另一个级别。最后一种方法非常强大和灵活,但取决于任务。在某些情况下,您可以将 selecting/creating 放在使用它的同一级别上。在其他情况下,您甚至可能最终将 selecting/creating 委派到 highest/lowest 级别。
2.1) 您可以使用 Registry
模式并在添加新策略时避免修改 "core" 对象。
我正在通读策略模式并尝试实施它,但我一直坚持决定我认为违反开闭原则的策略实施。
在策略模式中,我们编写接口并基于客户端交互,我们将传递策略实现。
现在,如果我们有一堆策略,那么我们需要决定使用条件,客户选择哪种策略,例如
IStrategy str;
if(stragety1) {
str = new Strategy1()
} else if (stragety2) {
str = new Strategy2()
} and so on..
str.run()
现在按照开闭原则,上面的扩展是开放的但它不是修改
如果我以后需要添加另一个策略(扩展),我确实需要更改此代码。
有没有办法避免这种情况,或者我们需要如何实施策略模式?
我认为,对 Closed for Modifications 存在误解。
1988 年,梅耶尔说:
当您的应用程序扩展新功能时,可能 不应更改正常工作的软件。
Rober C. Matrin 说:
这个定义显然已经过时了。 非常仔细地考虑一下。如果系统中所有模块的行为都可以扩展,而无需修改它们,那么您就可以向该系统添加新功能而无需修改任何旧代码.这些功能将 仅通过编写新代码 添加。 https://8thlight.com/blog/uncle-bob/2014/05/12/TheOpenClosedPrinciple.html
在不修改旧代码的情况下增加一些新代码不违反开闭原则。
这确实不是禁止修改的,但这是由于您的初始化方式所致。您正在使用一个值(枚举?)来确定应使用哪个 Strategy subclass。正如@bpjoshi 指出他们的
维基百科讨论了策略模式如何 support the Open/Closed Principle,而不是阻碍它。
在该示例中,他们使用 Car
class 和 Brake
策略。有些汽车使用 ABS 制动,有些则没有。不同的Car
子class子和实例可以被赋予不同的刹车策略
要关闭您的代码以进行修改,您需要 select 不同的策略。您想 select 在定义新行为或 subclass 的地方使用 Strategy。您必须重构您的代码,以便在扩展代码的地方应用特定的 Strategy subclass。
我觉得你说的这个决定应该是工厂的责任class。以下是一些示例代码:
public interface ISalary
{
decimal Calculate();
}
public class ManagerSalary : ISalary
{
public decimal Calculate()
{
return 0;
}
}
public class AdminSalary : ISalary
{
public decimal Calculate()
{
return 0;
}
}
public class Employee
{
private ISalary salary;
public Employee(ISalary salary)
{
this.salary = salary;
}
public string Name { get; set; }
public decimal CalculateSalary()
{
return this.salary.Calculate();
}
}
Employee class 使用策略模式并遵循 Open/Closed 原则,即它对通过构造函数注入的新策略类型(ISalary 实现)开放,但对修改关闭。
缺少的部分是创建 Employee 对象的代码,例如:
public enum EmployeeType
{
Manager,
Admin
}
public class EmployeeFactory
{
public Employee CreateEmployee(EmployeeType type)
{
if (type == EmployeeType.Manager)
return new Employee(new ManagerSalary());
else if (type == EmployeeType.Admin)
return new Employee(new AdminSalary());
etc
}
}
这是一个非常简单的工厂模式。有更好的方法可以做到这一点,但这是解释这个概念的最简单方法。
1) 您必须将 selecting/creating 具体 策略与其用途分开。 IE。使用函数 selectStrategy
,将其作为(构造函数)参数传递等
2) 没有办法完全避免条件创建,但你可以隐藏它(例如使用一些字典来映射状态=>策略)and/or 将它转移到应用程序的另一个级别。最后一种方法非常强大和灵活,但取决于任务。在某些情况下,您可以将 selecting/creating 放在使用它的同一级别上。在其他情况下,您甚至可能最终将 selecting/creating 委派到 highest/lowest 级别。
2.1) 您可以使用 Registry
模式并在添加新策略时避免修改 "core" 对象。