ユーティリティクラスは悪ですか?[閉まっている]


96

私はこのスレッドを見ました

「Utilities」クラスが邪悪な場合、どこに汎用コードを配置すればよいですか?

そして、なぜユーティリティクラスは邪悪なのでしょうか?

何十ものクラスのドメインモデルがあるとします。インスタンスをxml化できるようにする必要があります。親でtoXmlメソッドを作成しますか?MyDomainXmlUtility.toXmlヘルパークラスを作成しますか?これは、ビジネスニーズがドメインモデル全体に​​またがる場合です-インスタンスメソッドとして本当に属していますか?アプリケーションのxml機能に多数の補助メソッドがある場合はどうでしょうか?


27
「悪」という言葉の切り下げは悪です!
マシュー・ロック

1
@matthew私の質問の基になった投稿の条件を保存しました...;)
hvgotcodes

ユーティリティクラスは、シングルトンと同じ理由で悪い考えです。
CurtainDog 2010

1
toXMLメソッドを使用するかどうかについての議論は、リッチドメインモデルと貧血ドメインモデルに重点を置いたものです。codeflow.blogspot.com/2007/05/anemic-vs-rich-domain-models.html
James P.

@ james、toXMLは単なる例です...至る所で使用されているいくつかの正規表現機能はどうですか?同様に、ドメインモデル全体で文字列を使用して何かを行う必要がありますが、1つのスーパークラス(Java)を使用する別のオーバーライドの懸念があるため、サブクラス化を使用できません
でした

回答:


128

ユーティリティクラスは厳密に悪いわけではありませんが、優れたオブジェクト指向設計を構成する原則に違反する可能性があります。優れたオブジェクト指向設計では、ほとんどのクラスは単一のものとそのすべての属性と操作を表す必要があります。モノを操作している場合、そのメソッドはおそらくそのモノのメンバーである必要があります。

ただし、ユーティリティクラスを使用していくつかのメソッドをグループ化できる場合があります。たとえばjava.util.Collections、Javaコレクションで使用できるいくつかのユーティリティを提供するクラスです。これらは特定の種類のコレクションに固有のものではなく、任意のコレクションで使用できるアルゴリズムを実装しています。

本当に、あなたがする必要があるのはあなたのデザインについて考え、それがメソッドを置くのに最も意味がある場所を決定することです。通常、これはクラス内の操作です。ただし、場合によっては、実際にユーティリティクラスとして使用されることもあります。ただし、ユーティリティクラスを使用する場合は、ランダムなメソッドを投げるだけでなく、目的と機能ごとにメソッドを整理します。


SOLIDオブジェクト指向の原則に対するユーティリティ/ヘルパークラスをチェックするだけでなくについては、この記事にうまくつながりreadability.com/articles/rk1vvcqy
melaos

8
言語がクラス以外の名前空間メカニズムを提供していない場合は、名前空間が行う場所でクラスを悪用するしかありません。C ++では、他の関数とは無関係の独立した関数を名前空間に入れることができます。Javaでは、静的(クラス)メンバーとしてクラスに配置する必要があります。
Unslander Monica

1
メソッドとしてのグループ化は、OOPの観点からは標準的な場合があります。ただし、OOPが解決策になることはめったにありません(例外として、継承によるコード再利用の信じられないほどのセマンティクスが実際に適合するインスタンスの1つとして、高レベルのアーキテクチャとウィジェットツールキットがあります)。通常、メソッドは結合と依存関係を増やします。簡単な例:プリンター/スキャナーメソッドをメソッドとしてクラスに提供する場合、クラスをプリンター/スキャナーライブラリに結合します。さらに、可能な実装の数を効果的に1つに修正します。インターフェースを投入して依存関係をさらに増加させない限り。
ジョーだから、

一方、「目的と機能によるグループ化」というあなたの意見には同意します。プリンター/スキャナーの例をもう一度見てみましょう。共通の目的のために設計された一連のコンサートクラスから始めましょう。いくつかのデバッグコードを記述し、クラスのテキスト表現を設計することができます。デバッグプリンターはこれらすべてのクラスとプリンターライブラリに依存する単一のファイルに実装できます。デバッグ以外のコードは、この実装の依存関係の負担にはなりません。
ジョーだから、

1
UtilクラスとHelperクラスは、このような制約の不幸な成果物であり、OOPを理解していないか問題ドメインを理解していない人は、手続き型コードを書き続けてきました。
ビレロビッチ2017

57

一般的なコンセンサスは、ユーティリティクラス自体は悪ではないということです。あなたはそれらを慎重に使う必要があります:

  • 静的ユーティリティメソッドを一般的で再利用できるように設計します。それらがステートレスであることを確認してください。つまり、静的変数はありません。

  • ユーティリティメソッドが多数ある場合は、開発者が見つけやすいようにクラスに分割します。

  • ドメインクラスの静的メソッドまたはインスタンスメソッドの方が適切なソリューションである場合は、ユーティリティクラスを使用しないでください。たとえば、抽象基本クラスまたはインスタンス化可能なヘルパークラスのメソッドがより良いソリューションであるかどうかを検討します。

  • Java 8以降では、インターフェイスの「デフォルトメソッド」がユーティリティクラスよりも優れたオプションである場合があります。(たとえば、Java 8のデフォルトメソッドと抽象クラスのインターフェイスを参照してください。)


この質問を見るもう1つの方法は、引用された質問で、「ユーティリティクラスが「悪」である場合」がストローマンの議論であることを観察することです。それは私の質問のようです:

「豚が飛べるなら傘を持ってもいいですか?」

上記の質問では、私は実際には豚が飛ぶことができることを言っていないです...または私は、彼らがいることを命題に同意することができ飛びます。

典型的な「xyz is evil」の発言は、極端な視点でポーズをとって考えさせる修辞的な装置です。それらはめったに(もしあれば)文字通りの事実の陳述として意図されていません。


16

ユーティリティクラスは、それらをサポートするデータで責任をグループ化できないため、問題があります。

ただし、これらは非常に有用であり、永続的な構造として、またはより完全なリファクタリングの際の踏み台として、常に作成しています。

よりクリーンなコード視点ユーティリティクラスは、単一の責任とオープン・クローズの原則に違反します。それらには変更する理由がたくさんあり、設計上拡張可能ではありません。これらは、中間リフティングとしてリファクタリング中にのみ存在する必要があります。


2
たとえば、Javaではクラスのメソッドではない関数を作成できないため、これを実用的な問題と呼びます。そのような言語では、「変数がなく、静的メソッドのみを持つクラス」は、ユーティリティ関数の名前空間を表すイディオムです... Javaでこのようなクラスに直面したとき、クラスについて言えばIMHOは無効で無意味です、classキーワードが使用されている場合でも。それは名前空間のように
鳴り響き

2
率直に言って申し訳ありませんが、「ステートレスな静的メソッド[...] OOPシステムにおける奇妙な問題[...]哲学的問題」は、非常に誤った見方です。正しいコードを簡単に記述できるようになるため、状態を回避できればすばらしいことです。私が書かなければならないときに壊れてx.equals(y)1ので、私は置くことを意図したことはありません))、これは本当に伝えていない(と私は実装を知らないことに頼ることはできません一切の状態を変更するべきではないという事実)2 x」にy3)と比較した場合の「優先」または「実行」の位置。「の同一性」を検討したくない、xまたはy単にその値に関心があるだけ。
Jo So

4
実際、現実世界のほとんどの機能は、静的でステートレスな数学関数として最もよく表現されています。Math.PIは変更する必要があるオブジェクトですか?数値のサインを取得するためだけに、IAbstractRealOperationを実装するAbstractSineCalculatorをインスタンス化する必要がありますか?
Jo So

1
@JoSo私は無国籍と直接計算の価値について完全に同意します。ナイーブOOはこれに哲学的に反対しており、それが深刻な欠陥になっていると思います。それは(あなたが示したように)それを必要としないものの抽象化を奨励し、実際の計算に意味のある抽象化を提供することに失敗します。ただし、オブジェクト指向言語を使用するバランスのとれたアプローチは、モジュール性と保守性を促進するため、不変性とステートレスになる傾向があります。
Alain O'Dea

2
@ AlainO'Dea:私はあなたのコメントをステートレスな機能に対抗するものとして誤解していることに気づきました。私は自分の口調について謝罪します-私は現在、私の最初のJavaプロジェクト(10年間のプログラミングの大部分でOOPを避けた後)に関与しており、不明確な状態と意味を持つ抽象的なものの層の下に埋もれています。そして、私はいくつかの新鮮な空気が必要です:-)。
Jo So

8

私はそれが邪悪になり始めると思います

1)大きすぎます(この場合は、意味のあるカテゴリにグループ化してください)。
2)静的メソッドであってはならないメソッドが存在する

しかし、これらの条件が満たされない限り、それらは非常に役立つと思います。


「静的メソッドであってはならないメソッドが存在します。」これはどのようにして可能ですか?
Koray Tugay

@KorayTugay非静的Widget.compareTo(Widget widget)と静的を比較してくださいWidgetUtils.compare(Widget widgetOne, Widget widgetTwo)。比較は静的に行うべきではないものの例です。
ndm13 2017年

5

経験則

この問題は、次の2つの観点から見ることができます。

  • 全体的な*Util方法は、多くの場合、不適切なコード設計または遅延命名規則の提案です。
  • これは、再利用可能なクロスドメインステートレス機能の正当な設計ソリューションです。ほとんどすべての一般的な問題について、既存の解決策があることに注意してください。

例1. utilクラス/モジュールの正しい使用法。外部ライブラリの例

ローンやクレジットカードを管理するアプリケーションを作成しているとしましょう。これらのモジュールからのデータは、Webサービスを通じてjson形式で公開されます。理論的には、手動でオブジェクトをに含まれる文字列に変換できますがjson、これはホイールを再発明します。正しい解決策は、Javaオブジェクトを目的の形式に変換するために使用される外部ライブラリを両方のモジュールに含めることです。(例の画像ではgsonを示しています

ここに画像の説明を入力してください


例2. utilクラス/モジュールの正しい使用法。util他のチームメンバーに言い訳なしで独自に書く

ユースケースとして、2つのアプリケーションモジュールでいくつかの計算を実行する必要があると仮定しますが、どちらもポーランドに祝日があることを知る必要があります。理論的にはモジュール内でこれらの計算を行うことができますが、この機能を個別のモジュールに抽出する方が良いでしょう。

これは小さいですが、重要な詳細です。あなたが書いたクラス/モジュールはとは呼ばれずHolidayUtil、と呼ばれますPolishHolidayCalculator。機能的にはutilクラスですが、一般的な言葉は避けました。

ここに画像の説明を入力してください


1
私が見るyEdファン;)素晴らしいアプリ。
Sridhar Sarnobat

3

ユーティリティクラスは、クラスのより良い名前を考えるのが面倒なので、悪いのです:)

そうは言っても、私は怠惰です。時々、あなたは仕事を成し遂げる必要があるだけで、あなたの心は空っぽです。


3

ここでこの質問を振り返ると、C#拡張メソッドはユーティリティクラスの必要性を完全に破壊すると思います。しかし、すべての言語にそのような天才的な構成があるわけではありません。

また、JavaScriptがあり、新しい関数を既存のオブジェクトに直接追加できます。

しかし、C ++のような古い言語でこの問題を解決するエレガントな方法が本当にあるかどうかはわかりません...

Good OOを書くのはちょっと難しいし、見つけるのも難しいです。GoodOOを書くには、適切な機能コードを書くよりも多くの時間と知識が必要です。

そして、予算が限られているとき、上司はあなたがたくさんのクラスを書いて一日中過ごしたことをいつも喜んでいるわけではありません...


3

ユーティリティクラスが悪であることに完全に同意しません。

ユーティリティクラスは、いくつかの点でOOプリンシパルに違反する可能性がありますが、必ずしも悪いわけではありません。

たとえば、valueに一致するすべての部分文字列の文字列をクリーンアップする関数が必要だとしますx

stl c ++(現在)はこれを直接サポートしていません。

のポリモーフィック拡張を作成できますstd::string

しかし問題は、プロジェクトで使用するすべての文字列を拡張文字列クラスにしたいですか?

OOが実際に意味をなさない場合がありますが、これはその1つです。私たちはプログラムを他のプログラムと互換性があるようにしたいのでstd::string、クラスStringUtil_(または何か)に固執して作成します。

クラスごとに1つのユーティリティを使用するのがベストだと思います。すべてのクラスに1つのユーティリティを使用するか、1つのクラスに多くのユーティリティを使用するのはばかげていると思います。


2

デザイナーがコードを配置する適切な場所を考えることができなかったという理由だけで、ユーティリティを何かにブランド化するのは非常に簡単です。多くの場合、真の「ユーティリティ」はほとんどありません。

経験則として、私は通常、コードが最初に使用されるパッケージにコードを保持し、後で他の場所で本当に必要であることがわかった場合にのみ、より一般的な場所にリファクタリングします。唯一の例外は、同様の機能や関連する機能を実行するパッケージがすでにあり、コードがそこに最も適している場合です。


2

ステートレスな静的メソッドを含むユーティリティクラスが役立ちます。これらは多くの場合、単体テストが非常に簡単です。



1

ほとんどのUtilクラスは、次の理由で不良です。

  1. それらはメソッドの範囲を広げます。彼らはそうでなければプライベートになるコードを公開します。utilメソッドが別々のクラスの複数の呼び出し元で必要であり、安定している(つまり、更新する必要がない)場合は、プライベートヘルパーメソッドを呼び出し元のクラスにコピーして貼り付ける方が良いと思います。APIとして公開すると、jarコンポーネントへのパブリックエントリポイントが何であるかを理解するのが困難になります(メソッドごとに1つの親を持つ階層構造と呼ばれるツリー構造を維持します。複数の親メソッドから呼び出されるメソッドがあります)。
  2. それらはデッドコードになります。アプリの進化に伴い、Utilメソッドは時間の経過とともに使用されなくなり、コードベースを汚染する未使用のコードになってしまいます。プライベートのままだった場合、コンパイラはメソッドが使用されていないことを通知し、削除するだけです(最適なコードはコードがまったくないことです)。このようなメソッドを非プライベートにすると、コンピューターは無力になり、未使用のコードを削除するのに役立ちます。すべてのコンピュータが知っているため、別のjarファイルから呼び出すことができます。

静的ライブラリと動的ライブラリにはいくつかの類似点があります。


0

クラスにメソッドを追加できない場合(たとえば、Accountジュニア開発者による変更に対してロックされている場合)は、次のように、いくつかの静的メソッドをUtilitiesクラスに追加します。

public static int method01_Account(Object o, String... args) {
    Account acc = (Account)o;
    ...
    return acc.getInt();
}  

1
あなたは私にとってJrの1人のように見えAccountます。またはより良いのは、ロックしないソース管理システムです。
2013年

1
私は彼Accountがジュニア開発者がそれを変更できないようにファイルがロックされたことを意味したと思います。ジュニア開発者として、それを回避するために、彼は上記のようにしました。そうは言っても、ファイルへの書き込みアクセス権を取得して適切に実行する方が良いでしょう。
Doc

この回答に+1を追加する理由は、この回答を提供した理由の完全なコンテキストを誰も持っていないときに反対票が厳しいように見えるためです
joelc

0

ユーティリティクラスは常に悪であるとは限りません。ただし、それらには、幅広い機能に共通のメソッドのみを含める必要があります。限られた数のクラスでしか使用できないメソッドがある場合は、共通の親として抽象クラスを作成し、その中にメソッドを配置することを検討してください。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.