web-dev-qa-db-ja.com

ブール入力に従って動作を決定するために使用されるif-elseブロックを削除するにはどうすればよいですか?

動作を決定するためにブールパラメータを使用するのは間違っていますか? によると、私はそれが悪いことを知っています:

public void myFunction(boolean b){
    if(b){
    }else{
    }
}

そしてそれは別の機能を持っているはずです:

public void myFunctionTrue(){
}

public void myFunctionFalse(){
}

ただし、この実装はif-elseを内側から外側にのみ移動します。

public static void main(String[] s){
    boolean b=(something from input, eg:from network, or txt config);
    if(b){
        myFunctionTrue();
    }else{
        myFunctionFalse();
    }
}

動作を決定するためのif-elseがまだあります。ルールを内部に収めるためにif-elseを含まないようにこのコードを実装するにはどうすればよいですか ブールパラメータを使用して動作を決定するのは間違っていますか? if-elseを提供しないブール値ですか?

たとえば、そのようなものがあります:

System.callFunctionByName("myFunction"+b);

(しかし、それはより複雑なコードになるようです!)

5
ocomfd

「この実装はif-elseを内側から外側に移動するだけ」と言っていますが、実際には2番目の例では、ブール値の意味を知っているコードにifチェックを移動し、実行するプロシージャを実際に気にするコード。これにより、コードがより明確になります。

また、プログラマが実行するプロシージャを静的に決定できる状況での再利用も促進します。 (S)彼が望む効果を得るためだけにブール値を人工的に作成する必要はありません。

12
Daniel T.

私は、その質問は、複数の層に引数を渡すことに関するものだと思います。たとえば次のようなもの

void a(int x, boolean b) {
    doSomethingWithX(x);
    doSomethingElse(b);
}

void doSomethingElse(boolean b) {
    doY();
    doAnotherThing(b);
}

void doAnotherThing(boolean b) {
    // finally do something with b
}

bwは2つのレイヤーを通過しましたが、コードとは何の関係もありません。そこでのみ使用されているのに、呼び出しコード(aを呼び出すコード)がそれを知っているのはなぜですか?その質問で受け入れられた回答の作成者は、それを回避する方法についていくつかのオプションを示します(たとえば、使用されている場所により近いbの値を照会します)。

コードでif-elseを避けることはできないので、試さないでください。コンテキスト内でコードを明確にするようにしてください。また、密航者を避けないでください。

8
Zymus

ルールを内部に収めるために、if-elseを含まないようにこのコードを実装するにはどうすればよいですか?

多くの場合、散在するブールフラグの解釈を継承に置き換えることができます。 ( https://en.wikipedia.org/wiki/Strategy_pattern を参照してください)。

持つ代わりに

class InvoiceCalculator {
    public void createInvoice(....) {
        ...
        double total = calculateOrderTotal(isB2cCustomer);
        ...
    }
    private double calculateOrderTotal(boolean isB2cCustomer) {
       ...
       if (isB2cCustomer) { .... } else { ... }
       ...
    }
}

このように異なるサブクラス(または戦略)を実装できます

class InvoiceCalculatorBase {
    public void createInvoice(....) {
        ...
        double total = calculateOrderTotal();
        ...
    }
    abstract protected double calculateOrderTotal();
}

class InvoiceCalculatorB2c extends InvoiceCalculatorBase {
    protected double calculateOrderTotal() {...}
}

class InvoiceCalculatorB2b extends InvoiceCalculatorBase {
    protected double calculateOrderTotal() {...}
}

この方法では、使用する戦略を一度だけ決定する必要があります

InvoiceCalculatorBase calulator = (isB2cCustomer) 
             ? new InvoiceCalculatorB2c(...) 
             : new InvoiceCalculatorB2b(...);
6
k3b

trueまたはfalseをパラメーターとして渡すと、コードが2つの賢明な名前の異なるメソッドを持つよりも読みにくくなります。メソッドを想像してください

_public void storeObject(boolean includeDependencies) {
    ...
}
_

myPerson.storeObject(false)のように呼び出されるので、何が起こっているのかを理解するためにブール値の意味を知る必要があります(私は何度も経験しています...)。しかし、2つのメソッド名storeBareObject()storeObjectWithDependencies()があると、すぐにわかりやすくなります。

1
Ralf Kleberhoff

このような機械的なビューでは、与えられたブール値がメソッドのパラメーターとして渡されるためだけにifステートメントが「悪い」かどうかを判断する可能性はありません。これは、問題について意味的な見方がある場合にのみ可能です。

値に複数の動作(メソッド)を制御するために使用されるセマンティックがあるとしましょう。動作は両方とも同じレイヤーにあるため、同じ「責任」を果たします。次に、戦略パターンを使用して、実行の時点ではデカップリングを実施しますが、セマンティック評価ではカップリングを実施します。

0
oopexpert

効率を使用して設計の決定を明確にする

私は実際に、人間が使用するインターフェースを設計するときに悪いと思われる設計上の決定を計算効率で解決しますが、それを使用して、2つ以上の設計決定の賛否両論がどのトレードオフの決定を困難にする曖昧なケースを解決します人的要因のみを考慮する場合に受け入れます。

これを基本的な例として考えてみましょう:

// Locks or unlocks a mutex.
void Mutex::lock(bool on);

実際には、これはそれほど悪いことではないと思います。 Mutexが提供するメソッドの数が減り、インターフェイスが理解しやすくなる可能性があります。上記の関数の実装は合理的に簡単で、クライアントコードは必要ない場合でも分岐するには、非常に対称的な使用パターンでかなり簡単に見えます:

mutex.lock(true);
// do stuff
mutex.lock(false);

次に、これを単純な代替と比較できます。

// Locks the mutex.
void Mutex::lock();

// Unlocks the mutex.
void Mutex::unlock();

その時点で、インターフェースは少し大きくなりますが、以前のものよりもさらに簡単な2つの関数を備えています。この時点で、クライアントコードは次のようになります。

mutex.lock();
// do stuff
mutex.unlock();

...私の控えめな意見では、これは全面的な下見的な改善ではありません。 Mutexの保守性の観点から、またはそれを使用するクライアントコードの観点から、これは必ずしもthatより簡単なことではありません。もちろん、1つの適度に複雑なものと引き換えに、2つの単純な関数を生成しますが、コードの保守と使用の知的オーバーヘッドは、関数の機能の複雑さと同じくらい、関数の数に比例します。

メンテナンスの観点から見ると、平均して5つのLOCを平均化する100の単純なメソッドを提供するインターフェイスは、平均して17のLOCを平均化する30のメソッドを使用する方法よりも常に簡単に維持できるとは限りません。前者は必ずしも使いやすく、理解しやすいとは限りません。ありとあらゆるもののシンプルさと存在しなければならないものの数との間にはバランスの取れた行為があります。 100の非常にシンプルなものを備えたシステムは、特に前のバージョンがすべてのものとの間の相互作用がより多い場合に、明確で理解可能な責任をまだ持っている30のわずかに肉質なものを備えたシステムよりも、保守と使用が必ずしも容易ではありません。

しかし、もし計算効率が私の決定を導くとしたら、それは私のために設計を解決します:

// Locks the mutex.
void Mutex::lock();

// Unlocks the mutex.
void Mutex::unlock();

...この時点で、2つの関数は分岐せずに実装できるため、多くの使用例では、クライアントはブールパラメータの受け入れに伴う分岐からまったく利益を得られない可能性があります。出来上がりです、私の最終的なデザインがあります。

ミニマリズム

私が最も好きではない設計は、両方のソリューションを提供しようとするものです。

// Locks the mutex.
void Mutex::lock();

// Unlocks the mutex.
void Mutex::unlock();

// Locks or unlocks a mutex.
void Mutex::lock(bool on);

...これは、クライアントが使用方法を学習し、開発者が維持するためのほとんどの機能を備えた最大のインターフェイスにつながるためです。ミニマリズムは窓から投げ出されます。

このタイプの考え方を採用する人々は、最終的にはそれほど多くのことを行わないにもかかわらず、その機能とドキュメントがマップからスクロールオフするインターフェースを設計することになります。

使い勝手

使いやすさは私にとって最優先事項です。インターフェイスがより多くの機能を提供する場合、インターフェイスを使用するのに理想的には可能な限り2〜3倍の自明なコードが必要な場合は、戻ってデザインを変更したり、インターフェイスに機能を追加したりしません。これは、私が最も優先する事項であり、ミニマリズムと安定性に関連するものに違反しています(ミニマリズムとの相関が高い、変更する理由がほとんどないかまったくないものを設計する)。

すべての可能なシナリオでインターフェイスをより便利に使用できるようにする責任は、自分の便利なヘルパーライブラリなどを考案するためにそれらを使用する人々に任せます。シンプルなインターフェイスに触れないでください。20個の関数を追加してそれらをより便利にし、利便性とは何の関係もない重要な機能がない場合を除いて、ユニットテストに必要なコードの量を増やしてください。

とにかく、それは私がそれについて考えようとしている方法です。一部の人にとっては便利かもしれません。あなたのために、私はあなたのデザインを使用するコードが一連のif/elseステートメントを書かなければならないかどうか心配しないと言います。自分でそれをもっと便利にする方法を考えさせましょう。あなたがあなたのインターフェースのユーザーでありデザイナーでもあるとしても、統合主義者になりましょう。

インターフェースを使用して使用法のコードを絞り込み、インターフェースの設計へのリークを最小限に抑えることから得られる小さな誘惑のすべてを許さないでください。設計面からは、Linuxカーネルを維持するLinus Torvaldsのようになります。カーネルを使用してコードを合理化したいすべての人からのランダムなプルリクエストだけを受け入れたくありません。理由をもっと見るnot設計を常に変更して拡張するよりも、設計を変更/拡張する方がよい。それがミニマリストに求められる考え方です。 wantsの無限のウィッシュリストではなく、ユーザーの本物のneedsを提供することに焦点を当てます。ニーズを完璧にカバーすることは十分に難しいためです。

ポリモーフィズム

もちろん、ポリモーフィックソリューションを使用することもできます。これには、関数ポインター、デリゲート、ファンクター、またはこのコンテキストで使用する言語が提供するもののテーブルを含めることができます。の代わりに:

boolean b = ...;
if (b)
    myFunctionTrue();
else
    myFunctionFalse();

あなたがするかもしれません:

boolean b = ...;          // some external input, like from a file
functions[b]();           // use the boolean as a key for a table 
                          // of functions

またはこの効果に何か:

boolean b = ...;          // some external input, like from a file
objects[b].some_method(); // use the boolean to determine what object
                          // with overriden functionality to use
                          // or potentially even create and use through a
                          // factory.

ただし、特に下位のオブジェクト指向ソリューションでは、継承とポリモーフィズムを低レベルの分岐メカニズムとして使用しないでください。モデル化する抽象化が実際に意味をなし、if/elseを回避するよりもはるかに広いスケールでメリットを提供する場合に使用します。それで気楽にやってください。

0
user204677