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


12

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

  1. 静的インスタンスメソッド
  2. パラメータまたは戻り値のないメソッド vs パラメータおよび戻り値のあるメソッド
  3. オーバーラップするの異なる方法で機能
  4. 民間公共方法

例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つのメソッドのみがパブリックであり、残りはプライベートスタティックであるため、ユニットテストが困難です。readXml()がopenUrl()を呼び出すという点で、メソッドは重複しています。フィールドはありません。すべてのデータはメソッドのパラメーターとして渡され、結果はすぐに返されます。

適切なオブジェクト指向プログラミングを行うには、どの原則に従うべきですか?


2
マルチスレッドを行う場合、静的なことは悪いです。先日、XMLWriter.write(data、fileurl)のような静的なXMLWriterがありました。ただし、プライベート静的FileStreamがあるため、複数のスレッドから同時にこのクラスを使用すると、2番目のスレッドが最初のスレッドFileStreamを上書きし、見つけにくいエラーが発生しました。静的メンバーとマルチスレッドを使用した静的クラスは、災害のレシピです。
アレキサンダーソン

1
@Paxinum。説明する問題は、「静的な」問題ではなく、状態の問題です。非静的メンバーでシングルトンを使用した場合、マルチスレッドでも同じ問題が発生します。
mike30

1
@Per Alexandersson静的メソッドは、同時実行性に関しては悪くありません。静的状態が悪い。これが、すべてのメソッドが静的である関数型プログラミングが並行状況で非常にうまく機能する理由です。
玉里ボナー

回答:


10

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

例1は使いにくいです。どう?

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

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

URLを開いたり、XMLを読み込んだり、バイトを文字列に変換したり、解析したり、ソケットを閉じたりするなどのことは面白くない。オブジェクトを作成して使用することが重要です。

したがって、適切なオブジェクト指向デザインのIMHOは、2つのものだけを公開することです(何らかの理由で中間ステップが本当に必要でない限り)。静的は悪です。


-1。実際には、XmlReaderをモックオブジェクトに置き換えることができます。脳死のオープンソースのmoickフレームワークではなく、優れた工業グレードのものがあります;)開発者あたり数百ドルの費用がかかりますが、公開するAPIで密封された機能をテストするのは驚異的です。
トムトム

2
TomTomのセールスピッチに甘んじていないため+1。ライナーが1つ欲しいときは、Document result = new XmlReader(url).getDocument();「なぜ?」それにアップグレードしてDocument result = whateverReader.getDocument();whateverReader他の何かによって私に渡されるようにします。
candied_orange

6

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

すべては目標にかかっています。

あなたは芸術家であり、紙は空白です。繊細で細かく鉛筆で描かれた黒と白の側面の肖像画、または混合ネオンの巨大な切り傷のある抽象絵画を描くことができます。またはその間にあるもの。

それで、あなたが解決しようとしている問題に対して何が正しいのでしょうか?クラスを使用してxmlを操作する必要がある人々の苦情は何ですか?彼らの仕事の何が難しいですか?ライブラリへの呼び出しを取り巻く、どのような種類のコードを記述しようとしていますか?また、どのようにしてそれらのフローをより良くすることができますか?

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

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

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

プログラマーとして、それが最高の呼びかけです。あなたはそれを見つけるまで暗闇の中でしようとするものを手探りします。あなたがするとき、あなたは知っているでしょう。そして、あなたはあなたのユーザーを与えることでしょう巨大な生産性の飛躍を。そして、それがデザイン(ソフトウェア領域)のすべてです。


1
これは、ベストプラクティスとそれが「感じる」以外のその他のプラクティスの間に違いがないことを意味します。これは、多くの開発者にとって、クラスの境界を越えて手を伸ばすなど、維持不可能な泥の塊で終わる方法です。
エイミーブランケンシップ

@Amy Blankenship、OPが求めている選択をするための「最良の方法」は誰もいないと断言できません。それは100万ものものに依存し、100万の自由度があります。ただし、「ベストプラクティス」の場所はあると思います。それは特定の選択がすでに行われいるチーム環境にあり、チームの他のメンバーがそれらの以前の選択と一貫性を保つ必要があります。言い換えれば、特定の状況では、特定のものに「ベストプラクティス」というラベルを付ける理由があるかもしれません。しかし、OPはコンテキストを与えていません。彼は...何かを構築している
チャーリー花

...彼はこれらすべての可能な選択肢に直面しています。これらの選択に対する「正しい答え」はありません。これは、システムの目標と問題点によって推進されます。Haskellプログラマーは、すべてのメソッドをインスタンスメソッドにすべきだとは思わないことを保証します。そして、Linuxカーネルプログラマーは、TDDにアクセスできるようにすることは非常に重要だとは考えていません。また、C ++ゲームプログラマーは、多くの場合、すべてをオブジェクトにカプセル化するよりも、メモリ内のタイトなデータ構造にデータをバンドルします。すべての「ベストプラクティス」は、特定のコンテキストでは「ベストプラクティス」であり、他のコンテキストではアンチパターンです。
チャーリーフラワーズ

@AmyBlankenshipもう1つ、クラスの境界を越えて手を差し伸べるのは「おかしくなりそうな気分だ」とは思わない。それは維持不可能な泥の玉につながり、恐ろしいと感じます。あなたは、一部の労働者がだらしない/やる気がない/非常に経験が浅いという問題を解決しようとしていると思います。その場合、慎重で意欲的で経験豊富な人が重要な選択を行い、それらを「ベストプラクティス」と呼びます。しかし、それらの「ベストプラクティス」を選択する人は、「正しいと感じるもの」に基づいて選択を行っており、正しい答えはありません。が選択するか制御ているだけです。
チャーリーフラワーズ

私は自分自身を上級レベルと考えている複数のプログラマーと仕事をしており、経営陣は静的とシングルトンが通信の問題を処理する絶対的な正しい方法であると固く信じていました。この質問の静的な部分は、そのようなクラスの境界を越えて手を差し伸べるのが開発者にとって間違っているかどうか、また静的な代替案を提唱する答えが賛成票を受け取るかどうかは問われませんでした。
エイミーブランケンシップ

3

2番目のオプションの方が(ユーザーだけである場合でも)人々が使用するのがより簡単で、はるかに簡単です。

単体テストの場合、内部をプライベートから保護に移動する必要がある場合は、内部ではなくインターフェイスをテストするだけです。


2
テストケースが同じパッケージ内にある場合、ユニットテストの目的で、メソッドパッケージをプライベート(デフォルト)にすることもできます。

良い点-それは良いです。

2

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

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

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

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

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


テストスイートが別のプロジェクトにある場合、デフォルトの可視性を持つメソッドは引き続き表示されますか?
siamii

Javaはプロジェクトについて知りません。プロジェクトはIDEコンストラクトです。コンパイラーとJVMは、テストされたクラスとテスタークラスが含まれるパッケージを調べます-同じパッケージ、デフォルトの可視性が許可されています。Eclipseでは、2つの異なるソースディレクトリを持つ単一のプロジェクトを使用します。私は2つのプロジェクトを試しましたが、うまくいきました。

2

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

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

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

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

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

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

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

一般に、ゲッター、セッター、およびコンストラクターはパブリックである必要があります。他のクラスがそれを実行する必要がある場合を除いて、他のすべてはあなたがプライベートにしようとするでしょう。メソッドをデフォルトのプライベートにしておくと、カプセル化の維持に役立ちます。フィールドについても同様で、デフォルトとしてプライベートに慣れる


2

1.静的とインスタンス

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

そして、私が考えることができる最悪の実践は、あなたが言及した統計と皆のお気に入りのシングルトンを含むグローバル状態です。この件に関する Misko Heveryの古典的な記事からの抜粋。

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

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

以前はこのように考えていなかったかもしれませんが、静的状態を使用するときは常に、秘密の通信チャネルを作成し、APIでそれらを明確にしないことになります。離れた場所での不気味なアクションにより、開発者はコードのすべての行を読んで潜在的な相互作用を理解し、開発者の生産性を低下させ、新しいチームメンバーを混乱させます。

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

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

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

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

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

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

3.重複と個別

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

4.プライベートとパブリック

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

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


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

まず、オブジェクトの作成とその使用を分離する必要があります。そのため、通常、new XMLReader()使用する場所の隣には権利がありません。

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

_document Document = reader.read(info);

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

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


1

私はあなたの質問には答えませんが、問題はあなたが使った用語によって生み出されると思います。例えば

XmlReader.read => twice "read"

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

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

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

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

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

}

0

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

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

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

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

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

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

これは問題だとは思いません。他のメソッドを呼び出すメソッドは、これによりタスクが明確に機能する小さなチャンクに分割される場合は問題ありません。

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

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

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