コードが非常に複雑になり、保守が困難になった場合、リファクタリングと最適化しかできません。それで、これらの冗長なif / elseをエレガントに最適化するための解決策はありますか?
1.事前に返却する
これは、判断条件を逆にする方法です。コードは論理式でより明確になります。次のコードを見てください。
if (condition) {
// do something
} else {
return xxx;
}
実際、上記のコードを見るたびにかゆみを感じ、最初にそれを判断し!condition
て他のコードを殺すことができます。
if (!condition) {
return xxx;
}
// do something
2.戦略モード
異なるパラメータに応じて異なるロジックが使用されるシナリオがあります。実際、このシナリオは非常に一般的です。
最も一般的な実装:
if (strategy.equals("fast")) {
// 快速执行
} else if (strategy.equals("normal")) {
// 正常执行
} else if (strategy.equals("smooth")) {
// 平滑执行
} else if (strategy.equals("slow")) {
// 慢慢执行
}
上記のコードを見ると、4つの戦略と2つの最適化スキームがあります。
2.1ポリモーフィズム
interface Strategy {
void run() throws Exception;
}
class FastStrategy implements Strategy {
@Override
void run() throws Exception {
// 快速执行逻辑
}
}
class NormalStrategy implements Strategy {
@Override
void run() throws Exception {
// 正常执行逻辑
}
}
class SmoothStrategy implements Strategy {
@Override
void run() throws Exception {
// 平滑执行逻辑
}
}
class SlowStrategy implements Strategy {
@Override
void run() throws Exception {
// 慢速执行逻辑
}
}
特定の戦略オブジェクトは、最適化後の実装であるマップに保存されます
Strategy strategy = map.get(param);
strategy.run();
上記の最適化スキームには欠点があります。対応する戦略の実装をすばやく取得するには、戦略を保存するためのマップオブジェクトが必要です。新しい戦略を追加する場合は、手動でマップに追加する必要もあります。これは簡単です。無視されます。
2.2列挙
多くの学生は、メソッドを列挙型で定義できることを知らないことがわかりました。ここでは、状態を表す列挙型を定義し、runメソッドを実装することもできます。
public enum Status {
NEW(0) {
@Override
void run() {
//do something
}
},
RUNNABLE(1) {
@Override
void run() {
//do something
}
};
public int statusCode;
abstract void run();
Status(int statusCode){
this.statusCode = statusCode;
}
}
戦略の列挙を再定義する
public enum Strategy {
FAST {
@Override
void run() {
//do something
}
},
NORMAL {
@Override
void run() {
//do something
}
},
SMOOTH {
@Override
void run() {
//do something
}
},
SLOW {
@Override
void run() {
//do something
}
};
abstract void run();
}
列挙による最適化後のコードは次のとおりです。
Strategy strategy = Strategy.valueOf(param);
strategy.run();
3.オプションの使い方を学ぶ
オプションは主に空でない判断に使用されます。jdk8の新機能であるためあまり使用されませんが、とてもかっこいいです。
使用前:
if (user == null) {
//do action 1
} else {
//do action2
}
ログインしたユーザーが空の場合は、アクション1を実行し、それ以外の場合はアクション2を実行します。オプションの最適化を使用した後、null以外の検証をより洗練されたものにし、if操作を間接的に減らします。
Optional<User> userOptional = Optional.ofNullable(user);
userOptional.map(action1).orElse(action2);
4.アレイのヒント
グーグルの説明によると、これはテーブルドリブンメソッドと呼ばれるプログラミングモデルです。本質は、論理ステートメントではなくテーブルから情報をクエリすることです。たとえば、月の日数が月を通して取得されるシナリオがあります。これはケースデモンストレーションとしてのみ使用され、データは厳密ではありません。
一般的な認識:
int getDays(int month){
if (month == 1) return 31;
if (month == 2) return 29;
if (month == 3) return 31;
if (month == 4) return 30;
if (month == 5) return 31;
if (month == 6) return 30;
if (month == 7) return 31;
if (month == 8) return 31;
if (month == 9) return 30;
if (month == 10) return 31;
if (month == 11) return 30;
if (month == 12) return 31;
}
最適化されたコード
int monthDays[12] = {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
int getDays(int month){
return monthDays[--month];
}
終わり
そうでなければ、すべてのプログラミング言語で不可欠な条件文として、プログラミングで多く使用されます。入れ子は3レベルを超えないようにすることをお勧めします。入れ子が多すぎると、コードの可読性が大幅に低下し、その後のメンテナンスの難しさが大幅に改善されます。
視点2(IT技術制御):
if / elseレイヤーの数にはあまり注意を払わないでください。ただし、インターフェイスのセマンティクスが十分に明確であるかどうかに注意してください。単にif / elseレイヤーの数を減らしてから、一連のdo_logic1を取り出します。 do_logic2 ...そのようなインターフェースは役に立たない。
任意のインターフェイスの実行プロセスは、入力+内部状態->出力として表すことができます。次の状況で説明します。
入力、内部状態、および出力はすべて単純ですが、中間論理は複雑です。たとえば、慎重に最適化された数値計算プログラムは、入力に応じてさまざまな値の範囲でさまざまな戦略を採用する必要がある場合があり、問題(0による除算など)を引き起こす境界値を処理するための多くのロジックがあります。 / elseの数が多いことが避けられない場合は、手順に応じていくつかの内部メソッドを分割すると便利ですが、問題を完全に解決することはできません。この場合の最善の方法は、最も原始的な数学モデルから始めて詳細なドキュメントを作成し、どのような状況でどのような計算戦略を採用するか、戦略を導き出す方法、で使用される特定の形式を知ることです。コードを作成し、メソッド全体にコメントを追加してドキュメントアドレスを添付し、各ブランチにコメントを追加して、どの式がドキュメントに対応するかを示します。この場合、メソッドは非常に複雑ですが、セマンティクスは明確です。実装を変更しない場合は、セマンティクスを理解できます。実装を変更する場合は、制御ドキュメントの式を参照する必要があります。 。
入力が複雑すぎます。たとえば、入力にさまざまなパラメータがたくさんある場合や、さまざまな奇妙なフラグがあり、各フラグの機能が異なる場合などです。この場合、最初にインターフェースの抽象化レベルを改善する必要があります。インターフェースに複数の異なる機能がある場合は、異なるインターフェースに分割する必要があります。インターフェースが異なるパラメーターに従って異なるブランチに分割されている場合、これらのパラメーターと対応するブランチをアダプタにパッケージ化して使用する必要がありますパラメータはアダプタのインターフェイスに書き換えられ、渡されたアダプタのタイプに応じてさまざまな実装が入力されます。インターフェイス内に複雑なパラメータ変換関係がある場合は、ルックアップテーブルに書き換えられます。この場合の主な問題は、インターフェース自体の抽象化です。より明確な抽象化の後、当然、if / elseの実装はそれほど多くありません。
出力が複雑すぎます。トラブルを回避するために、1つのプロセスで計算されるものが多すぎます。また、計算するかどうかを制御するパフォーマンスのために、多数のフラグが追加されています。この場合、メソッドを複数の異なるメソッドに決定的に分割する必要があり、各メソッドは必要なものだけを返します。異なる計算間で内部結果が共有されている場合はどうなりますか?この内部結果の計算でボトルネックが発生しない場合は、内部メソッドを抽出して、さまざまなプロセスで個別に呼び出します。繰り返しの計算を避けたい場合は、パラメータとしてキャッシュオブジェクトを追加できます。キャッシュの内容は透過的ではありません。ユーザーは、同じキャッシュオブジェクトを同じ入力に使用できることを保証するだけです。計算では、中間結果がキャッシュに保存されます。次の計算の前に、結果がすでに取得されているかどうかを確認してください。これにより、回避できます。二重計算。
内部状態が複雑すぎます。まず、状態設定が適切かどうかを確認します。内部状態で入力パラメーターとして使用する必要があるものはありますか(たとえば、2つの異なるメソッド呼び出し間でパラメーターを暗黙的に渡すために使用されます)。次に、これらの状態によって制御される側面はどれですか。また、それらをグループ化して、さまざまなStateManagerに実装できますか?3番目に、状態遷移図を作成し、内部状態を単一レベルのブランチに分割してから、on_xxx_stateなどのメソッドに実装してから、単一レベルのスイッチまたはルックアップテーブルを介して呼び出します。
実際、通常最適化する必要があるのは、単一のインターフェイスの実現ではなく、インターフェイス全体の抽象化です。単一のインターフェイスの実現が不明確なのは、通常、インターフェイスの実装と要件の構成が異なるためです。