私はPythonを使用していますが、私の質問は一般的にOOPに当てはまります。クラスを作成するときはいつでも、パラメータ/属性をコンストラクタ、またはパラメータが関連するメソッド。
たとえば、days_away
メソッドを持つPersonクラスを考えてみましょう。 days_awayの役割は、いくらtimestamps
を与えられた人がdays離れているかを計算することです。 Personのコンストラクター(つまり、__init__
)は、パラメーターとしてname
を取得します。また、パラメータとしてtimestamps
を取得しますか、またはtimestamps
は、特定の日時の期間で人が何日間離れていたかを計算することになっているdays_away
メソッドのパラメータとして取得しますか?どうして?
編集:コンテキストを追加します。これはアパートを共有する人の間で電気代を分けるためです。請求書は、人がその日を支払う必要がないように、人がいなくなった日数の関数として分割されます。離れた日数は、その人が残してアパートに戻ったときのタイムスタンプを指定して、days_awayメソッドによって計算されます。
使用しているコードを見ます。
これの違いは何ですか
duration = Person(then).days_away(now)
この?
duration = Person().days_away(then, now)
何も。だが
duration = person.days_away(now)
違います。このコードはthen
を知っている必要はありません。 now
を知っていれば十分です。それは非常に強力です。このコードは、now
のみを知っている場所で機能します。
したがって、唯一の問題は、then
をnow
から分離する必要があるかどうかです。そうでない場合は、まあ。
インスタンスメンバーを使用するかどうかを理解する1つの方法。コンストラクターのパラメーターとして渡されるか、メソッドのパラメーター、またはその他は、情報の存続期間を考慮することです。
Person
にtimestamps
があり、実質的に人物オブジェクトと同じ長さで存続する場合、それはおそらくそれが属している場所です。
ある見方をすれば、オブジェクトは、情報を抽象化するための情報の結合です。このバインディングは、オブジェクトの持続時間に適していると思います。このバインディングは、コンストラクターパラメーターを介して行われます(理想的には、それができない場合があります)。
ただし、インスタンスフィールドを使用してパラメーターをメソッドに渡すことは望ましくありません。パラメーターとしてグローバル変数を使用する場合と同じ問題が多く発生します(たとえば、扱いにくくエラーが発生しやすく、スレッドセーフではありません)。
person
が異なるさまざまな状況で、同じtimestamps
でdays_away
メソッドを呼び出すことはできますか?その場合、timestamps
はperson
自体よりも有効期間が短い(少なくともライフタイムは間違いなく異なる)ため、コンストラクターパラメーターではなくメソッドパラメーターとして属します。
要約すると、何かを1つの抽象にバインドしますか、または1つのアイテムを動的に提供するつもりですか?セットおよびリセットし、バインディングで得られる範囲を超える状況で使用されます。
それは依存関係、範囲、思考プロセスについてです。後者から始めましょう。
あなたはたぶんあなたがそれを機能させる方法をあなた自身に尋ねるこの種の問題について行くでしょう。作成しようとしているオブジェクトのアプリケーションをすでに考えており、そのタスクに焦点を当てています。 OO進むべき道ではありません。シナリオベースのソリューションにジャンプしています。
Personクラスが必要だと認識したら、それに焦点を当てて、個人を識別するのは何かと考えます。それを人と呼ぶ前に、あなたはそれについて何かを知る必要がありますか? personオブジェクトはいつアプリケーションで意味を持ち始めますか?
名前の場合は、コンストラクターが取得します。それが人々が数である政府のアプリケーションのためであるならば、あなたは人を全体にするためにその数が必要になります。それが配達サービスの場合は、結局のところ、そのアドレスにもっと興味があり、その人自身がそのアドレスの知っているだけの属性であることに気づくかもしれません。
上記は、何かのタスクで後で必要になる可能性があるという理由だけで、オブジェクトコンストラクターにデータを注入できないようにする必要があります。コンストラクターのタスクは、オブジェクトを作成することであり、動作を準備することではありません。
今あなたの例です。 DaysAwayは行為ではなく、メソッドであってはなりません。どうやらあなたは欠席に対処する必要があります。それがすべてである場合、プロパティとしてPersonにタイムスロットのリストを含め、その人物が別の期間不在になることがわかったときにスロットを追加できますが、予定表オブジェクトプロパティを使用するほうがよい場合があります。ですから良い例ではないと思います。
データがオブジェクトに関連付けられているのではなく、行為に関連付けられている場合は、パラメーターとしてメソッドに渡す必要があります。オブジェクトは、あるコンテキストのあるタスクのある時点で必要となる可能性のある変数のコートラックではありません。オブジェクトデータは、オブジェクトの存続期間中、オブジェクト自体にとって意味のあるものでなければなりません。これは、スコープと依存関係の問題の両方をカバーしています。
クラスに属するデータは、コンストラクターに入れる必要があります。このデータは、プライベートフィールドに配置してカプセル化するのが理想的です。メソッドはこのデータを操作できますが、クラスに属さない追加のデータがメソッドで必要になる場合があります。
人が私的なフィールドとして生年月日を持ち、特定の日付での人の年齢を知りたいとします。その2番目の日付は、年齢を計算するメソッドの引数でなければなりません。
コンストラクターを考える1つの方法は、「完全」であることが必要なものでオブジェクトインスタンスをインスタンス化する関数です。
Bagのオブジェクトが必要で、Bagを空にできる場合、そのコンストラクターまたはそのコンストラクターを作成して、変数を渡す必要がないようにすることができます。
Bag b = new Bag();
ただし、オブジェクトがおそらくデータベース接続の場合、この不自然な図では、オブジェクトがデータベースに接続するために「必須」であるため、コンストラクターにはdataBasePasswordやdatabaseUserNameなどの属性が必要になる可能性があります。
DatabaseConnection dc = new DatabaseConnection(user, pwd);
次のヒューリスティックがほとんどの場合に非常に役立つことがわかりました。
Premiseを優れたオブジェクト指向設計の公理として使用する場合、Theoremは設計を導くのに役立つ可能性があります。実際には1つあります(クラス名のためだけにオブジェクトに盲目的に名前を付けるのではありません)。
TL; DRバージョンでは、この回答は次のようになっています。設計中の最新の時点でパラメーター値が判明した場合は、メソッドに追加するだけです。事前に知っている場合は、コンストラクターまたは別のメソッドのいずれかで取り出してください。
Point
メソッドを使用してdistanceFrom
クラスを作成し、別のポイントからの距離を計算します。メソッドdistanceFrom(x, y, xOther, yOther)
を持つことは理にかなっていますか?いいえ、その構築時にポイントのxとyを知っていたので、それをコンストラクタに渡すべきでした。したがって、適切なメソッドはdistanceFrom(x,y)
(または、より可能性が高いのはdistanceFrom(point)
)です。
Personからの日数を計算するときは、2つのタイムスタンプを減算する必要があります。それらの1つはnowです。もう1つは過去のある時点です。過去のタイムスタンプはearlierであることがわかっていたため、最適な設計はそれをメソッドに含めないことです。 Person
オブジェクトをできるだけ早く過去のタイムスタンプと知り合う方法を見つけてください。できればそれがわかった瞬間に、つまりコンストラクタで、またはPerson
オブジェクトの有効期間に沿って変更したい場合はPerson.cameBack(timestamp)
の行に沿って別のメソッドを作成し、Personがタイムスタンプにマークを付けて、後でdaysAway
メソッドに使用できるようにします。