web-dev-qa-db-ja.com

このアンチパターンの名前は?ローカル変数としてのフィールド

私がレビューしているいくつかのコードで、私は以下の道徳的同等物であるものを見ています:

public class Foo
{
    private Bar bar;

    public MethodA()
    {
        bar = new Bar();
        bar.A();
        bar = null;
    }

    public MethodB()
    {
        bar = new Bar();
        bar.B();
        bar = null;
    }
}

ここでのフィールドbar論理的にローカル変数です。その値はメソッド呼び出し間で永続化されることを意図していないためです。ただし、Fooのメソッドの多くはBar型のオブジェクトを必要とするため、元のコード作成者はBar型のフィールドを作成しました。

  1. これは明らかに悪いですよね?
  2. このアンチパターンに名前はありますか?
69
JSBձոգչ

これは明らかに悪いですよね?

うん。

  • これは、メソッドを再入不可能にします。これは、同じインスタンスで再帰的に呼び出された場合やマルチスレッドコンテキストで呼び出された場合に問題になります。

  • これは、ある呼び出しから別の呼び出しに状態がリークすることを意味します(再初期化を忘れた場合)。

  • 上記をチェックして、コードが実際に何をしているかを確認する必要があるため、コードが理解しにくくなります。 (ローカル変数の使用と比較してください。)

  • Fooインスタンスが必要以上に大きくなります。 (そして、これをN個の変数に対して行うことを想像してください...)

このアンチパターンに名前はありますか?

IMO、これはアンチパターンと呼ばれるに値しません。これは単に悪いコーディング習慣/ Java構文/がらくたコードの誤用です。これは、学生が講義をスキップしたり、学部生のコードを読み飛ばしたりしたときに、学部のコードに表示されるようなものです。プログラミングの適性はありません。本番コードでそれを確認した場合は、さらに多くのコードレビューを行う必要があることを示しています...


記録のために、これを書く正しい方法は:

public class Foo
{
    public methodA()
    {
        Bar bar = new Bar();  // Use a >>local<< variable!!
        bar.a();
    }

    // Or more concisely (in this case) ...
    public methodB()
    {
        new Bar().b();
    }
}

Java識別子の受け入れ可能なスタイルルールに準拠するように、メソッドと変数の名前も修正しました。

87
Stephen C

変数の不要な大きなスコープと呼びます。この設定により、複数のスレッドがMethodAとMethodBにアクセスする場合の競合状態も可能になります。

39
Th 00 mÄ s

これは、global doorknobbingと呼ばれる一般的なパターンの特定のケースです。家を建てるとき、ドアノブを1つだけ購入し、横に置いておくのは魅力的です。誰かがドアや食器棚を使いたいときは、そのグローバルドアノブを1つつかみます。ドアノブが高価な場合、それは良いデザインになる可能性があります。

残念ながら、あなたの友人がやって来て、ドアノブが2つの異なる場所で同時に使用されると、現実は粉々になります。ドアノブが高すぎて1つしか買うことができない場合は、ドアノブを順番に待つのを安全にできるシステムを構築することは価値があります。

この場合、ドアノブはあくまで参考であり、安価です。ドアノブが実際のオブジェクトだった場合(そして、参照だけでなくオブジェクトを再利用していた場合)は、prudent global doorknobになるのに十分なコストがかかる可能性があります。ただし、安価な場合はcheapskate global doorknobと呼ばれます。

あなたの例はcheapskateのバリエーションです。

30
Ned Twigg

ここでの主な関心事は同時実行性です。Fooが複数のスレッドで使用されている場合、問題が発生します。

それを超えて、それはただ愚かです-ローカル変数は自身のライフサイクルを完全にうまく管理します。それらが不要になったときにnullにする必要のあるインスタンス変数に置き換えると、エラーが発生します。

19
Matthew Flynn

これは、「変数の再利用」という側面を持つ、「不適切なスコープ」の特定のケースです。

15
ikegami

それはアンチパターンではありません。アンチパターンにはいくつかの特性があり、良い考えのように見えるため、人々は故意にそれを行うようになります。それらはパターンとして計画されており、それはひどく間違っています。

それはまた、何かがパターンであるか、アンチパターンであるか、または一部の場所でまだ使用されている一般的に誤って適用されたパターンであるかどうかについての議論を生むものでもあります。

これは間違っています。

もう少し追加します。

このコードは迷信的であり、せいぜいカーゴカルトの習慣です。

迷信は明確な正当化なしに行われるものです。それは現実のものに関連している可能性がありますが、接続は論理的ではありません。

カーゴカルトの慣習は、より知識のある情報源から学んだことをコピーしようとするものですが、実際にはプロセスではなく表面のアーティファクトをコピーしています(パプアニューギニアのカルトと呼ばれる第二次世界大戦の日本とアメリカの飛行機が戻ってくることを期待して竹から航空機のラジオを制御します)。

これらのどちらの場合も、実際のケースはありません。

アンチパターンは、小規模(処理する必要がある追加のケースに対処するための追加の分岐、スパゲッティコードにつながる)であるか、パターンを非常に意図的に実装する大規模であるかに関わらず、合理的な改善の試みですそれは信用されていないか議論されています(多くはシングルトン自体を説明しますが、一部は書き込み専用を除外します-たとえば、ロギングオブジェクトまたは読み取り専用、たとえば構成設定オブジェクト-一部はそれらを非難します)、またはあなたが解決している場所間違った問題(.NETが最初に発表されたとき、MSは、管理されていないフィールドと使い捨ての管理されたフィールドの両方がある場合の破棄の処理パターンを推奨しました-実際、その状況に非常によく対処しますが、実際の問題は、同じクラスの両方のタイプのフィールド)。

このように、アンチパターンは、言語、問題ドメイン、利用可能なライブラリをよく知っている賢い人が故意に行うものであり、それでも、良い面を圧倒するマイナス面を持っています(または持っていると主張されています)。

与えられた言語、問題ドメイン、利用可能なライブラリーについて十分に理解し始めていないため、そして合理的な解決策から別の解決策に移行するときに、誰もが何かを見落とす可能性があるためです(たとえば、適切な使用のためにフィールドに何かを保存し始めてから、それをリファクタリングしますが、ジョブは完了しません。質問のようなコードが作成されます)。また、学習で時々見落としているため、ある時点ですべての迷信的またはカーゴカルトコードを作成しました。良い点は、アンチパターンよりも識別と修正が実際に明確であることです。真のアンチパターンは間違いなくアンチパターンではないか、魅力的な品質であるか、または悪いと識別された場合でも少なくともそれらを引き寄せる方法があります(レイヤーが多すぎたり少なすぎたりすると、議論の余地なく悪いが、1つは避けます他につながる)。

8
Jon Hanna

(1)良くないと思います。

スタックがローカル変数を使用して自動的に実行できるのは手動のハウスキーピングです。

「新規」は各メソッド呼び出しと呼ばれるため、パフォーマンス上の利点はありません。

編集:クラスのメモリフットプリントは、バーポインターがクラスの存続期間中常にメモリを占有するため、大きくなります。バーポインターがローカルの場合、メソッド呼び出しの有効期間中のみメモリを使用します。ポインターの記憶は、海に落ちた一滴ですが、それでも不必要な一滴です。

バーはクラス内の他のメソッドから見えます。 barを使用しないメソッドはそれを見ることができないはずです。

状態ベースのエラー。この特定のケースでは、 "new"が毎回呼び出されるため、状態ベースのエラーはマルチスレッドでのみ発生します。

(2)それは実際にはいくつかの問題であり、1つではないため、名前があるかどうかはわかりません。
-自動が利用可能な場合の手動ハウスキーピング
-不必要な状態
-寿命よりも長生きする状態
-可視性またはスコープが高すぎます

3
mike30

ここで粒子に反対しますが、与えられた例が現在の形式のように良いコーディングスタイルであるとは考えていませんが、patternは存在しますユニットテストを行います。

単体テストを行うこれはかなり標準的な方法です

public class BarTester
{
    private Bar bar;

    public Setup() { bar = new Bar(); }
    public Teardown() { bar = null; }

    public TestMethodA()
    {
        bar.A();        
    }

    public TestMethodB()
    {
        bar.B();
    }
}

これはOPのコードと同等のリファクタリングです

public class BarTester
{
    private Bar bar;

    public TestMethodA()
    {
        bar = new Bar();
        bar.A();
        bar = null;
    }

    public TestMethodB()
    {
        bar = new Bar();
        bar.B();
        bar = null;
    }
}

OPの例のようにコードを記述することは決してありませんが、リファクタリングを叫びますが、パターン無効でもアンチパターンでもないと見なします。

2

それを「ワンダリング・ゼノフォーブ」と呼んでいます。それはそのままにしたいのですが、それがどこにあるのか、あるいはそれが何であるのかをまったく知らないままになってしまいます。したがって、他の人が述べているように、それは悪いことです。

2
World Engineer

このコードのにおいは、Beck/Fowlerによるリファクタリングの一時フィールドと呼ばれています。

http://sourcemaking.com/refactoring/temporary-field

モデレーターのリクエストにより詳細な説明を追加するために編集:コードのにおいの詳細については、上記の参照URLまたは本のハードコピーを参照してください。 OPがこの特定のアンチパターン/コードの臭いの名前を探していたので、10年以上前の確立されたソースからこれまで言及されていない参照を引用することが役立つと思いましたが、この質問についてのゲームなので、回答が投票または承認されることはほとんどありません。

個人的な宣伝ではない場合は、この特定のコードの匂いについても、来たるPluralsightコースのRefactoring Fundamentalsで参照および説明します。これは2013年8月に公開される予定です。

さらに価値を付加するために、この特定の問題を修正する方法について話しましょう。いくつかのオプションがあります。フィールドが本当に1つのメソッドでのみ使用されている場合は、ローカル変数に降格できます。ただし、複数のメソッドが(パラメーターの代わりに)通信にフィールドを使用している場合、これはリファクタリングで説明されている典型的なケースであり、フィールドをパラメーターに置き換えることで修正できます。または、このコードで機能するメソッドが密接である場合関連して、Extract Classを使用して、メソッドとフィールドを独自のクラスに引き出すことができます。この場合、フィールドは常に有効な状態にあり(おそらく構築中に設定されます)、この新しいクラスの状態を表します。パラメータールートを使用する場合に渡す値が多すぎる場合、別のオプションは、個々の変数すべてではなく渡すことができる新しいパラメーターオブジェクトを導入することです。

2
ssmith

これは、Flyweightパターンの恐ろしい誤用のように見えます。残念ながら、いくつかの開発者が異なる方法で同じ「もの」を複数回使用し、メモリを節約したり、パフォーマンスを改善したり、その他の奇妙な疑似最適化を行ったりして、誤ってプライベートフィールドに引き出さなければならないことを知っています。 Flyweightは、実際に解決する必要のある問題ではない状況で、問題を解決することだけを目的としているため、彼らは同じような問題を解決しようとしていると考えています。

1
Evicatos

すぐに理解すると、これはまとまりの欠如であり、「偽のまとまり」という名前を付けるかもしれません。この用語は、クラスがまとまりがあり、そのメソッドがメンバーフィールドで動作しているように見えるため、この用語を使用します(または、どこかからそれを取得している場合は、「借用」します)。ただし、実際にはそうではありません。つまり、見かけの結束は実際には誤りです。

悪いかどうかははっきりと言えますが、Stephen Cはその理由を説明するのに優れていると思います。しかし、それが「アンチパターン」と呼ばれるに値するかどうかに関係なく、私はそれと他のコーディングパラダイムがラベルで行うことができると思います。

1
Erik Dietrich

すでに述べた問題は別として、自動ガベージコレクションのない環境(C++など)でこのアプローチを使用すると、使用後に一時オブジェクトが解放されないため、メモリリークが発生します。 (これは少しフェッチされているように見えるかもしれませんが、「null」を「NULL」に変更してコードがコンパイルされることに満足する人々がいます。)

1
peterph