条件の確認中にコード内のネストされたifを取り除く最良の方法は何ですか?
-
05-07-2019 - |
質問
JavaでBlackBerryアプリを開発していますが、すべてのユーザー設定が保存されるオプションクラスがあります。問題は、反応する方法を知るためにいくつかの条件を確認する必要があることです。機能を追加し続けると、ユーザーに表示されるGUIオプションが増え、Optionsクラスに保存される設定が増え、チェックする必要のある条件が増えます。
たとえば、次のコードを使用します。
private void doCallMonitoring(int callId){
/*This is the part that I want to avoid. Having
multiple nested ifs. Here's just two conditions
but as I add more, it will get unmantainable
very quickly.*/
if(Options.isActive().booleanValue()){
callTime = new Timer();
TimerTask callTimeTask = new TimerTask(){
public void run(){
callTimeSeconds++;
if((callTimeSeconds == Options.getSoftLimit().intValue()) && (Phone.getActiveCall().getStatus() == PhoneCall.STATUS_CONNECTED)){
injectDTMFTone(Phone.getActiveCall());
}else if((callTimeSeconds >= Options.getHardLimit().intValue()) && (Phone.getActiveCall().getStatus() == PhoneCall.STATUS_CONNECTED)){
injectEndCall();
}
}
};
callTime.schedule(callTimeTask, 0,1000);
}else{
System.out.println("Service not active");
}
}
どのように動作させたいかは、1回の呼び出しですべてのオプションを確認し、そこからアクションの呪いを決定することです。このようなデザインを実現するにはどうすればよいですか
解決
別のオプションは、 injectDMTFTone()
などのメソッドがその条件を処理するかどうかを確認し、処理されたかどうかに応じてtrueまたはfalseを返すことです。
たとえば:
public void run() {
callTimeSeconds++;
do {
if (handleInjectDMTFTone())
break;
if (handleInjectEndCall())
break;
} while(false);
callTime.schedule(callTimeTask, 0,1000);
}
boolean handleInjectDMTFTone() {
if ((callTimeSeconds != Options.getSoftLimit().intValue()) ||
(Phone.getActiveCall().getStatus() != PhoneCall.STATUS_CONNECTED))
return false;
injectDTMFTone(Phone.getActiveCall());
return true;
}
boolean handleInjectEndCall() {
if ((callTimeSeconds < Options.getHardLimit().intValue()) ||
(Phone.getActiveCall().getStatus() != PhoneCall.STATUS_CONNECTED))
return false;
injectEndCall();
return true;
}
もちろん、別の injectDMTFTone()
メソッドまたは injectEndCall()
メソッドを呼び出す代わりに、それらのメソッドでそのロジックをインライン化します。そのようにして、同じ場所でそれらの条件を処理する方法とタイミングのすべてのロジックをグループ化しました。
これは私のお気に入りのパターンの1つです。 if
ステートメントは、条件を削除して返すのが理にかなっている限り、メソッドの最上部の近くで使用します。メソッドの残りの部分は多くのレベルでインデントされておらず、読みやすく簡単です。
すべてが同じインターフェースを実装し、 run
メソッドが繰り返し処理できるハンドラーのリポジトリにあるオブジェクトを作成することで、これをさらに拡張して、どのオブジェクトを処理するかを確認できます。それはあなたのケースに行き過ぎかもしれませんし、そうでないかもしれません。
他のヒント
「抽出方法」を使用できます;リファクタリングし、これらすべてのチェックを1つの「読み取り可能」なものにします。条件。
この関連回答ビットは長いですが、ポイントは次のような構造を置き換えることです:
}else if((callTimeSeconds >= Options.getHardLimit().intValue()) && (Phone.getActiveCall().getStatus() == PhoneCall.STATUS_CONNECTED)){
injectEndCall();
}
}
次のような場合:
....
}else if(shouldInjectEndCall() ){
injectEndCall();
}
}
...
オブジェクトには状態があり、他のオブジェクトを使用して作業を支援する場合があります。
他のオプションは、ある種の「条件をポリモーフィズムに置き換える」ことです。
より多くのコードを記述するだけのように思えますが、これらすべてのルールを&quot; validator&quot;に置き換えることができます。オブジェクトを検証し、すべての検証をいくつかの配列に入れてループします。
このスクラッチコードのようなもの。
private void doCallMonitoring(int callId){
// Iterate the valiators and take action if needed.
for( Validation validation : validationRules ) {
if( validation.succeed() ) {
validation.takeAction();
}
}
}
そして次のように実装します:
abstract class Validation {
public boolean suceed();
public void takeAction();
}
class InjectDTMFToneValidation extends Validation {
public boolean suceed() {
return (callTimeSeconds == Options.getSoftLimit().intValue())
&& (Phone.getActiveCall().getStatus() == PhoneCall.STATUS_CONNECTED)
}
public void takeAction() {
injectDTMFTone(Phone.getActiveCall());
}
}
class InjectEndCallValidation extends Validation {
public boolean suceed() {
return (callTimeSeconds >= Options.getHardLimit().intValue())
&& (Phone.getActiveCall().getStatus() == PhoneCall.STATUS_CONNECTED)
}
public void takeAction() {
injectEndCall();
}
}
そして最後にそれらをリストにインストールします:
private List<Validation> validationRules = new ArrayList<Validation>();{
validationrules.add( new InjectDTMFToneValidation() );
validationrules.add( new InjectEndCallValidation () );
...
...
}
ここでの考え方は、ロジックをサブクラスに移動することです。もちろん、より良い構造が得られ、おそらく suceed および takeAction を他のより意味のあるメソッドに置き換えることができます。目的は、検証をそこから引き出すことです。
より抽象的になりますか? .. はい。
ところで、インスタンスを使用するのではなく、静的メソッドを呼び出すオプションおよび電話クラスを使用するのはなぜですか?
これらの答えはすべて、おそらくより良いオブジェクト指向の答えです。一度だけ、迅速で汚い答えを求めます。
私は、そのような複雑なネストされたifを、それらを反転することによって単純化するのが本当に好きです。
if(!Options.isActive().booleanValue()) {
System.out.println("Service not active");
return;
}
the rest...
中間メソッドの戻り値が気に入らない人もいますが、私にとって常に素晴らしいパターンであるエントリ条件を検証するとき、それを使用したことを後悔したことはありません。
メソッドの見た目が本当に簡単になります。
画面よりも長いメソッドを記述する場合、これを実行したり、それを指摘する大きなコメントを記述したりしないでください。returnステートメントを失い、やったことを忘れるのは簡単すぎます。さらに良いことに、画面よりも長いメソッドを記述しないでください。