私の現在の仕事でかなり多く浮かび上がっているのは、一般化されたプロセスが発生する必要があるということですが、そのプロセスの奇妙な部分は特定の変数の値に応じてわずかに異なる方法で発生する必要があります。これを処理する最もエレガントな方法は何でしょうか。
私たちが通常使用している例を使用します。これは、処理する国に応じて少し異なる動作をします。
だから私はクラスを持っています、それを呼びましょう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);
}
}
ただし、特定の国ではこれらのアクションの一部のみを実行する必要があります。たとえば、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);
}
しかし、世界のすべての可能な国を扱っているとき、それは非常に面倒になります。それに関係なく、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);
}
}
ハンドラー:
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; }
}
同様に私は本当に好きかどうかはわかりません。ロジックは、すべてのファクトリー作成によってまだいくらか隠されており、元のメソッドを単純に見て、たとえば「GBR」プロセスが実行されたときに何が起こるかを確認することはできません。また、、などのスタイルGbrPunctuationHandler
で多くのクラスを(これよりも複雑な例で)作成UsaPunctuationHandler
することになります。つまり、句読点の間に発生する可能性のあるすべてのアクションを理解するには、いくつかの異なるクラスを調べる必要があります。取り扱い。明らかに、10億のif
ステートメントを持つ1つの巨大なクラスは必要ありませんが、わずかに異なるロジックを持つ20のクラスも同様に不格好に感じます。
基本的に私は自分が何らかのOOPノットに陥っていると思いますが、それを解くための良い方法がよくわかりません。このタイプのプロセスに役立つパターンがそこにあるかどうか疑問に思いましたか?
if (country == "DEU")
あなたの代わりにチェックしてくださいif (config.ShouldRemovePunctuation)
。
country
PreProcess
一部の国に基づいて異なる方法で実装できる機能DetermineSeparator
があり、すべての国で使用できる機能があるようですPostProcess
。それらすべてをprotected virtual void
デフォルトの実装にすることができ、Processors
国ごとに特定のものにすることができます