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


68

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

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論理的にローカル変数です。これは、その値がメソッド呼び出し間で持続することを意図していないためです。ただし、メソッドの多くはFootypeのオブジェクトを必要とするBarため、元のコード作成者はtypeのフィールドを作成しましたBar

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

60
まあ、それは新しいものです。このクラスがマルチスレッドコンテキストで使用されないことを願っています。
TMN

8
これは本当に珍しいことですか?私はこれまでキャリアで何度も見てきました。
JSBձոգչ12年

32
@TMNスレッドセーフではありませんが、さらに悪いことに、リエントラントでさえありません。任意のコードでいる場合MethodAMethodB、原因MethodAまたはがMethodBどのような方法で呼ばれるように(とbarの場合には、再び使用されbar.{A,B}()ている犯罪者)あなたも、任意の同時実行せずに同様の問題を持っています。

73
だれかができるすべての愚かなことに名前をつける必要があるのでしょうか?それを悪い考えと呼んでください。
カレブ

74
ぶら下がるプライベートアンチパターンと呼ばないのは難しい。
psr

回答:


86

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

うん。

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

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

  • コードが実際に実行していることを確認するために上記を確認する必要があるため、コードが理解しにくくなります。(ローカル変数の使用とは対照的です。)

  • 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識別子の受け入れられたスタイル規則に準拠していることに注意してください。


11
「本番コードで見た場合、採用プロセス再検討する必要があることを示しています」
ベータ版

@Beta-それも、激しいコードレビューでそのような悪いコーディング習慣を修正できるはずですが。
スティーブンC

コードレビューの詳細については、+ 1。人々が考えていることにもかかわらず、雇用慣行を改善してもこの種の問題を防ぐことはできません。
マッテンツ

@StephenC「メソッドをリエントラントにしないため、同じインスタンスで再帰的に、またはマルチスレッドコンテキストで呼び出されると問題になります。」。私は再入可能性が何であるかを知っていますが、メソッドがロックを使用していないため、ここでこの点を見ることができませんか?説明してください。
オタク

@Geek-特定の例に集中しすぎています。メソッドが複数のスレッドから、または再帰的に呼び出される可能性がある一般的なケースについて話しています。
スティーブンC

39

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


12
具体的には、ここでは「変数スコープ」が問題の名前だと思います。この人は、ローカル変数とは何か、どのように/いつ使用するかを学ぶ必要があります。肯定的なパターンまたは一般的なルールは、各変数に使用可能な最小のスコープを使用し、必要に応じてのみ拡張することです。明確にするために、このエラーは単に悪いスタイルではありません。このような競合状態のコーディングはエラーです。
グレンペターソン

30

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

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

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

あなたの例はcheapskateさまざまです。


19

ここでの主な懸念は並行性です。Fooが複数のスレッドで使用されている場合、問題があります。

それを超えて、それは単なる愚かです-ローカル変数は独自のライフサイクルを完璧に管理します。それらが有用でなくなったときに無効にする必要があるインスタンス変数で置き換えることは、エラーの誘因です。


15

これは、「不適切なスコープ」の特殊なケースであり、「変数の再利用」という側面があります。


7

アンチパターンではありません。アンチパターンには、良いアイデアのように思わせるいくつかの特性があり、人々が意図的にそれを行うように導きます。それらはパターンとして計画されており、ひどく間違っています。

また、何かがパターン、アンチパターン、またはいくつかの場所でまだ使用されている一般的に誤用されたパターンであるかどうかについての議論になります。

これは間違っています。

もう少し追加します。

このコードは迷信的であるか、せいぜい貨物カルトの慣行です。

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

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

これらの両方のケースで、実際のケースを作成する必要はありません。

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

そのため、アンチパターンは、言語、問題領域、および利用可能なライブラリを熟知している賢明な人が意図的に行うものであり、それでも欠点を抱えている(または抱えていると主張されている)欠点を克服しています。

誰も特定の言語、問題領域、利用可能なライブラリをよく知らないので、誰もが合理的な解決策から別の解決策に移行するときに何かを逃す可能性があるので(たとえば、使用目的のためにフィールドに何かを保存し始めてから、それをリファクタリングしますが、仕事を完了しません、そしてあなたは質問のようなコードになります)、そして私たちは学習で時々物事を見逃しているので、私たちはいくつかの点で迷信的または貨物カルトなコードを作成しました。良いことは、アンチパターンよりも実際に識別して修正する方が明確であることです。真のアンチパターンは、ほぼ間違いなくアンチパターンではないか、魅力的な品質を持っているか、少なくとも悪いと特定された場合でもそれらを引き付ける何らかの方法があります(層が多すぎて少なすぎると、議論の余地なく悪いが、1つを避ける他につながる)。


1
しかし、人々おそらくこれがコードの再利用の一種であると考えるため、これは良い考えだと考えています。
JSBձոգչ12年

初心者は多くの場合、2つの変数の宣言に何らかの理由で2倍のスペースが必要であると考えるため、変数を再利用することを好みます。これは、メモリモデルの誤解(空きストアとスタック、スタックロケーションの再利用の可能性)およびすべてのコンパイラが実行できる最適化を示しています。したがって、初心者は変数の再利用を良いアイデアと考えるかもしれないので、アンチパターンとして分類できると主張します。
フィリップ

それは迷信であり、アンチパターンではありません。
ジョンハンナ

3

(1)それは良くないと言うでしょう。

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

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

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

バーはクラス内の他のメソッドに表示されます。barを使用しないメソッドはそれを見ることができません。

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

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


2

私はそれを「放浪するXenophobe」と呼びます。それは放っておかれたいが、それがどこにあるか、何であるかを全く知らないままになってしまう。したがって、他の人が述べているように、それは悪いことです。


2

ここで穀物に反しますが、現在の形式のように、与えられた例を良いコーディングスタイルとは考えていませんが、単体テストを行っている間にパターンは存在します。

ユニットテストを行うこのかなり標準的な方法

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の例で与えられるように、私はコードを書くことはない、それはリファクタリングを叫ぶが、私は考えるパターンでもないし、無効でもアンチパターン


その場合、フィクスチャはテストごとに独立してインスタンス化され、変数の再利用または同時実行性に関連する問題は発生しません。一般的なケース(単体テスト以外)では、各オブジェクトで最大1つのメソッドが呼び出されるかどうかはわかりません。
フィリップ

1
@Philipp-再利用または同時実行の問題に異議を唱えませんが、OPの質問は、単体テストで一般的に使用されるパターンに関するものでした。フィクスチャが各テストに対してインスタンス化されるという事実は、テストフレームワークの実装の詳細です。単体テストでも、テストフレームワークが使用されるはずであるときに使用される場合にのみ、パターンは安全です。本番コードでこのパターンを使用する場合、同じ推論が適用されます。
リーベンキースマッカーズ

に設定barする必要はありませんnull。JUnitはテストメソッドごとに新しいテストインスタンスを作成します。
13

2

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

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

モデレーターのリクエストにより説明を追加するように編集:上記の参照URLまたは本のハードコピーでコードの匂いについて詳しく読むことができます。OPはこの特定のアンチパターン/コードの匂いの名前を探していたので、10歳以上の確立されたソースからこれまで言及されていなかった言及を引用すると役立つと思いましたが、この質問に答えるので、答えが投票されるか受け入れられる可能性は低いです。

個人的なプロモーションではない場合は、2013年8月に公開される予定の、今後のPluralsightコース、Refactoring Fundamentalsでこの特定のコードの匂いを参照して説明します。

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


抽出クラスのリファクタリングには一時フィールドが必要になることが多いことに注意することが重要です。しかし、これを知っている人は、おそらくこの中間状態のコードをチェックインしないでしょう。
13

1

あなたがそれを理解するとき、私は言う、これは本当に結束の欠如であり、私はそれに「偽の結束」という名前を付けるかもしれません。クラスはメソッドがメンバーフィールドで動作するように見えるという点で凝集性があるように見えるので、私はこの用語を作成します(またはどこかから取得して「借用」することを忘れた場合)。ただし、実際にはそうではありません。つまり、見かけの凝集は実際には間違っています。

それが悪いかどうかについては、明らかにそうだと思います。スティーブンCはその理由を説明する上で素晴らしい仕事をしていると思います。しかし、それが「アンチパターン」と呼ばれるに値するかどうかに関係なく、私はそれと他のコーディングパラダイムがラベルでできると思います。


1

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


1

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


-1

私はこれに何度も遭遇しました。通常、VBA For Dummiesを最初のタイトルとして選んだ人が以前に書いたコードを更新し、つまずいた言語で比較的大規模なシステムを書き続けました。

プログラミングのやり方が本当に悪いと言うのは正しいことですが、それはインターネットとピアレビューのリソースがないことの結果である可能性があります。

しかし、実際には「アンチパターン」タグに値するわけではありません-しかし、OPをどのように再コーディングできるかの提案を見るのは良いことです。


1
Stack Exchange Programmersへようこそ。最初の回答を入力していただきありがとうございます。おそらくFAQのProgrammers.stackexchange.com/faqにリストされている理由が原因で、反対票があったようです。よくある質問は、効果的な(そして賛成票を投じた)答えを出すための素晴らしい提案を提供します。時には、人々は、何らかの方法で間違っているか、重複しているか、証拠のない意見か、以前の答えを繰り返す私自身かを示すために、反対票に関するメモを追加するのに十分です。私たちのほとんどは、反対票を投じたり、閉鎖したり、回答を削除したりしているので、心配する必要はありません。これは開始の一部にすぎません。ようこそ。
DeveloperDon
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.