回答
フラグを使用するコードを保守するときに私が見た問題は、状態の数が急速に増加し、ほとんどの場合、未処理の状態が存在することです。私自身の経験からの一例:これらの3つのフラグを持つコードに取り組んでいました
bool capturing, processing, sending;
これらの3つは8つの状態を作成しました(実際には、他に2つのフラグがありました)同じように)。考えられるすべての値の組み合わせがコードでカバーされているわけではなく、ユーザーにはバグが見られました。
if(capturing && sending){ // we must be processing as well ... }
ifステートメントの仮定が状況にあることが判明しました。上記は誤りでした。
フラグは時間の経過とともに複雑になる傾向があり、クラスの実際の状態を隠します。そのため、避ける必要があります。
コメント
回答
フラグが役立つ例を次に示します。
パスワードを生成するコードがあります(暗号的に安全な疑似乱数ジェネレーターを使用)。メソッドの呼び出し元は、パスワードに大文字、小文字、数字、基本記号、拡張記号、ギリシャ記号、キリル記号、ユニコードを含めることはできません。
フラグを使用すると、このメソッドを簡単に呼び出すことができます。
var password = this.PasswordGenerator.Generate( CharacterSet.Digits | CharacterSet.LowercaseLetters | CharacterSet.UppercaseLetters);
さらに次のように簡略化することもできます:
var password = this.PasswordGenerator.Generate(CharacterSet.LettersAndDigits);
フラグがない場合、メソッドの署名はどうなりますか?
public byte[] Generate( bool uppercaseLetters, bool lowercaseLetters, bool digits, bool basicSymbols, bool extendedSymbols, bool greekLetters, bool cyrillicLetters, bool unicode);
次のように呼ばれます:
// Very readable, isn"t it? // Tell me just by looking at this code what symbols do I want to be included? var password = this.PasswordGenerator.Generate( true, true, true, false, false, false, false, false);
コメントに記載されているように、別のアプローチはコレクションを使用することです。
var password = this.PasswordGenerator.Generate( new [] { CharacterSet.Digits, CharacterSet.LowercaseLetters, CharacterSet.UppercaseLetters, });
これは、true
とfalse
ですが、それでも2つの欠点があります。
主な欠点は、CharacterSet.LettersAndDigits
Generate()
メソッドでそのようなものを記述します:
if (set.Contains(CharacterSet.LowercaseLetters) || set.Contains(CharacterSet.Letters) || set.Contains(CharacterSet.LettersAndDigits) || set.Contains(CharacterSet.Default) || set.Contains(CharacterSet.All)) { // The password should contain lowercase letters. }
おそらく次のように書き直されます:
var lowercaseGroups = new [] { CharacterSet.LowercaseLetters, CharacterSet.Letters, CharacterSet.LettersAndDigits, CharacterSet.Default, CharacterSet.All, }; if (lowercaseGroups.Any(s => set.Contains(s))) { // The password should contain lowercase letters. }
フラグを使用して、これを現在の状態と比較します。
if (set & CharacterSet.LowercaseLetters == CharacterSet.LowercaseLetters) { // The password should contain lowercase letters. }
s econdの非常に小さな欠点は、次のように呼び出された場合にメソッドがどのように動作するかが明確でないことです。
var password = this.PasswordGenerator.Generate( new [] { CharacterSet.Digits, CharacterSet.LettersAndDigits, // So digits are requested two times. });
コメント
回答
巨大な機能ブロックは匂いです、フラグではありません。 5行目にフラグを設定した場合は、354行目のフラグのみを確認してください。それでは問題です。8行目にフラグを設定して10行目にフラグを確認した場合は、問題ありません。また、コードのブロックごとに1つまたは2つのフラグで問題ありませんが、関数内の300のフラグは不適切です。
回答
通常はフラグフラグのすべての可能な値に対して1つの戦略実装を使用して、戦略パターンのいくつかのフレーバーで完全に置き換えることができます。これにより、新しい動作の追加がはるかに簡単になります。
パフォーマンスが重要な状況では、間接参照のコストが表面化し、明確なフラグへの分解が必要になる可能性があります。 そうは言っても、実際にそれをしなければならなかった1つのケースを思い出すのに苦労しています。
回答
いいえ、フラグは悪いものでも悪いものでもないので、絶対にリファクタリングする必要があります。
Javaの Pattern.compile(String regex、int flags)<を検討してください。 / a>呼び出し。これは従来のビットマスクであり、機能します。 Javaの定数を一瞥すると、2 n がたくさんある場合は、フラグが存在することがわかります。
理想的なリファクタリングの世界では、代わりに EnumSet を使用します。定数は代わりに列挙型の値であり、ドキュメントには次のように記載されています。
このクラスの空間と時間のパフォーマンスは、従来のintベースの「ビットフラグ」に代わる高品質でタイプセーフな代替手段として使用できるほど十分に優れている必要があります。
完璧な世界では、そのPattern.compile呼び出しはPattern.compile(String regex, EnumSet<PatternFlagEnum> flags)
になります。
すべてそうは言っても、それはまだフラグです。Pattern.compile("foo", Pattern.CASE_INSENSTIVE | Pattern.MULTILINE)
を使用する方が、Pattern.compile("foo", new PatternFlags().caseInsenstive().multiline())
やその他のスタイルのフラグを実際に実行しようとするよりも、はるかに簡単です。
システムレベルのものを操作するときに、フラグがよく見られます。オペレーティングシステムレベルで何かとインターフェイスする場合、どこかにフラグが設定されている可能性があります。プロセスの戻り値、ファイルのアクセス許可、ソケットを開くためのフラグなどです。知覚されたコードの臭いに対して魔女狩りでこれらのインスタンスをリファクタリングしようとすると、フラグを受け入れて理解した場合よりも悪いコードになる可能性があります。
問題は、人々がフラグを誤用してそれらを一緒に投げると発生します。あらゆる種類の無関係なフラグのフランケンフラグセットを作成するか、フラグではない場所でそれらを使用しようとします。
回答
メソッドシグネチャ内のフラグについて話していると思います。
単一のフラグを使用するだけでは十分ではありません。
同僚が最初にそれを見たとき、それは何の意味もありません。彼らは「メソッドの機能を確立するために、メソッドのソースコードを調べる必要があります。メソッドの内容を忘れると、数か月後には同じ位置にいる可能性があります。
メソッドにフラグを渡すことは、通常、メソッドが複数のことを担当していることを意味します。メソッド内では、おそらく次の行で簡単なチェックを行っています。
if (flag) DoFlagSet(); else DoFlagNotSet();
それは「関心の分離が不十分であり、通常はそれを回避する方法を見つけることができます。
通常、2つの別々の方法があります:
public void DoFlagSet() { } public void DoFlagNotSet() { }
これは、解決しようとしている問題に適用できるメソッド名でより意味があります。
複数のフラグを渡すことは2倍悪いことです。本当に複数のフラグを渡す必要がある場合は、それらをクラス内にカプセル化することを検討してください。それでも、メソッドが複数のことを実行している可能性があるため、同じ問題に直面します。
回答
フラグとほとんどの温度変数は強い匂いです。ほとんどの場合、リファクタリングしてクエリメソッドに置き換えることができます。
改訂:
状態を表すときのフラグと一時変数は、クエリメソッドにリファクタリングする必要があります。状態値(ブール値、int、およびその他のプリミティブ)は、実装の詳細の一部としてほぼ常に非表示にする必要があります。
制御、ルーティング、および一般的なプログラムフローに使用されるフラグは、制御構造のセクションを、クエリメソッドを引き続き使用する個別の戦略やファクトリ、または状況に応じて適切なものにリファクタリングする機会。
回答
フラグについて話すときは、プログラムの実行中にフラグが変更され、状態に基づいてプログラムの動作に影響を与えることを知っておく必要があります。これら2つのことをきちんと制御できれば、うまく機能します。
フラグは、適切なスコープで定義していれば、うまく機能します。
- 適切とは、スコープに、変更する必要のない/変更すべきでないコードを含めないことを意味します。または、少なくともコードは安全です(たとえば、外部から直接呼び出されない場合があります)
- 外部からフラグを処理する必要があり、フラグが多数ある場合は、フラグハンドラーを唯一の方法としてコーディングできます。フラグを安全に変更します。このフラグハンドラー自体が、フラグとそれらを変更するメソッドをカプセル化する場合があります。次に、シングルトンにして、フラグへのアクセスが必要なクラス間で共有できます。
- 最後に、フラグが多すぎる場合は、保守性のために次のようにします。
- 必要があると言う必要はありません。賢明な命名に従う
- 有効な値を文書化する必要があります(列挙を使用する場合があります)
- それぞれを変更するコードと、結果として生じる条件を文書化する必要がありますフラグへの特定の値の割り当て。
- どのコードがそれらを消費し、特定の値に対してどのような動作が発生するか
フラグが非常に多い場合は、フラグがプログラムの動作で重要な役割を果たし始めるため、適切な設計作業を行う必要があります。モデリング用の状態図を参照できます。このような図は、それらを処理する際のドキュメントや視覚的なガイダンスとしても機能します。
これらが整っている限り、混乱を招くことはないと思います。
回答
質問から、QAは関数パラメーターのビットではなく、フラグ(グローバル)変数を意味していると思いました。
状況があります。他に多くの可能性がない場合。たとえば、オペレーティングシステムがない場合は、割り込みを評価する必要があります。割り込みが非常に頻繁に発生し、ISRで長い評価を行う時間がない場合は、許可されるだけでなく、ISRに一部のグローバルフラグのみを設定することもベストプラクティスになる場合があります(できるだけ時間をかけないでください)。 ISRで)、メインループでこれらのフラグを評価します。
回答
何でもは、プログラミングにおいて絶対的な悪です。
フラグが適切である可能性がある別の状況がありますが、 ここではまだ言及されていません…
このJavascriptスニペットでクロージャの使用を検討してください:
exports.isPostDraft = function ( post, draftTag ) { var isDraft = false; if (post.tags) post.tags.forEach(function(tag){ if (tag === draftTag) isDraft = true; }); return isDraft; }
渡される内部関数 「Array.forEach」に、単に「trueを返す」ことはできません。
したがって、フラグを使用して状態を外部に保持する必要があります。