静的なメソッドだけを含む*** Helperまたは*** Utilクラスの使用はAntiPatternです


19

私はしばしば、Javaのヘルパークラスまたはutilクラスまたはあらゆる種類の言語に直面しています。それで、私はこれがアンチパターンの一種であり、この種のクラスの存在はソフトウェアの設計とアーキテクチャに欠けているだけかどうかを自問していた。

多くの場合、これらのクラスは、多くのことを行う静的メソッドのみを使用して制限されます。しかし、ほとんどの場合、実際にはコンテキストに依存し、ステートフルです。

私の質問は、そのような種類の静的ヘルパー/ユーティリティクラスについてのあなたの意見は何ですか?利点はもちろんクラス名だけを使用した高速呼び出しであるためです。

そして、これらの種類のクラスの使用を避けるのは、どのような抽象化レベルですか?

私の意見では、キーワード「static」はクラスの宣言(Java)内でのみ許可され、メソッドでは許可されません。私の意見では、この方法でそれを使用することは、Javaで手続き型パラダイムとオブジェクト指向パラダイムを組み合わせて、キーワードの誤用を回避するための優れた代替手段であると考えられます。

答えによる追加:

最初は、異なるパラダイムを組み合わせたり、マシンまたはvmでコンパイルされたコード内で実行時解釈スクリプト言語を使用したりすることは完全に合法だと思います。

私の経験では、プロジェクトの開発プロセス中に、そのような種類のヘルパーやユーティリティ、または名前が何であれ、コードベースの忘れられた隅々で成長し、使用されています。そして、リファクタリングを行う時間や設計をもう一度考える時間がないため、時間の経過とともにそれをさらに悪化させます。

staticJavaから削除する必要があると思います。特に今では、さらに洗練された関数型言語要素を使用することができます。



ヘルパーがクラスをインスタンス化する必要がなければ、それでいいと思います。問題は、ヘルパーがクラスの具体的な実装をインスタンス化し、そのインターフェイスをモックできない場合です。ヘルパーがインターフェイスまたは抽象クラスを渡された場合、それは大丈夫だと思います。
bobek

手続き型プログラミングを行っていると主張するのは問題ありません。オブジェクト(指向)プログラミングを行っていると主張する場合ではありません。
発見

1
@Spotted:そして、オブジェクト指向プログラミングだけを行うと主張することにはビジネス上の価値がないので、常にパラダイムプログラミングを混合して、たわごとを終わらせてください。
-RemcoGerlich

@RemcoGerlichまさに。私のポイントは、答えは主に「哲学的」であり、どのプログラミングパラダイムからそれを検討するかということに注意することでした。
発見

回答:


20

Javaには無料の関数が不足しているため、それらを静的関数としてプロフォーマクラスに入れる必要があります。優雅さを欠くかもしれませんが、必要な回避策は決してアンチパターンではありません。

次に、無料の関数には何の問題もありません。実際には、無料の関数を使用すると、すべての面倒な詳細の代わりにパブリックインターフェイスにしかアクセスできないため、結合が減少します。

もちろん、自由/静的関数を使用しても、可変の共有状態、特にグローバルな状態の危険性は決して軽減されません。


2
@TimothyTruckleどちらか彼らは静的で、または、彼らは余分を持っているthis、と何かが適切にインスタンス化することが必要
Caleth

6
@TimothyTruckle Javaだから。他の言語では、インスタンスメソッドではないことを宣言するためだけにクラスに追加するオーバーヘッドなしに、無料の関数を使用できます。そして、ある時点で、密結合が必要になります。良いことだかどうかは完全にアップ問題の関数がどのようなことです
-nvoigt

5
@TimothyTruckle絶対に、状態を維持することが「良くない」ことの主な理由になるでしょう。
nvoigt

2
@nvoigt、私はこの答えがその点を明示的に述べることによって大いに改善されると思う。ステートフルスタティックは不良です。ステートレスのものは良いです。本当に簡単です。
デビッドアルノ

3
@TimothyTruckleそれは密結合ではありません。疎結合について説明しています。この文脈での密結合は、ある種の相互依存関係を意味します。一方向の依存関係はその要件を満たしていません。
ジミージェームズ

18

静的ユーティリティまたはヘルパー関数は、いくつかのガイドラインに従っている場合、アンチパターンではありません。

  1. 副作用がないはずです

  2. これらは、これらの関数が動作するクラスに属さないアプリケーション固有の動作に使用されます

  3. 共有状態は必要ありません

一般的な使用例:

  • アプリケーション固有の方法で日付をフォーマットする
  • 1つのタイプを入力として受け取り、別のタイプを返す関数。

たとえば、私が取り組んだアプリケーションでは、ユーザーはアクティビティのジャーナルエントリを保持します。フォローアップ日を指定し、イベントリマインダーをダウンロードできます。ジャーナルエントリを取得し、.icsファイルの生テキストを返す静的ユーティリティクラスを作成しました。

共有状態は必要ありませんでした。状態を変更することはなく、iCalイベントの作成は確かにアプリケーション固有であり、ジャーナルエントリクラスに属していませんでした。

静的関数またはユーティリティクラスに副作用がある場合、または共有状態が必要な場合、このコードを再評価することをお勧めします。これにより、単体テストでモックすることが困難なカップリングが導入されるためです。


4

それはあなたのアプローチ次第です。多くのアプリケーションは、従来の考え方(多くのサイトのスイートを含む)とは対照的に、より機能的に書かれています。

この考え方により、ワーカークラスには、静的メソッドとして結び付けることができる多くのユーティリティメソッドがあります。データを保持するだけの単純なオブジェクトにより近い場所でフィード/使用されます。

これは有効なアプローチであり、特に大規模で非常にうまく機能します。


「特に大規模な環境で非常にうまく機能します」いいえ。静的アクセスによる密結合の原因は、プロジェクトの成長とともにすぐに邪魔になります。
ティモシートラックル

@TimothyTruckle Agent.Save(obj)がobj.Save()よりもはるかに密結合されていることを説明できますか?エージェントは、クラスインスタンスのような静的メソッドであっても、DI / IoC参照を取得できます。参考までに、Stack Exchange自体はこのタイプの考え方で記述されており、私が知っている他のどのリソースよりも少ないリソースで他のどのASP.Netアプリよりも優れた拡張性を備えています。
トラッカー1

「Agent.Save(obj)がobj.Save()よりもはるかに密結合されていることを説明できますか?」これは間違った例です。正しい例はAgent.Save(obj)vs agentInstance.Save(obj)です。後者を使用すると、使用中のコードに挿入 agentInstanceする可能性があります。その結果では、注入することができる任意の子クラスAgent異なる「種類」ので使用してコードOG再利用することができますあまりにもAgent再コンパイルせずに。これは低結合です。
ティモシートラックル

それは本当にいくつかのことになると思います。状態は必要ですか、それはどれほど柔軟である必要があり、グローバル/インスタンス状態になっていても大丈夫ですか?それは決定する各開発者/アーキテクト次第です。全体としてすべてを理解することをより困難にするシナリオの複雑さを追加するよりも、より単純なコードベースを好むでしょう。node / jsで私が気に入っていることの1つは、DI / IoC仕様に記述されたコードなしで、単純/クリーンなコードを作成し、テスト用に注入できることです。
トラッカー1

2

私は個人的に、そのようなヘルパークラスについての唯一の重要な部分は、それらがプライベートにされることだと思います。それに加えて、それらは厳密に良いアイデアです(わずかに適用されます)。

私がこれについて考える方法は-クラス(または関数)を実装する場合です-このような何かが有用であり、その実装をより明確にします、それはどのように悪いでしょうか?そして、そのようなプライベートヘルパークラスを定義して、特定の形状のデータに依存する他のアルゴリズムとの統合および使用を可能にすることがしばしば重要です。

それを「ヘルパー」とラベル付けするかどうかは個人的な好みの小さな問題ですが、実装に役立ち、より多くの聴衆に興味/使用がないことを暗示しています。それが理にかなっている場合-それのために行く!


1
公益事業のクラスはまだ役に立つかもしれません。主な例:java.lang.Math
アモン

1

static誤解に基づくヘルパークラスの普及。static「ユーティリティクラス」というメソッドだけでクラスを呼び出すからといって、POJOで一般的な動作を記述できないというわけではありません。

static ヘルパークラスは、次の3つの理由でアンチパターンです。

  1. このヘルパーメソッドへの静的アクセスにより、依存関係が隠されます。これらの「ユーティリティクラス」がPOJOである場合、依存クラスのすべてのユーザーに依存関係を明らかにするコンストラクタパラメータとして、それらを依存クラスに挿入できます

  2. このヘルパーメソッドへの静的アクセスにより、密結合が発生します。これは、ヘルパーメソッドを使用するコード再利用するのが難しく、(副作用として)テストするのが難しいこと意味します。

  3. 特に状態を維持する場合、これらは単にグローバル変数です。そして、うまくいけば、誰もグローバル変数が良いと主張しない...

静的ヘルパークラスは、STUPIDコードアンチパターンの一部です。


グローバル状態は質問とは無関係です– max630

OPは書きました:

しかし、ほとんどの場合、実際にはコンテキストに依存し、ステートフルです。

静的ステートフル構成体グローバルな状態です。

どんな種類のコードでも使用できます。– max630

はい、原因の。そして、ほとんどすべてのアプリケーションには、何らかのグローバル状態が必要です。

しかし、 グローバル状態 != グローバル変数

依存性注入を介してオブジェクト指向技術でグローバル状態を作成でき ますが、下部のグローバル変数であるステートフル静的構造ではグローバル状態を回避できません。


2
グローバル状態は質問とは無関係で、あらゆる種類のコードで使用できます。
max630

@ max630私のアップデートをご覧ください
ティモシー

1
カップリングなしで静的なフリー関数を使用できます。Func<Result, Args>他の場所でインスタンス化されるオブジェクトを渡します。
カレス

1
1)実装を隠すことは、適切な場合には一般的に良いことだと思います。2)すべてを引数/依存性注入として使用すると、一種の設計時結合も作成されます。長い依存性チェーンの署名の変更に多くの時間を費やさなければならないときに、これを感じることができます。また、Loggerを必要とするすべての関数、または他の分野横断的な懸念を引数として持つように...(しかし、はい、低レベルのテストを難しくする可能性があります)3)もちろん、静的関数はステートレスでなければなりません。
アレックス

1
@Alex次に、別の言い方をしましょう。ユニットとは、変更する理由が同じであるコードの束(静的に参照される「ユーティリティ」メソッドを含む)です。このユニット内のすべてが実装の詳細です。それ以外は依存関係であり、ユニットはそれに静的アクセスすることはできません。
ティモシートラックル
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.