面向对象的编程-如何避免在因变量而略有不同的过程中重复

Joh*_*ill 59 c# oop

在我目前的工作中,经常发生的事情是有一个通用的过程需要发生,但是该过程的奇数部分需要根据某个变量的值而稍有不同,所以我不是非常确定处理此问题的最优雅方法是什么。

我将使用我们通常使用的示例,该示例在做事时会有所不同,具体取决于所处理的国家/地区。

所以我有一堂课,我们称之为Processor

public class Processor
{
    public string Process(string country, string text)
    {
        text.Capitalise();

        text.RemovePunctuation();

        text.Replace("é", "e");

        var split = text.Split(",");

        string.Join("|", split);
    }
}
Run Code Online (Sandbox Code Playgroud)

除了某些国家只需要采​​取其中一些行动。例如,只有6个国家需要资本化步骤。要分割的字符可能会因国家/地区而异。'e'仅根据国家/地区,才需要替换重音符号。

显然,您可以通过执行以下操作来解决此问题:

public string Process(string country, string text)
{
    if (country == "USA" || country == "GBR")
    {
        text.Capitalise();
    }

    if (country == "DEU")
    {
        text.RemovePunctuation();
    }

    if (country != "FRA")
    {
        text.Replace("é", "e");
    }

    var separator = DetermineSeparator(country);
    var split = text.Split(separator);

    string.Join("|", split);
}
Run Code Online (Sandbox Code Playgroud)

但是,当您与世界上所有可能的国家打交道时,这将变得非常麻烦。而且无论如何,这些if语句使逻辑更难以阅读(至少,如果您想象的是比示例更复杂的方法),则循环复杂性开始迅速攀升。

所以目前我正在做这样的事情:

public class Processor
{
    CountrySpecificHandlerFactory handlerFactory;

    public Processor(CountrySpecificHandlerFactory handlerFactory)
    {
        this.handlerFactory = handlerFactory;
    }

    public string Process(string country, string text)
    {
        var handlers = this.handlerFactory.CreateHandlers(country);
        handlers.Capitalier.Capitalise(text);

        handlers.PunctuationHandler.RemovePunctuation(text);

        handlers.SpecialCharacterHandler.ReplaceSpecialCharacters(text);

        var separator = handlers.SeparatorHandler.DetermineSeparator();
        var split = text.Split(separator);

        string.Join("|", split);
    }
}
Run Code Online (Sandbox Code Playgroud)

处理程序:

public class CountrySpecificHandlerFactory
{
    private static IDictionary<string, ICapitaliser> capitaliserDictionary
                                    = new Dictionary<string, ICapitaliser>
    {
        { "USA", new Capitaliser() },
        { "GBR", new Capitaliser() },
        { "FRA", new ThingThatDoesNotCapitaliseButImplementsICapitaliser() },
        { "DEU", new ThingThatDoesNotCapitaliseButImplementsICapitaliser() },
    };

    // Imagine the other dictionaries like this...

    public CreateHandlers(string country)
    {
        return new CountrySpecificHandlers
        {
            Capitaliser = capitaliserDictionary[country],
            PunctuationHanlder = punctuationDictionary[country],
            // etc...
        };
    }
}

public class CountrySpecificHandlers
{
    public ICapitaliser Capitaliser { get; private set; }
    public IPunctuationHanlder PunctuationHanlder { get; private set; }
    public ISpecialCharacterHandler SpecialCharacterHandler { get; private set; }
    public ISeparatorHandler SeparatorHandler { get; private set; }
}
Run Code Online (Sandbox Code Playgroud)

同样,我不确定自己是否喜欢。逻辑仍然被所有工厂创建所模糊,例如,您不能简单地查看原始方法并查看执行“ GBR”过程时会发生什么。您也结束了(在比这更复杂的例子),在风格造就了不少的班GbrPunctuationHandlerUsaPunctuationHandler等等......这意味着你必须在几个不同的类别看,以计算出所有标点期间可能发生的可能采取的行动处理。显然,我不希望一个拥有十亿个if语句的巨型类,但同样地,逻辑略有不同的20个类也感到笨拙。

基本上,我认为我已经陷入某种OOP纠结,并且还不太了解解决纠缠的好方法。我想知道是否有某种模式可以帮助此类流程?

Mic*_*zyn 50

I would suggest encapsulating all options in one class:

public class ProcessOptions
{
  public bool Capitalise { get; set; }
  public bool RemovePunctuation { get; set; }
  public bool Replace { get; set; }
  public char ReplaceChar { get; set; }
  public char ReplacementChar { get; set; }
  public char JoinChar { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

and pass it into the Process method:

public string Process(ProcessOptions options, string text)
{
  if(options.Capitalise)
    text.Capitalise();

  if(options.RemovePunctuation)
    text.RemovePunctuation();

  if(options.Replace)
    text.Replace(options.ReplaceChar, options.ReplacementChar);

  var split = text.Split(options.SplitChar);

  string.Join(options.JoinChar, split);
}
Run Code Online (Sandbox Code Playgroud)

  • 不知道为什么在跳到`CountrySpecificHandlerFactory` ... o_0之前没有尝试过这种操作 (4认同)
  • 该“公共类ProcessOptions”实际上应该只是“ [Flags]枚举类ProcessOptions:int {...}`...”。 (4认同)

Dam*_*ver 24

当.NET框架着手处理这类问题时,它并没有将所有模型都建模为string。例如,您拥有CultureInfo

提供有关特定区域性的信息(称为非托管代码开发的区域设置)。该信息包括区域性的名称,书写系统,使用的日历,字符串的排序顺序以及日期和数字的格式。

Now, this class may not contain the specific features that you need, but you can obviously create something analogous. And then you change your Process method:

public string Process(CountryInfo country, string text)
Run Code Online (Sandbox Code Playgroud)

Your CountryInfo class can then have a bool RequiresCapitalization property, etc, that helps your Process method direct its processing appropriately.


Cor*_*ane 12

也许Processor每个国家可以有一个?

public class FrProcessor : Processor {
    protected override string Separator => ".";

    protected override string ProcessSpecific(string text) {
        return text.Replace("é", "e");
    }
}

public class UsaProcessor : Processor {
    protected override string Separator => ",";

    protected override string ProcessSpecific(string text) {
        return text.Capitalise().RemovePunctuation();
    }
}
Run Code Online (Sandbox Code Playgroud)

一个基类来处理处理的常见部分:

public abstract class Processor {
    protected abstract string Separator { get; }

    protected virtual string ProcessSpecific(string text) { }

    private string ProcessCommon(string text) {
        var split = text.Split(Separator);
        return string.Join("|", split);
    }

    public string Process(string text) {
        var s = ProcessSpecific(text);
        return ProcessCommon(s);
    }
}
Run Code Online (Sandbox Code Playgroud)

另外,您应该重新处理您的返回类型,因为它在编写它们时不会编译-有时某个string方法不会返回任何内容。

  • 有时候,继承是完成这项工作的正确工具。如果您的流程在几种不同情况下的运行方式几乎都相同,但是又有几个部分在不同情况下的运行方式不同,这是一个好兆头,您应该考虑使用继承。 (2认同)

Arc*_*her 5

You can create a common interface with a Process method...

public interface IProcessor
{
    string Process(string text);
}
Run Code Online (Sandbox Code Playgroud)

Then you implement it for each country...

public class Processors
{
    public class GBR : IProcessor
    {
        public string Process(string text)
        {
            return $"{text} (processed with GBR rules)";
        }
    }

    public class FRA : IProcessor
    {
        public string Process(string text)
        {
            return $"{text} (processed with FRA rules)";
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

You can then create a common method for instantiating and executing each country related class...

// also place these in the Processors class above
public static IProcessor CreateProcessor(string country)
{
    var typeName = $"{typeof(Processors).FullName}+{country}";
    var processor = (IProcessor)Assembly.GetAssembly(typeof(Processors)).CreateInstance(typeName);
    return processor;
}

public static string Process(string country, string text)
{
    var processor = CreateProcessor(country);
    return processor?.Process(text);
}
Run Code Online (Sandbox Code Playgroud)

Then you just need to create and use the processors like so...

// create a processor object for multiple use, if needed...
var processorGbr = Processors.CreateProcessor("GBR");
Console.WriteLine(processorGbr.Process("This is some text."));

// create and use a processor for one-time use
Console.WriteLine(Processors.Process("FRA", "This is some more text."));
Run Code Online (Sandbox Code Playgroud)

Here's a working dotnet fiddle example...

您将所有特定于国家/地区的处理置于每个国家/地区类别中。为所有实际的单个方法创建一个通用类(在Processing类中),因此每个国家/地区处理器将成为其他通用调用的列表,而不是在每个国家/地区类中复制代码。

注意:您需要添加...

using System.Assembly;
Run Code Online (Sandbox Code Playgroud)

为了使静态方法能够创建国家/地区类的实例。