了解开闭原则
Posted
技术标签:
【中文标题】了解开闭原则【英文标题】:Understanding the Open Closed Principle 【发布时间】:2011-03-24 08:28:11 【问题描述】:当我遇到以下代码时,我正在重构一些简单脚本文件解析器的旧代码:
StringReader reader = new StringReader(scriptTextToProcess);
StringBuilder scope = new StringBuilder();
string line = reader.ReadLine();
while (line != null)
switch (line[0])
case '$':
// Process the entire "line" as a variable,
// i.e. add it to a collection of KeyValuePair.
AddToVariables(line);
break;
case '!':
// Depending of what comes after the '!' character,
// process the entire "scope" and/or the command in "line".
if (line == "!execute")
ExecuteScope(scope);
else if (line.StartsWith("!custom_command"))
RunCustomCommand(line, scope);
else if (line == "!single_line_directive")
ProcessDirective(line);
scope = new StringBuilder();
break;
default:
// No processing directive, i.e. add the "line"
// to the current scope.
scope.Append(line);
break;
line = reader.ReadLine();
在我看来,这个简单的脚本处理器很适合通过应用“开放封闭原则”进行重构。以$
开头的行可能永远不会有不同的处理方式。但是,如果需要添加以!
开头的新指令怎么办?还是需要新的处理标识符(例如新的 switch-case)?
问题是,我不知道如何在不破坏 OCP 的情况下轻松正确地添加更多指令和处理器。 !
-case 使用 scope
和/或 line
使其有点棘手,default
-case 也是如此。
有什么建议吗?
【问题讨论】:
这不是责任链模式的主要候选者吗? 【参考方案1】:使用Dictionary<Char, YourDelegate>
指定应如何处理字符。如果字典中不存在字符键,则调用DefaultHandler
。
添加一个Add(char key, YourDelegate handler)
方法,允许任何人处理特定字符。
更新
最好使用接口:
/// <summary>
/// Let anyone implement this interface.
/// </summary>
public interface IMyHandler
void Process(IProcessContext context, string line);
/// <summary>
/// Context information
/// </summary>
public interface IProcessContext
// Actual parser
public class Parser
private Dictionary<char, IMyHandler> _handlers = new Dictionary<char, IMyHandler>();
private IMyHandler _defaultHandler;
public void Add(char controlCharacter, IMyHandler handler)
_handlers.Add(controlCharacter, handler);
private void Parse(TextReader reader)
StringBuilder scope = new StringBuilder();
IProcessContext context = null; // create your context here.
string line = reader.ReadLine();
while (line != null)
IMyHandler handler = null;
if (!_handlers.TryGetValue(line[0], out handler))
handler = _defaultHandler;
handler.Process(context, line);
line = reader.ReadLine();
请注意,我改为传递TextReader
。它提供了更大的灵活性,因为源可以是从简单字符串到复杂流的任何内容。
更新 2
我也会以类似的方式分解!
处理。即创建一个处理 IMyHandler 的类:
public interface ICommandHandler
void Handle(ICommandContext context, string commandName, string[] arguments);
public class CommandService : IMyHandler
public void Add(string commandName, ICommandHandler handler)
public void Handle(IProcessContext context, string line)
// first word on the line is the command, all other words are arguments.
// split the string properly
// then find the corrext command handler and invoke it.
// take the result and add it to the `IProcessContext`
这为处理实际协议和添加更多命令提供了更大的灵活性。您无需更改任何内容即可添加更多功能。因此,对于 Open/Closed 和其他一些 SOLID 原则,该解决方案是可以的。
【讨论】:
好东西!我唯一要改变的是创建一个结构作为字典的包装器(这将返回默认或创建处理程序)。我也将 SB 范围添加到 ProcessContext,只要它也应该传递给处理程序。 我不会在上下文中公开 SB,而是添加一个用于附加内容的方法。为未来的变化提供更大的灵活性。 这里不是访客的例子吗 访问者模式是解决开放/封闭原则的一种方式。但是在这个例子中,所有的处理程序(访问者)都是预先注册的,因此命令服务是已知的。在访问者模式中,您可以随时接受(访问者契约的)任何实现。 @jgauffin 感谢这个例子。我会采用您的方法并走得更远,并在IMyHandler
中添加另一种方法boolean appliesTo(String line)
。现在用字典交换一个简单的列表。要找到正确的IMyHandler
,可以遍历列表并返回适用于该行的处理程序。这样一来,您也可以在同一程序结构中处理更新 2 的 !
情况,而无需创建更多接口,因为对单个字符没有限制。只关心处理程序的顺序;较长的比赛需要先出现。以上是关于了解开闭原则的主要内容,如果未能解决你的问题,请参考以下文章