これは beautiful code の章に関連しています。そしてその章では、ネストされたif
sについて読みました。
作者は、深くネストされたif
sをバグの発生源として読みやすくしていました。そして、彼はネストされたif
sをcase
ステートメントと 判定テーブル で置き換えることについて話していました。
ネストされたif
sをcase
(select case
)および決定表
まあ、具体的にswitch/caseステートメントについて尋ねるので、あなたの質問への直接的な答えではありませんが、同様の質問があります。
これは、ネストされたifを、戻り値に落ち着く前に徐々にチェックするのではなく、早期に戻るガードステートメントで置き換えることについて説明しています。
私がいつもやろうとする1つの例は、次のような重いネストを置き換えることです(実際、これはtooではありませんが、私はそれらを最大8または9レベルの深さで見ました):
if (i == 1) {
// action 1
} else {
if (i == 2) {
// action 2
} else {
if (i == 3) {
// action 3
} else {
// action 4
}
}
}
これとともに:
switch (i) {
case 1:
// action 1
break;
case 2:
// action 2
break;
case 3:
// action 3
break;
default:
// action 4
break;
}
また、アクションを可能な限り小さくして(関数呼び出しが最適です)、switchステートメントを圧縮したままにします(そのため、4ページ先に移動して終了を確認する必要はありません)。
決定テーブルは、後で実行する必要があるアクションを示すフラグを設定するだけです。 「後で」セクションは、これらのフラグに基づいたアクションの単純なシーケンスです。私は間違っている可能性があります(初めてでも最後でもありません:-)。
例は次のとおりです(フラグ設定フェーズは、アクションが非常に単純なので、ifが複雑になる場合があります)。
switch (i) {
case 1:
outmsg = "no paper";
genmsg = true;
mailmsg = true;
phonemsg = false;
break;
case 2:
outmsg = "no ink";
genmsg = true;
mailmsg = true;
phonemsg = false;
break;
default:
outmsg = "unknown problem";
genmsg = true;
mailmsg = true;
phonemsg = true;
break;
}
if (genmsg)
// Send message to screen.
if (mailmsg)
// Send message to operators email address.
if (phonemsg)
// Hassle operators mobile phone.
連鎖ifはどうですか?
交換
if (condition1)
{
do1
}
else
{
if (condition2)
{
do2
}
else (condition3)
{
do3;
}
}
と
if (condition1) {
do1;
} else if (condition2) {
do2;
} else if (condition3) {
do3;
}
これは、複雑な条件のswitchステートメントによく似ています。
条件をブール値にしてから、各ケースにブール式を記述します。
コードが次の場合:
if (condition1)
{
do1
}
else
{
if (condition2)
{
do2
}
else (condition3)
{
do3;
}
}
次のように書くことができます:
bool cond1=condition1;
bool cond2=condition2;
bool cond3=condition3;
if (cond1) {do1;}
if (!cond1 and cond2) {do2;}
if (!cond1 and cond3) {do2;}
たとえば、検証の一部が失敗した場合にブレークできます。
function validate(){
if(b=="" || b==null){
alert("Please enter your city");
return false;
}
if(a=="" || a==null){
alert("Please enter your address");
return false;
}
return true;
}
決定テーブルでは、コード自体ではなくデータ構造に条件付きロジックを保存しますを使用します。
したがって、これの代わりに(@Paxの例を使用して):
if (i == 1) {
// action 1
} else {
if (i == 2) {
// action 2
} else {
if (i == 3) {
// action 3
} else {
// action 4
}
}
}
あなたはこのようなことをします:
void action1()
{
// action 1
}
void action2()
{
// action 2
}
void action3()
{
// action 3
}
void action4()
{
// action 4
}
#define NUM_ACTIONS 4
// Create array of function pointers for each allowed value of i
void (*actions[NUM_ACTIONS])() = { NULL, action1, action2, action3 }
// And now in the body of a function somewhere...
if ((i < NUM_ACTIONS) && actions[i])
actions[i]();
else
action4();
i
の可能性が小さい番号の整数でない場合、i
配列のactions
th要素に直接アクセスする代わりに、ルックアップテーブルを作成できます。
この手法は、多数の可能な値を決定するときに、ネストされたif
sまたはswitch
ステートメントよりもはるかに便利になります。
Ifおよびswitchステートメントは純粋にオブジェクト指向ではありません。それらは条件付き手続き論理ですが、非常に良い仕事をします!より多くのOOアプローチのためにこれらのステートメントを削除したい場合、 'State'パターンと 'Descriptor'パターンを組み合わせてください 。
Visitor pattern の使用を検討することもできます。
論理演算子ANDと同等の場合はネストされます
if (condition1)
{
if (function(2))
{
if (condition3)
{
// do something
}
}
}
同等のコード:
if (condition1 && function(2) && condition3)
{
// do something
}
どちらの場合も、式が偽と評価されると、後続の式は評価されません。たとえば、condition1がfalseの場合、function()は呼び出されず、condition3は評価されません。