私の C# コードには、無邪気に始まった if ステートメントがあります。
if((something == -1) && (somethingelse == -1) && (etc == -1)) {
// ...
}
成長しています。現在、20 の句が含まれている必要があると思います。
これをどのように処理すればよいですか?
私の C# コードには、無邪気に始まった if ステートメントがあります。
if((something == -1) && (somethingelse == -1) && (etc == -1)) {
// ...
}
成長しています。現在、20 の句が含まれている必要があると思います。
これをどのように処理すればよいですか?
それを関数に分解し、各条件をガード句にします。
int maybe_do_something(...) {
if(something != -1)
return 0;
if(somethingelse != -1)
return 0;
if(etc != -1)
return 0;
do_something();
return 1;
}
考慮すべきことの 1 つは、なぜそんなに多くの節があるのかということです。SWITCH ステートメントが選択肢をサブクラスに移動する必要があることを示すことが多いのと同様に、IF ステートメントの大規模で複雑なチェーンは、1 つの場所にあまりにも多くの概念 (したがって決定) を組み合わせていることを示している可能性があります。
例として、手数料の計算例を使用します。私が構築したあるシステムでは、手数料率は卸売業者に与えられた手数料の額に依存し、卸売業者は小売業者にいくらかを渡します。卸売業者から小売業者に支払われる金額は、小売業者と卸売業者の間の特定の合意 (契約に基づく) によって異なります。同様に、卸売業者が受け取る金額は、製品ライン、特定の製品、販売された製品の量によって異なります。
これを超えて、顧客の状態、特定の製品のカスタマイズなどに基づく追加の「例外」ルールがありました。これはすべて複雑な IF ステートメントのチェーンで解決できましたが、代わりにアプリケーションをデータ駆動型にしました。
このすべての情報をテーブルに入れることで、データ ルールを渡すことができました。最初に卸売業者の一般的なルールがトリガーされ、次に任意のオーバーライド ルールがトリガーされます。次に、基本的な卸売業者の手数料を手に入れて、卸売業者から小売業者への一般的なルールを実行し、次にそれらの例外を実行します.
これにより、ロジックの巨大な毛玉が単純な 4 ステップのプロセスに変わりました。各ステップは、適用する正しいルールを見つけるためにデータベース クエリを作成するだけです。
もちろん、これはあなたの状況には当てはまらないかもしれませんが、多くの場合、そのような大規模な複合体は、責任を分割する十分なクラスがないことを意味します (私たちの問題に対する別の解決策は、特定のルールとオーバーライドを含むファクトリ クラスである可能性があります)。または機能はデータ駆動型である必要があります。
関数にリファクタリングします。
bool Check()
{
return (something == -1) && (somethingelse == -1) && (etc == -1);
}
または、Check 関数でより読みやすいコード/ロジックを構築することもできます。
検討できるさらに別の方法は、複合式の使用です。これらの式 (.NET フレームワークでの LINQ の動作の基礎) を使用すると、これらすべての条件に基づいて式ツリーを作成できます。ビジネス ロジック コードは最上位の式を単純に操作して、真/偽の結果を取得できます。 .
式を評価するには、訪問者パターンを利用できます
これにより、条件のツリーを簡単に構成できます。これらのツリーをシリアル化して、決定を下した条件を永続化することもできます。多くの機会がここにあります。
あなたは文字列を見ていると言うので、誰かがすでにコメントしたこのようなものはどうですか。
var items = new List<string>();
items.Add("string1");
items.Add("string2");
if (items.Contains("string2"))
{
// do something
}
ある種の構成ファイルから値を取得して、リストに入力することもできます。
そして、このようなもの
もう少し説明します。(そして愚かなエラーを修正します:S)
//Interface to be able to which classes are able to give a boolean result used in the if stuff
public interface IResultable
{
bool Result();
}
//A list that is IResultable itself it gathers the results of the IResultables inside.
public class ComparatorList<T> : List<T>, IResultable where T : IResultable
{
public bool Result()
{
bool retval = true;
foreach (T t in this)
{
if (!t.Result())
{
retval = false;
}
}
return retval;
}
}
//Or two bools
public class OrComparator : IResultable
{
bool left;
bool right;
public OrComparator(bool left, bool right)
{
this.left = left;
this.right = right;
}
#region IResultable Members
public bool Result()
{
return (left || right);
}
#endregion
}
// And two bools
public class AndComparator : IResultable
{
bool left;
bool right;
public AndComparator(bool left, bool right)
{
this.left = left;
this.right = right;
}
#region IResultable Members
public bool Result()
{
return (left && right);
}
#endregion
}
// compare two ints
public class IntIsComparator : IResultable
{
int left;
int right;
public IntIsComparator(int left, int right)
{
this.left = left;
this.right = right;
}
#region IResultable Members
public bool Result()
{
return (left == right);
}
#endregion
}
ifステートメントがたくさんありますか?これはクールかもしれません:)
したがって、多くの比較を処理するためのこの構造があります。実装例を少し紹介します。
//list of ands
ComparatorList<AndComparator> ands = new ComparatorList<AndComparator>();
ands.Add(new AndComparator(true,true));
//list of ors
ComparatorList<OrComparator> ors = new ComparatorList<OrComparator>();
ors.Add(new OrComparator(false, true));
//list of intiss
ComparatorList<IntIsComparator> ints = new ComparatorList<IntIsComparator>();
ints.Add(new IntIsComparator(1, 1));
//list of all things :)
ComparatorList<IResultable> collected = new ComparatorList<IResultable>();
collected.Add(ands);
collected.Add(ors);
collected.Add(ints);
// if all things are as they must be :)
if (collected.Result())
{
//Evertything is as it schould be :)
}
私はこれを、私以外の誰も好きではない方法で行います。コードが「見栄えがする」かどうかは気にしません。その条件に 1 つ以上のテストがある場合、それはさらに多くのテストが必要になる可能性があることを意味します。または、それらのいくつかをコメントインまたはアウトしたい場合があり、そのような変更の作成を簡素化したいと考えています。
だから私はそれを次のようにコーディングします:
if (true
&& test_1
&& test_2
...
)
{
...
}
これにより、テストをコメントアウトしたり、新しいテストを 1 行の編集として追加したりすることが簡単になります。
しかし、私は最初に認めます、それはきれいであると主張していません.
これらすべての条件を本当にチェックする必要がある場合は、それらを取り除くことはできません。リファクタリングして読みやすくすることしかできませんが、ロジックは同じままです。
コードの再設計について誰も言及していないことに非常に驚いています。あなたは本当に本当に20の異なる状態が必要ですか? 私の経験では、多くの状態が他の状態に依存していることが多いため、チェックするのに論理的に冗長であることがよくあります。
コードをリファクタリングすると、コードと、状態が互いにどのように結合されているかをよりよく理解するのに役立つ場合があります。私があなただったら、まずここから始めます:)