「Util」クラスを持つことは懸念の原因ですか?[閉まっている]


14

私は時々、実際には他の場所に属していないように見えるメソッドと値を保持するのに役立つ「Util」クラスを作成します。しかし、これらのクラスの1つを作成するたびに、「あー、これを後悔するつもりだ...」と思うのは、どこかで悪いことを読んだからです。

しかし、他方では、2つの説得力のある(少なくとも私にとって)ケースがあるようです。

  1. パッケージ内の複数のクラスで使用される実装の秘密
  2. インターフェイスを乱雑にすることなく、クラスを増強するための便利な機能を提供します

私は破壊に向かっていますか?あなたが言うこと !!リファクタリングする必要がありますか?


11
Utilクラス名での使用をやめてください。問題が解決しました。
ヤニス

3
@MattFenwick Yannisには良い点があります。クラスの命名SomethingUtilは少々怠け者であり、クラスの真の目的を曖昧にするだけです- SomethingManagerまたはという名前のクラスと同じSomethingServiceです。そのクラスに単一の責任がある場合、意味のある名前を簡単に付ける必要があります。そうでない場合、それは対処する本当の問題です
...-MattDavey

1
@MDavey良い点、それはおそらく悪い言葉の選択でしたがUtil、明らかにそれが

1
操作するオブジェクトの種類で区切られた複数の* Utilクラスを作成する場合は、問題ないと思います。StringUtil、ListUtilなどを使用しても問題はありません。C#を使用している場合を除き、拡張メソッドを使用する必要があります。
marco-fiset

4
多くの場合、関連する目的で使用される関数のセットがあります。彼らは本当にクラスにうまくマッピングしていません。ImageResizerクラスは必要ありません。ResizeImage関数が必要です。そこで、関連する関数をImageToolsのような静的クラスに入れます。まだグループ化されていない関数の場合、それらを保持するUtil静的クラスがありますが、1つのクラスに収まるほど相互に関連する少数の関数ができたら、それらを移動します。これを処理するためのより良いオブジェクト指向の方法は見当たりません。
フィリップ

回答:


26

現代のオブジェクト指向デザインは、すべてがオブジェクトではないことを受け入れています。いくつかのことは行動、または式であり、それらのいくつかは状態を持っていません。これらのことを純粋な関数としてモデル化して、その設計の利点を得るのは良いことです。

JavaとC#(およびその他)では、utilクラスを作成し、そのフープをジャンプして実行する必要があります。迷惑ですが、世界の終わりではありません。設計の観点からはそれほど面倒ではありません。


ええ、それは私が考えていたようなものです。回答ありがとうございます!

同意しましたが、.NETは現在、このアプローチの代替として拡張メソッドと部分クラスを提供していることに言及する必要があります。この項目を極端にすると、ユーティリティクラスをほとんど必要としない言語であるRubyミックスインになります。
-neontapir

2
@neontapir-拡張メソッドの実装を除き、基本的に拡張メソッドを使用してutilクラスを作成することを強制します
...-Telastyn

本当です。Utilクラスが妨害する場所は2つあります。1つはクラス自体にあり、もう1つはコールサイトです。メソッドを拡張オブジェクトに存在するように見せることにより、拡張メソッドはコールサイトの問題に対処します。私は同意します、Utilクラスの存在の問題はまだあります。
-neontapir

16

絶対とは絶対言うな"

私はそれが必ずしも悪いとは思わない、あなたがそれをひどくし、それを乱用した場合にのみ悪い。

ツールとユーティリティが必要

まず最初に、私たちは皆、ほとんどどこにでもあると思われるライブラリを使用しています。たとえば、Javaの世界では、Google GuavaまたはApache Commonsの一部(Apache Commons LangApache Commons Collectionsなど)。

したがって、明らかにこれらが必要です。

ハードワード、重複、バグの導入を避ける

あなたはこれらを考えるならば、かなりこれらのちょうど非常に大きな集まりですUtil誰かが彼らの(比較的)が権利を取得するために偉大な長さを経て、彼らがしてきた以外、あなたが記述するクラスの時間 - テストと大きく目を球状に他人による。

だから、Utilクラスを書くことを悩むときの最初の経験則は、そのUtilクラスが実際に存在しないことを確認することだと思います。

私が見た唯一の反論は、依存関係を制限したいときです:

  • 依存関係のメモリフットプリントを制限したい場合、
  • または、開発者が使用できるものを厳密に制御したい場合(強迫観念の大規模なチームで発生する場合、または特定のフレームワークがどこかで絶対に回避するための奇妙な超クラッピークラスを持つことが知られている場合)。

ただし、ProGuardまたは同等のものを使用してライブラリを再パッケージ化するか、自分で分解することで、これらの両方に対処できます(Mavenユーザーの場合、maven-shade-pluginは、ビルドの一部としてこれを統合するためのフィルターパターンを提供します)。

そのため、ライブラリ内にあり、ユースケースと一致し、それ以外のベンチマークが示されていない場合は、それを使用します。それがあなたのものと少し異なる場合は、それを(可能であれば)拡張するか、拡張するか、最後の手段で書き直してください。

命名規則

しかし、これまでのところ、この回答では、私はそれらをUtilあなたのように呼んでいます。それらに名前を付けないでください。

意味のある名前を付けてください。Google Guavaを(非常に)非常に良い例として取り上げて、com.google.guava名前空間が実際にあなたのutilルートであると想像してください。

util最悪の場合はパッケージを呼び出しますが、クラスは呼び出しません。Stringオブジェクトと文字列構造の操作を扱う場合は、それを呼び出さStringsないでくださいStringUtils(申し訳ありませんが、Apache Commons Lang-私は今でも好きで、使っています!)。特定の処理を行う場合は、特定のクラス名(SplitterまたはなどJoiner)を選択します。

単体テスト

これらのユーティリティの作成に頼る必要がある場合は、必ずユニットテストを行ってください。ユーティリティーの良いところは、通常、自己完結型のコンポーネントであり、特定の入力を受け取り、特定の出力を返すことです。それがコンセプトです。そのため、それらを単体テストしない理由はありません。

また、単体テストでは、APIのコントラクトを定義および文書化できます。テストが壊れた場合、何かを間違った方法で変更したか、APIのコントラクトを変更しようとしていることを意味します(または、元のテストはがらくたでした-そこから学び、二度としないでください)

APIデザイン

これらのAPIについての設計上の決定は、おそらくあなたに長い間続くでしょう。そのため、Splitter-cloneの作成に何時間も費やさずに、問題へのアプローチに注意してください。

いくつか質問をしてください:

  • あなたのユーティリティメソッドは、クラスをそれ自体で保証しますか、それとも同様に有用なメソッドのグループの一部であることが理にかなっている場合、静的メソッドで十分ですか?
  • オブジェクトを作成し、APIをより読みやすくするためにファクトリメソッドが必要ですか?
  • 読みやすさと言えば、Fluent APIBuilderなどが必要ですか?

これらのユーティリティは、幅広いユースケースをカバーし、堅牢で安定し、十分に文書化され、最小限の驚きの原則に従って、自己完結型であることが必要です。理想的には、utilsの各サブパッケージ、または少なくともutilパッケージ全体をバンドルにエクスポートして、簡単に再利用できるようにする必要があります。

いつものように、ここで巨人から学びます:

はい、これらの多くはコレクションとデータ構造に重点を置いていますが、ほとんどまたはすべてのユーティリティを直接または間接的に実装する可能性が高いとは言いません。


+1If deals with String objects and manipulation of string constructs, call it Strings, not StringUtils
TulainsCórdova12年

+1 .NetのAPIデザインについては、.Netの設計者による本を読んでください。フレームワーク設計ガイドライン:再利用可能な.NETライブラリの規則、イディオム、パターン
MarkJ

StringUtil(s)ではなくStringsと呼ぶのはなぜですか?私にとっての文字列は、コレクションオブジェクトのように聞こえます。
bdrx 14

@bdrx:私にとってはStringsCollectionTypeHere、具体的な実装が必要な場合、コレクションオブジェクトはどちらかになります。または、これらの文字列がアプリのコンテキストで特定の意味を持つ場合、より具体的な名前。この特定のケースでは、Commons Lang StringsStringUtilsは対照的に、Guavaは文字列関連のヘルパーに使用します。クラスがStringオブジェクトを処理するか、文字列を管理する汎用クラスであることを意味します。
ヘイレム14

@bdrx:一般的NameUtilsには、明確にラベル付けされたパッケージ名の下にある場合、ユーティリティクラスであることを既に知っているので、終わりがありません(そうでない場合は、APIを調べるとすぐにわかります)。、、またはのようなものを宣言する人と同じくらいSomethingInterface、私にとってはうっとうしいものです。IDEを使用せずにCでコーディングする場合、このようなアーティファクトには大丈夫でした。今日、私は一般的にそのようなことの必要はありません。ISomethingSomethingImpl
ヘイレム14

8

Utilクラスは凝集性がなく、一般に、クラスは変更する単一の理由(単一責任原則)を持たなければならないため、設計が悪いです。

しかし、私のような、非常にJava APIのクラス「UTIL」を見てきました:MathCollectionsArrays

実際、これらはユーティリティクラスですが、それらのメソッドはすべて単一のテーマに関連しています。1つは数学演算、1つはコレクションを操作するメソッド、もう1つは配列を操作するメソッドがあります。

ユーティリティクラスにまったく関係のないメソッドがないようにしてください。その場合、実際に所属する場所以外に配置することもできます。

utilクラスが必要な場合は、Java やのようなテーマで区切るようMathCollectionsしてくださいArrays。少なくとも、名前空間にすぎない場合でも、設計の意図を示しています。

私は、ユーティリティクラスを常に避け、作成する必要ありませんでした。


回答ありがとうございます。それで、utilクラスがパッケージプライベートであるとしても、それらはまだデザインの匂いです?

ただし、すべてがクラスに属するわけではありません。あなたがそれを主張する言語で立ち往生している場合、utilsクラスが発生します。
jk。

1
まあ、ユーティリティクラスには、最初にまとまりがないため、メソッドの数が多すぎることを知らせる設計原則がありません。停止する方法をどのように知ることができますか?30メソッド後?40?100?一方、OOPの原則は、メソッドが特定のクラスに属していない場合と、クラスがあまりにも多くのことを行っている場合の概念を示します。それは凝集性と呼ばれます。しかし、私の答えでお話ししたように、Javaを設計した人々はユーティリティクラスを使用していました。あなたもできません。私はユーティリティクラスを作成しません。また、通常のクラスに属しないものがある状況にはありません。
Tulainsコルドバ

1
Utilsクラスは通常、単なる純粋な関数の束を含む単なる名前空間であるクラスではありません。純粋な関数は、あなたが得ることができるほどまとまりがあります。
jk。

1
@jk私はUtilsを意味していました...ちなみに、少なくともいくつかのデザインの意図のようなMathまたは名前を思い付くArrays
Tulainsコルドバ

3

utilクラスを使用することは完全に許容できますが、という用語を好みClassNameHelperます。.NET BCLにはヘルパークラスも含まれています。覚えておくべき最も重要なことは、クラスの目的と個々のヘルパーメソッドを徹底的に文書化し、それを高品質で保守可能なコードにすることです。

また、ヘルパークラスに夢中にならないでください。


0

私は2段階のアプローチを使用します。「util」パッケージ(フォルダ)内の「Globals」クラス。何かが「Globals」クラスまたは「util」パッケージに入るには、次の条件が必要です。

  1. シンプル、
  2. 変わらない、
  3. アプリケーションの他の要素に依存しない

これらのテストに合格する例:

  • システム全体の日付および10進形式
  • EARLIEST_YEAR(システムがサポートする)やIS_TEST_MODEなどの不変のグローバルデータ、または真にグローバルで変化しない(システムの実行中)値。
  • 言語、フレームワーク、またはツールキットのギャップを回避する非常に小さなヘルパーメソッド

以下は、アプリケーションの他の部分から完全に独立した非常に小さなヘルパーメソッドの例です。

public static String ordinal(final int i) {
    return (i == 1) ? "1st" :
           (i == 2) ? "2nd" :
           (i == 3) ? "3rd" :
           new StringBuilder().append(i).append("th").toString();
}

これを見ると、21が「21番」、22が「22番」などであるというバグがありますが、それは重要なことです。

これらのヘルパーメソッドの1つが大きくなるか複雑になる場合は、utilパッケージ内の独自のクラスに移動する必要があります。utilパッケージ内の2つ以上のヘルパークラスが相互に関連している場合、それらを独自のパッケージに移動する必要があります。定数またはヘルパーメソッドがアプリケーションの特定の部分に関連していることが判明した場合は、そこに移動する必要があります。

Globalsクラスまたはutilパッケージにこだわるすべてのものに適した場所がない理由を正当化でき、上記のテストを使用して定期的にクリーニングする必要があります。それ以外の場合は、混乱を作成しているだけです。

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