DRY原理の解釈


10

現在、コーディングでこのDRY(Do n't Repeat Yourself)の概念に取り組んでいます。この関数を作成していますが、複雑になりすぎるのではないかと心配していますが、DRYの原則に従っています。

createTrajectoryFromPoint(A a,B b,C c,boolean doesSomething,boolean doesSomething2)

私が言ったこの関数は3つの入力パラメーターを取り、ブール関数の組み合わせdoesSomethingとを指定すると、関数は少し異なる動作をしdoesSomething2ます。しかし、私が抱えている問題は、このブール関数が追加されるたびに、この関数が非常に複雑になることです。

だから私の質問は、多くの同じロジックを共有するさまざまな関数(したがってDRYの原則に違反している)や、いくつかのパラメーターを指定した場合にわずかに異なる動作をするが、はるかに複雑にする1つの関数(しかし、 DRYを保存する)?


3
共有/共通ロジックを、さまざまなパブリックcreateTrajectory...関数がすべて呼び出すプライベート関数に分解できますか?
FrustratedWithFormsDesigner

可能性はありますが、これらのプライベート関数はこれらのブールパラメータを取得する必要があります
Albinoswordfish '17年

2
これは、ある種の具体的な例を考えると、はるかに簡単に答えられると思います。私の即座の反応は、あなたが描写している二分法は完全に現実的ではないということです。つまり、それらは2つの選択肢だけではありません。余談ですが、booleanパラメータとしてのaの使用は、せいぜい疑わしいものと考えます。
ジェリーコフィン


ええと、なぜ条件付きのものを独自の関数に分解しないのですか?
リグ

回答:


19

単一の関数/メソッドで異なるコードパスをトリガーするブール引数は、ひどいコードの匂いです。

あなたがやっていることは違反疎結合高い結束シングル責任ある原則、はるかに重要優先でDRYよりを。

つまり、物事は他の物事に依存する必要がある場合にのみ(カップリング)、1つのことだけを実行する必要があります(非常にうまくいく)(凝集)。

あなた自身の省略により、これはあまりにも強く結合されており(すべてのブールフラグは一種の状態依存性であり、これは最悪の1つです!)、個々の責任が多すぎます(過度に複雑です)。

とにかくあなたがしていることはDRYの精神ではありません。DRYは繰り返しに関するものです(のRREPEAT)。コピーと貼り付けを避けることが最も基本的な形式です。あなたがしていることは繰り返しとは関係ありません。

あなたの問題は、あなたのコードの分解が正しいレベルにないことです。重複するコードがあると思われる場合、それは適切にパラメーター化された独自の関数/メソッドであり、コピーや貼り付けではなく、他の名前はわかりやすい名前を付け、コアの関数/メソッドに委任する必要があります。


わかりました、私が書いている方法はあまり良くないようです(私の最初の疑い)私はこの 'DRYの精神'が何であるか本当に
わかり


4

関数に別のことをさせるためにブール値を渡すという事実は、単一責任原則の違反です。関数は1つのことを行う必要があります。1つのことだけを実行する必要があります。

これらのブール値に対応するコードパスを分離し、説明的な名前でいくつかの小さな関数に分割する必要があるようなにおいがします。

それができたら、結果の関数で共通のコードを探し、それを独自の関数に分解する必要があります。このことの複雑さに応じて、クラスを1つまたは2つに分解できる場合もあります。

もちろん、これはバージョン管理システムを使用していること、および何かを壊すことを恐れずにリファクタリングできるように適切なテストスイートがあることを前提としています。


3

何かをする前に、関数内のすべてのロジックを含む別の関数を作成して、次のような3つの関数を作成しないのはなぜですか。

createTrajectoryFromPoint(A a,B b,C c){...}

dosomething(A a, B b, C c){...}

dosomething2(A a, B b, C c){...}

そして、3つの同じパラメーター型を3つの異なる関数に渡すことにより、再び繰り返します。したがって、A、B、Cを含む構造体またはクラスを定義する必要があります。

あるいは、パラメーターA、B、Cと実行する操作のリストを含むクラスを作成できます。オブジェクトに操作を登録して、これらのパラメーター(A、B、C)で実行する操作(something、something2)を追加します。次に、オブジェクトに登録されているすべての操作を呼び出すメソッドを用意します。

public class MyComplexType
{
    public A a{get;set;}
    public B b{get;set;}
    public C c{get;set;}

    public delegate void Operation(A a, B b, C c);
    public List<Operation> Operations{get;set;}

    public MyComplexType(A a, B b, C c)
    {
        this.a = a;
        this.b = b;
        this.c = c   
        Operations = new List<Operation>();
    }

    public CallMyOperations()
    {
        foreach(var operation in Operations)
        {
            operation(a,b,c);
        }
    }
}

ブール値dosomethingとdosomething2の値の可能な組み合わせを説明するには、基本のcreateTrajectoryFromPoint関数に加えて、2ではなく4つの関数が必要になります。オプションの数が増えると、このアプローチはうまく拡張できなくなり、関数の名前付けさえも面倒になります。
JGWeissman

2

DRYは長すぎる可能性があります。DRYと組み合わせて単一責任原則(SRP)を使用するのが最善です。関数にブールフラグを追加して、同じコードのわずかに異なるバージョンを実行させると、1つの関数でやりすぎている兆候になる場合があります。この場合、フラグが表すケースごとに個別の関数を作成することをお勧めします。各関数を書き出すと、すべてのフラグを渡さずにプライベート関数に移動できる共通セクションがあるかどうかが明確になります。 、コードの明らかなセクションがない場合は、実際には自分自身を繰り返さず、いくつかの異なるが類似したケースがあります。


1

私は通常、この問題についていくつかのステップを実行し、さらに先に進む方法が理解できないときに停止します。

まず、あなたがしたことをしてください。DRYで頑張ってください。あなた大きな毛むくじゃらの混乱で終わらないならば、あなた終わりです。あなたの場合のように、重複するコードはないが、各ブール値の値が20か所でチェックされている場合は、次のステップに進みます。

次に、コードをブロックに分割します。ブール値はそれぞれ、実行を正しいブロックに向けるために1回だけ参照されます(まあ、場合によっては2回)。2つのブール値を使用すると、4つのブロックになります。各ブロックはほとんど同じです。DRYはなくなりました。 各ブロックを個別のメソッドにしないでください。これはより洗練された方法ですが、すべてのコードを1つのメソッドに配置すると、メンテナンスを行う人が4か所でそれぞれの変更を行う必要があることを簡単に、または可能にさえすることができます。よく整理されたコードと高いモニターを使用すると、違いと間違いがほぼ明白になります。あなたは今、保守コードを持っているし、それはより速く、元のもつれた混乱よりも実行されます。

3番目に、各ブロックから重複するコード行を取得して、それらを素敵でシンプルなメソッドにしてみます。時には何もできません。時には、あなたは多くを行うことができません。しかし、少しでも行うと、DRYに戻り、コードの追跡が少し簡単になり、保守が安全になります。理想的には、元のメソッドが重複コードなしで終了する可能性があります。その時点で、ブール値パラメータなしでいくつかのメソッドに分割したい場合とそうでない場合があります。呼び出しコードの利便性が主な関心事です。

2番目のステップのために、ここですでに多数の回答を追加しました。重複するコードは嫌いですが、それが問題を解決する唯一の理解できる方法である場合は、誰かがあなたが何をしているのか一目でわかるような方法でそれを行います。 複数のブロックと1つの方法のみを使用します。 名前、間隔、配置などをすべて可能な限り同じにする。その後、違いは読者に飛び出るはずです。それはそれをDRY方式で書き換える方法を明らかにするかもしれませんし、そうでなければ、それを維持することはかなり簡単です。


0

別のアプローチは、ブール型パラメーターをインターフェース・パラメーターに置き換え、インターフェース実装にリファクタリングされたさまざまなブール値を処理するコードを使用することです。だからあなたは

createTrajectoryFromPoint(A a,B b,C c,IX x,IY y)

ここで、ブール値の異なる値を表すIXおよびIYの実装があります。関数の本体で、どこにいても

if (doesSomething)
{
     ...
}
else
{
     ...
}

これを、IXで定義されたメソッドの呼び出しに置き換え、実装には省略されたコードブロックを含めます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.