web-dev-qa-db-ja.com

オブジェクト指向クラスの設計

オブジェクト指向の優れたクラス設計について疑問に思っていました。特に、これらのオプションを決定するのに苦労しています。

  1. 静的 vs インスタンスメソッド
  2. パラメータまたは戻り値のないメソッド vs パラメータおよび戻り値のあるメソッド
  3. 重複 vs 個別のメソッド機能
  4. プライベート vs パブリックメソッド

例1:

この実装では、戻り値やパラメータがなく、機能が重複していないインスタンスメソッドを使用し、すべてのメソッドをpublicにします。

XmlReader reader = new XmlReader(url);
reader.openUrl();
reader.readXml();
Document result = reader.getDocument();

例2:

この実装は、戻り値とパラメーターを持ち、機能とプライベートメソッドが重複している静的メソッドを使用します。

Document result = XmlReader.readXml(url); 

例1では、すべてのメソッドがパブリックインスタンスであるため、ユニットテストを簡単に行うことができます。すべてのメソッドは異なりますが、readXml()はopenUrl()に依存しているため、openUrl()を最初に呼び出す必要があります。すべてのデータはインスタンスフィールドで宣言されるため、コンストラクターとアクセサーを除いて、どのメソッドにも戻り値やパラメーターはありません。

例2では、​​1つのメソッドのみがpublicであり、残りはprivate staticであるため、単体テストが困難です。 readXml()がopenUrl()を呼び出すという点で、メソッドは重複しています。フィールドはなく、すべてのデータはメソッドのパラメーターとして渡され、結果はすぐに返されます。

適切なオブジェクト指向プログラミングを行うには、どの原則に従う必要がありますか?

12
siamii

例2はテストに非常に適していません...そして、内部をテストできないというわけではありません。オブジェクトがないため、XmlReaderオブジェクトをモックオブジェクトに置き換えることもできません。

例1は不必要に使いにくいものです。どうですか

XmlReader reader = new XmlReader(url);
Document result = reader.getDocument();

これは、静的メソッドよりも使用が難しくありません。

URLを開く、XMLを読み取る、バイトを文字列に変換する、解析する、ソケットを閉じるなど、興味深いことは何もありません。オブジェクトを作成して使用することは重要です。

つまり、適切なOO設計は、2つのことだけを公開することです(何らかの理由で本当に中間ステップが必要でない限り)。Static is evil。

11
maaartinus

正解は1つではなく、「適切なオブジェクト指向設計」の絶対的な定義もありません(一部の人々はそれを提供するかもしれませんが、素朴です...時間を与えてください)。

それはすべてあなたの目標に帰着します。

あなたは芸術家であり、紙は白紙です。繊細な鉛筆で描かれた白黒の側面の肖像画、または混合したネオンの巨大なガッシュを備えた抽象的な絵画を描くことができます。またはその間の何か。

では、あなたが解決しようとしている問題にどのような権利があるのでしょうか?クラスを使用してxmlを操作する必要がある人々の不満は何ですか?彼らの仕事の何が難しいですか?彼らはそれをどのような種類のコードで書こうとしているのかsurroundsライブラリへの呼び出し、およびそれらのフローを改善するためにどうすればよいですか?

彼らはもっと簡潔さを望みますか?彼らは、パラメータのデフォルト値を理解するのに非常に賢くなりたいので、彼らは多く(または何も)を指定する必要がなく、正しく推測しますか?ライブラリが必要とするセットアップとクリーンアップのタスクを自動化して、それらの手順を忘れないようにすることはできますか?あなたは彼らのために他に何ができますか?

地獄、おそらくあなたがする必要があるのは、それを4つか5つの異なる方法でコード化してから、消費者の帽子をかぶって5つすべてを使用するコードを書いて、どちらが気分が良いかを確認することです。ライブラリ全体に対してそれを実行できない場合は、サブセットに対して実行してください。そして、あなたはあなたのリストにいくつかの追加の選択肢を追加する必要もあります-流れるようなインターフェース、より機能的なアプローチ、または名前付きパラメーター、またはDynamicObjectに基づく何かについて、それらを助ける意味のある「疑似メソッド」を構成できるようにする必要がありますでる?

なぜ今jQueryが王様なのですか? Resigとチームはこのプロセスをたどったので、彼らが信じられないほどという構文の原則に出会うまで、domとイベントを処理するために必要なJSコードの量を減らしました。彼らが始めたとき、その統語原理は彼らまたは他の誰にも明らかではありませんでした。彼らはそれを見つけました。

プログラマーとして、それがあなたの最高の召しです。あなたはそれが見つかるまで暗闇の中で物事を試みて歩き回っています。そうすればわかるでしょう。そして、ユーザーに巨大な生産性の飛躍をもたらすでしょう。そして、それこそが(ソフトウェアレルムの)設計のすべてです。

6
Charlie Flowers

2番目のオプションは、人々にとって(たとえそれがあなただけの場合でも)使用するのが簡単であるため、より簡単です。

単体テストでは、内部をプライベートから保護に移動したい場合は、内部ではなくインターフェイスをテストします。

3
Tom

1。静的とインスタンス

OOデザインとそうでないものについては、非常に明確なガイドラインがあると思います。問題は、ブロゴスフィアによって、良いものと悪いもの、そして醜いものを区別することが難しくなることです。 some一種のリファレンスは、あなたが考えることができる最悪の練習でさえサポートします。

そして、私が考えることができる最悪の慣行は、あなたが言及した静力学とみんなのお気に入りのシングルトンを含むグローバル状態です。 Misko Heveryの 主題に関する古典的な記事 からの抜粋。

依存関係を本当に理解するには、開発者はコードのすべての行を読む必要があります。これにより、遠くで不気味なアクションが発生します。テストスイートを実行すると、1つのテストで変更されたグローバル状態により、後続のテストまたは並列テストが予期せず失敗する可能性があります。手動またはGuice依存性注入を使用して静的依存関係を解消します。

遠くでの不気味なアクションとは、(参照を渡さなかったため)隔離されていると私たちが確信している1つのことを実行したときですが、予期しない相互作用や状態の変化がシステムの離れた場所で発生し、オブジェクトについては伝えていません。これはグローバルな状態でのみ発生します。

以前はこのように考えていなかったかもしれませんが、静的状態を使用するときはいつでも、秘密の通信チャネルを作成していて、APIでそれらを明確にしていないのです。 Spooky Action at a Distanceは、開発者に潜在的な相互作用を理解するためにコードのすべての行を読み取らせ、開発者の生産性を低下させ、新しいチームメンバーを混乱させます。

つまり、何らかの状態が保存されているものへの静的参照を提供してはならないということです。私が静的を使用する唯一の場所は列挙された定数のためであり、それについてさえ不安があります。

2。入力パラメーターと戻り値を持つメソッドとなしを持つメソッド

あなたが理解する必要があるのは、入力パラメーターと出力パラメーターがないメソッドは、ある種の内部的に保存された状態で動作することがほぼ保証されているということです(そうでなければ、何をしているのですか?)。保存された状態を回避するという考えに基づいて構築された 言語全体 があります。

状態を保存すると必ず副作用が発生する可能性があるため、常に注意して使用するようにしてください。これは、入力および/または出力が定義された関数をpreferする必要があることを意味します。

実際、入力と出力が定義されている関数の方がテストがはるかに簡単です。ここで関数を実行して何が起こっているかを確認する必要はなく、どこかにプロパティを設定する必要はありません。テスト中の関数を実行する前に、それ以外の場合。

安全にこのタイプの関数をstaticとして使用することもできます。ただし、後でその関数の少し異なる実装をどこかに使用する場合、新しい実装で別のインスタンスを提供するのではなく、機能を置き換える方法がないので、私はそうしません。

3。重複と区別

質問が理解できません。 2つの重複する方法の利点は何ですか?

4。プライベートvsパブリック

公開する必要のないものは公開しないでください。しかし、私もプライベートの大ファンではありません。私はC#開発者ではなく、ActionScript開発者です。 2007年頃に書かれたAdobeのFlexフレームワークのコードに多くの時間を費やしてきました。そして、何を非公開にするかについて非常に悪い選択をしたため、クラスを拡張しようとする悪夢のようなものになりました。

したがって、2007年頃のアドビ開発者より優れたアーキテクトであると思わない限り(質問から、その主張をする機会が得られるまであと数年はかかると思います)、おそらくデフォルトで保護されたいと思うでしょう。 。


コード例にはいくつかの問題があり、それらは十分に設計されていないため、AまたはBを選択することはできません。

1つには、おそらく オブジェクトの作成とその使用を分離する を使用する必要があります。そのため、通常はnew XMLReader()が使用される場所のすぐ隣にありません。

また、@ djnaが言うように、XMLリーダーが使用するメソッドをカプセル化する必要があるため、API(インスタンスの例)は次のように簡略化できます。

_document Document = reader.read(info);

C#がどのように機能するかはわかりませんが、私はいくつかのWebテクノロジを扱ってきたので、常にXMLドキュメントをすぐに返すことができるとは思えません(多分、promiseや将来の型を除いて)オブジェクト)、しかし私はあなたにC#で非同期ロードを処理する方法についてアドバイスを与えることはできません。

このアプローチを使用すると、XMLオブジェクトを読み取り、どこに何を返すかを指示するパラメーターを取り、プロジェクトのニーズに基づいてそれらを交換できるいくつかの実装を作成できます。たとえば、データベース、ローカルストア、または元の例のようにURLから直接読み取る場合があります。静的メソッドを使用する場合は、これを行うことはできません。

3
Amy Blankenship

クライアントの視点に焦点を当てます。

IReader reader = new XmlReader.readXml(url);  // or injection, or factory or ...
Document document = reader.read();

静的メソッドは将来の進化を制限する傾向があります。クライアントは、おそらく多くの異なる実装によって提供されるインターフェースに関して機能しています。

オープン/読み取りイディオムの主な問題は、クライアントがメソッドを呼び出す順序を知っている必要があることです。単純な仕事をしたいだけの場合です。ここでは自明ですが、より大きなクラスでは、自明ではありません。

テストする主なメソッドはread()です。内部メソッドは、それらをパブリックにもプライベートにもせず、テストを同じパッケージに入れることによって、テストプログラムから見えるようにすることができます。テストは、リリースされたコードとは別に保つことができます。

2
djna

静的vsインスタンスメソッド

実際には、静的メソッドは一般にユーティリティクラスに限定されており、ドメインオブジェクト、マネージャー、コントローラー、DAOを乱雑にしてはいけません。静的メソッドは、必要なすべての参照をパラメーターとして合理的に渡すことができ、多くのクラスで再利用できるいくつかの機能を提供できる場合に最も役立ちます。インスタンスオブジェクトへの参照を作成するための回避策として静的メソッドを使用している場合は、代わりにその参照を持たない理由を自問してください。

パラメータまたは戻り値のないメソッドと、パラメータおよび戻り値のあるメソッド

メソッドにパラメーターが必要ない場合は、追加しないでください。戻り値についても同様です。これを念頭に置くと、コードが簡略化され、決して発生することのない多数のシナリオでコーディングすることがなくなります。

重複するメソッドと個別のメソッドの機能

機能の重複を避けることをお勧めします。難しい場合もありますが、ロジックの変更が必要な場合は、同様の機能を持つメソッド全体を変更するよりも、再利用される1つのメソッドを変更する方がはるかに簡単です。

プライベートメソッドとパブリックメソッド

通常、ゲッター、セッター、コンストラクターは公開する必要があります。他のクラスがそれを実行する必要がある場合がない限り、あなたがプライベートにしたい他のすべてのもの。メソッドをデフォルトでプライベートにしておくと、 カプセル化 を維持するのに役立ちます。同じことがフィールドにも当てはまり、デフォルトでプライベートに慣れます

2
Erich

質問にはお答えしませんが、あなたが使った言葉が問題だと思います。例えば。

XmlReader.read => twice "read"

XMLが必要だと思うので、テキストタイプから作成できるオブジェクトXMLを作成します(C#はわかりません... Javaこれは文字列と呼ばれます)。

class XML {
    XML(String text) { [...] }
}

あなたはそれをテストすることができ、それは明らかです。次に、ファクトリが必要な場合は、ファクトリメソッドを追加できます(2番目の例のように静的にすることもできます)。例えば。

class XML {
    XML(String text) { [...] }

    static XML fromUrl(url) { [...] }

}
1
sixro

いくつかの簡単なルールに従うことができます。ルールの理由を理解すると役立ちます。

静的vsインスタンスメソッド

メソッドの場合、意識的にこの決定を行う必要はありません。メソッドがフィールドメンバーを使用していないように見える場合(お気に入りのアナライザーがそのように指示しているはずです)、staticキーワードを追加する必要があります。

パラメーターまたは戻り値のないメソッドvsパラメーターと戻り値のあるメソッド

2番目のオプションはスコープの関係で優れています。常にスコープを狭く保つ必要があります。必要なものに手を差し伸べるのは悪いことです。入力が必要で、ローカルで作業して結果を返す必要があります。最初のオプションは、グローバル変数が通常悪いのと同じ理由で悪いです:それらはあなたのコードの一部だけに意味がありますが、それらはどこでも目に見え(そしてノイズ)、どこからでも改ざんされるかもしれません。これにより、ロジックの全体像を把握することが難しくなります。

重複するメソッドと異なるメソッドの機能

これは問題ではないと思います。他のメソッドを呼び出すメソッドは、これがタスクを細かく機能的なチャンクに分割する場合は問題ありません。

プライベートメソッドとパブリックメソッド

公開する必要がない限り、すべてを非公開にします。あなたのクラスのユーザーは騒音なしで行うことができます、彼は彼にとって重要なものだけを見たいと思っています。

0
Martin Maat