草庐IT

c# - 了解开闭原则

coder 2024-05-29 原文

当我遇到以下代码时,我正在重构一些简单脚本文件解析器的旧代码:

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-cases)?

问题是,我不知道如何在不破坏 OCP 的情况下轻松、正确地添加更多指令和处理器。 !-case 使用 scope and/or line 让它有点棘手,默认-case.

有什么建议吗?

最佳答案

使用 Dictionary<Char, YourDelegate>指定应如何处理字符。调用DefaultHandler如果字符键在字典中不存在。

添加 Add(char key, YourDelegate handler)允许任何人处理特定字符的方法。

更新

最好使用接口(interface):

/// <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反而。它提供了更大的灵 active ,因为源可以是从简单字符串到复杂流的任何内容。

更新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`
    }
}

这为处理实际协议(protocol)和添加更多命令提供了更大的灵 active 。您无需更改任何内容即可添加更多功能。因此,关于开放/封闭和其他一些 SOLID 原则,该解决方案是可行的。

关于c# - 了解开闭原则,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5416500/

有关c# - 了解开闭原则的更多相关文章

  1. c# - 如何在 ruby​​ 中调用 C# dll? - 2

    如何在ruby​​中调用C#dll? 最佳答案 我能想到几种可能性:为您的DLL编写(或找人编写)一个COM包装器,如果它还没有,则使用Ruby的WIN32OLE库来调用它;看看RubyCLR,其中一位作者是JohnLam,他继续在Microsoft从事IronRuby方面的工作。(估计不会再维护了,可能不支持.Net2.0以上的版本);正如其他地方已经提到的,看看使用IronRuby,如果这是您的技术选择。有一个主题是here.请注意,最后一篇文章实际上来自JohnLam(看起来像是2009年3月),他似乎很自在地断言RubyCL

  2. C# 到 Ruby sha1 base64 编码 - 2

    我正在尝试在Ruby中复制Convert.ToBase64String()行为。这是我的C#代码:varsha1=newSHA1CryptoServiceProvider();varpasswordBytes=Encoding.UTF8.GetBytes("password");varpasswordHash=sha1.ComputeHash(passwordBytes);returnConvert.ToBase64String(passwordHash);//returns"W6ph5Mm5Pz8GgiULbPgzG37mj9g="当我在Ruby中尝试同样的事情时,我得到了相同sha

  3. 基于C#实现简易绘图工具【100010177】 - 2

    C#实现简易绘图工具一.引言实验目的:通过制作窗体应用程序(C#画图软件),熟悉基本的窗体设计过程以及控件设计,事件处理等,熟悉使用C#的winform窗体进行绘图的基本步骤,对于面向对象编程有更加深刻的体会.Tutorial任务设计一个具有基本功能的画图软件**·包括简单的新建文件,保存,重新绘图等功能**·实现一些基本图形的绘制,包括铅笔和基本形状等,学习橡皮工具的创建**·设计一个合理舒适的UI界面**注明:你可能需要先了解一些关于winform窗体应用程序绘图的基本知识,以及关于GDI+类和结构的知识二.实验环境Windows系统下的visualstudio2017C#窗体应用程序三.

  4. ruby - 最佳原则中的原则 - 2

    我似乎经常遇到一些设计问题,但我不知道是什么是真的很合适。一方面我经常听到我应该限制耦合和坚持单一职责,但当我这样做时,我常常发现它很困难到在需要时将信息获取到程序的一部分。为了例如,classSingerdefinitialize(name)@name=nameendattr:nameend那么Song应该是:classSongdefnew(singer)@singer=singerendend或classSongdefnew(singer_name)@singer_name=singer_nameendend后者耦合性小,按道理应该用。但如果我以后发现宋有什么需要了解更多歌手,我的

  5. ruby - 了解在 Ruby 中与 lambda 一起使用的 inject 行为 - 2

    我经常将预配置的lambda插入可枚举的方法中,例如“map”、“select”等。但是“注入(inject)”的行为似乎有所不同。例如与mult4=lambda{|item|item*4}然后(5..10).map&mult4给我[20,24,28,32,36,40]但是,如果我制作一个2参数lambda用于像这样的注入(inject),multL=lambda{|product,n|product*n}我想说(5..10).inject(2)&multL因为“inject”有一个可选的单个初始值参数,但这给了我......irb(main):027:0>(5..10).inject

  6. ruby-on-rails - 如何测试自己对 Ruby/ROR 的了解? - 2

    是否有self验证的问题列表。看着那个,我可以确定我知道。我应该复习一下。在学习的过程中,我列了一个这样的list,但它只包含我在某处听说过的项目。我需要一段时间才能找到新的东西。 最佳答案 以下是针对ruby​​和Rails的一些测试列表。证书名称:RubyonRails谁提供:oDeskIncorporation认证费用:免费网站:https://www.odesk.com/tests/985?pos=0证书名称:RubyonRails提供者:Techgig.com(TimesBusinessSolutionsLimited(T

  7. ruby-on-rails - 了解 "attribute_will_change!"方法 - 2

    我想覆盖store_accessor的getter。可以查到here.代码在这里:#Fileactiverecord/lib/active_record/store.rb,line74defstore_accessor(store_attribute,*keys)keys=keys.flatten_store_accessors_module.module_evaldokeys.eachdo|key|define_method("#{key}=")do|value|write_store_attribute(store_attribute,key,value)enddefine_met

  8. ruby - 我怎样才能更好地了解/了解更多关于 Ruby 的知识? - 2

    按照目前的情况,这个问题不适合我们的问答形式。我们希望答案得到事实、引用或专业知识的支持,但这个问题可能会引发辩论、争论、投票或扩展讨论。如果您觉得这个问题可以改进并可能重新打开,visitthehelpcenter指导。关闭9年前。我最近开始学习Ruby,这是我的第一门编程语言。我对语法感到满意,并且我已经完成了许多只教授相同基础知识的教程。我已经写了一些小程序(包括我自己的数组排序方法,在有人告诉我谷歌“冒泡排序”之前我认为它非常聪明),但我觉得我需要尝试更大更难的东西来理解更多关于Ruby.关于如何执行此操作的任何想法?

  9. c# - C# 中的 Flatten Ruby 方法 - 2

    我如何做Ruby方法"Flatten"RubyMethod在C#中。此方法将锯齿状数组展平为一维数组。例如:s=[1,2,3]#=>[1,2,3]t=[4,5,6,[7,8]]#=>[4,5,6,[7,8]]a=[s,t,9,10]#=>[[1,2,3],[4,5,6,[7,8]],9,10]a.flatten#=>[1,2,3,4,5,6,7,8,9,10 最佳答案 递归解决方案:IEnumerableFlatten(IEnumerablearray){foreach(variteminarray){if(itemisIEnume

  10. ruby - 可以像在 C# 中使用#region 一样在 Ruby 中使用 begin/end 吗? - 2

    我最近从C#转向了Ruby,我发现自己无法制作可折叠的标记代码区域。我只是想到做这种事情应该没问题:classExamplebegin#agroupofmethodsdefmethod1..enddefmethod2..endenddefmethod3..endend...但是这样做真的可以吗?method1和method2最终与method3是同一种东西吗?还是有一些我还没有见过的用于执行此操作的Ruby惯用语? 最佳答案 正如其他人所说,这不会改变方法定义。但是,如果要标记方法组,为什么不使用Ruby语义来标记它们呢?您可以使用

随机推荐