グローバルが理にかなっているとしたら?


10

多くのオブジェクトに必要な値を持っています。たとえば、オブジェクトとしてさまざまな投資を行う金融アプリケーションで、それらのほとんどは現在の金利を必要とします。

金利をプロパティとして、「金融環境」をオブジェクトとしてカプセル化したいと思っていました。しかし、その値を必要とする兄弟オブジェクトはそれに到達できません。

では、デザインを過度に結合せずに、多くのオブジェクト間で値を共有するにはどうすればよいですか?明らかに、これは間違っていると考えています。


2
計算の期間中、金利は固定されていますか、それともタイムステップ間で変動する可能性があるシミュレーションのようなことをしていますか?
James

これはシミュレーションに似ています-実行中に変更できます。
グレッグ

その場合、各投資は本当に金利を節約する必要がありupdateますか、それとも各タイムステップで呼び出される関数へのパラメーターを介してそれを受け取ることができますか?シミュレーションの動作を疑似コードで投稿できますか?
ジェームズ

3
あなたは正しいトラックの1つであり、a SingletonはOO構文糖を含むグローバルであり、コードを可能な限り最悪の方法で密結合する恐ろしいソリューションです。理解するまでこの記事を繰り返し読んでください!

金利は、入力として受け取り、数値を出力として返す関数である時系列のようなものDateTimeです。
rwong 2012

回答:


14

多くのオブジェクトに必要な値を持っています。

これはデザインのにおいです。多くのオブジェクトが何かについて知る必要があるのは珍しいことです。とはいえ、現在の金利は例外的な状況のかなり良い例です。心配しなければならないのは金利がめったにないことです。異なる金融商品は異なるレートを使用します。少なくとも、ロケールによって使用される「標準」レートは異なります。さらに、テストとレポートを支援するために、現在のレートを使用したくないので、通常はレートを渡します。「仮定」または「レポート日現在」のレートを使用したい。

では、デザインを過度に結合せずに、多くのオブジェクト間で値を共有するにはどうすればよいですか?

それらを共有することにより、それらすべてが単一のインスタンスを参照することはありません。さまざまな計算への入力として現在の金利のようなものが必要であるため、同じことを渡すことは、ある程度まで結合していますが、過剰結合ではありません。


17
例外的な状況の問題は、実際のソフトウェアではそれらはそれほど例外的ではないということです。
mattnz

これは深刻な誤解だと思います。私たちのアルゴリズムは、異なるコンテキスト内に同時に存在します。1つはグローバルコンテキストです。次に、オブジェクト指向言語の「この」コンテキスト。Webサービスのセッションコンテキスト、DB環境のトランザクションコンテキスト、guiのメインウィンドウなど。これらのコンテキストに属する情報がいくつかあります。それらは、同じ場所にいる誰でも同じセット(オブジェクト)で「ぶらぶら」て利用可能でなければなりません。これは大丈夫です。問題は、コンテキストサービスを作成したり、JavaのSpringのようなフレームワークを使用したりするのではなく、オブジェクトごとにこれを解決することです。
Lorand Kedves

3
現在の金利に例外はありません。実世界のアイテムには1つの値を持つ多くの例があります。たとえば、一般道の制限速度、許容できる血中アルコール濃度、GST(またはVAT)税率などです。それが科学と工学の違いです。エンジニアは今日の現実世界の問題を解決します。科学者は現実世界が完璧な箱に収まり、それらの問題を解決する日を夢見ています。
mattnz

1
これは単純で、10年にわたるOOPの経験に依存しないので、これを答えとして選びました。すべての回答者に感謝します。多くの参考文献のおかげで私は丸一日読んでいましたが、それでも少し困惑したままです。そのような単純な質問に対して、私は応答の多様性とその背後にある感情に驚きました。私は、シングルトンによって最もよく提供されるグローバルで変化するデータの中心的なソースがあることを確信しています。シングルトンを回避するためだけに、オブジェクトの階層の上下にポインタを渡すべきではないと思います。皆さんありがとうございました。
グレッグ

@mattnz、問題は、企業、州、または国にまたがることができる複数のユーザーベースにプログラムを配布する場合、例のすべてが可変であるということです。それらのすべては、時間とともに変化する可能性もあります。
Dan Lyons

10

この特定のインスタンスでは、シングルトンパターンを使用します。FinancialEnvironmentは、他のすべてのクラスライブラリが認識しているオブジェクトですが、シングルトンによってインスタンス化されます。理想的には、そのインスタンス化されたオブジェクトをさまざまなクラスライブラリに送信します。

例えば:

  • サービス層(クラスライブラリ)-シングルトンを介してFinancialEnvironmentオブジェクトをインスタンス化します
  • ビジネスロジックレイヤー(クラスライブラリ)-サービスレイヤーからFinancialEnvironmentオブジェクトを受け入れます
  • データアクセス層(クラスライブラリ)-サービス層(またはアーキテクチャによってはビジネスロジック層)からFinancialEnvironmentオブジェクトを受け入れます。あるいは、シングルトンがデータアクセスレイヤーを呼び出して、金利などの情報をリポジトリ(データベース/ Webサービス/ WCFサービス)から取得します。
  • エンティティ(または、それを呼び出す場合はDTO)クラスライブラリ-FinancialEnvironmentオブジェクトが存在する場所。他のすべてのクラスライブラリには、エンティティクラスライブラリへの参照があります。

他のクラスはEntitiesクラスライブラリを介してのみ関連付けられ、インスタンス化されたFinancialEnvironmentオブジェクトを受け入れます。彼らはそれがどのように作成されたかを気にせず、サービス層だけが行います。彼らが望むのは情報だけです。シングルトンは、@ Telastynが指摘したように、ローカルのルールに応じて、いくつかのFinancialEnvironmentオブジェクトを格納するのに十分なほど賢い場合もあります。

余談ですが、私はシングルトンパターンの大ファンではありません。非常に簡単に悪用される可能性があるため、コードのにおいだと考えています。しかし、場合によってはそれが必要になります。

更新:

あなたが絶対的に、積極的にグローバル変数を持つ必要がある場合は、上記のようにシングルトンパターンを実装するとうまくいきます。しかし、私はこれの大ファンではなく、私の元の投稿からのコメントに基づいて、他の何人かの人もそうではありません。InterestRateのように不安定なものとして、シングルトンは最適なソリューションではない可能性があります。シングルトンは、情報が変化しない場合に最適に機能します。たとえば、アプリケーションの1つでシングルトンを使用して、パフォーマンスカウンターをインスタンス化しました。なぜなら、それらが変更される場合は、更新されるデータを処理するための適切なロジックが必要だからです。

私が賭けをしている人なら、金利はデータベースのどこかに保存されているか、またはWebサービスを介して取得されたに違いない。その場合、その情報を取得するには、リポジトリ(データアクセス層)が推奨されます。不必要なデータベースへのアクセスを回避するために(金利がどのくらいの頻度で変化するか、またはFinancialInformationクラスの他の情報がわかりません)、キャッシングを使用できます。C#の世界では、MicrosoftのCaching Application Blockライブラリは非常にうまく機能します。

上記の例から変更されるのは、オブジェクトをインスタンス化するシングルトンの代わりにFinancialInformationがデータアクセスレイヤーから取得する必要があるサービスレイヤーのさまざまなクラスだけです。


8
ああ。グローバルをシングルトンにしても、臭いが減ることはありません。どちらかと言えば、あなたはそれ以上に自分を制限しました。
Telastyn 2012

3
@DavidCowden実装が複雑でないかどうかは関係ありません。彼らはあなたのデザインをグローバルより悪いものにします。それらはグローバルであり、1つしか持たない(不要な)制限を適用します。
Telastyn 2012

4
「シングルトンにすれば、悪い習慣からベストプラクティスになる」という皮肉なコメントを投稿するつもりでしたが、それはすでに受け入れられ、賛成票だったことがわかりました。非常に素晴らしい!
ケビン

4
SingletonOOシンタックスシュガーと怠惰で弱い心のための松葉杖を持つグローバルです。コードがガンになるようなものとコードSingleton/global密接に結びつける絶対的に最悪の方法です

4
@Telastyn:理論的なソフトウェア設計の完全に秩序立った世界を離れ、現実の世界に加わると、ほとんどの完璧な設計が不幸になるという残念な現実。
mattnz 2012

4

設定ファイル?

「グローバルに」使用される値がある場合は、構成ファイルに入れてください。次に、各システムとサブシステムはこれを参照し、必要なキーをプルして、読み取り専用にします。


それで、金利が変わるたびにユーザーに設定ファイルを更新させるでしょうか?
カレブ2012

2
何故なの?もちろん、「変数」に依存します。頻繁に変更されるものは、CENTRALIZEDデータストア内に配置する必要があります。
Darknight

1

私がリリースしたばかりの適切なサイズのプロジェクト(〜50k LOC)で約1か月のメンテナンスを行っている人の経験から話しています。

あなたはおそらく本当にグローバルオブジェクトを望んでいないと言えるでしょう。そのような粗末を導入すると、それが役立つよりも虐待の多くの機会を提供します。

私の最初の提案は、現在の金利を必要とするいくつかの異なるクラスがある場合、おそらくそれらにIInterestRateConsumer何かを実装させたいと思うでしょう。そのインターフェースの内部には、SetCurrentInterestRate(double rate)(または意味のあるものは何でも)、またはおそらく単なるプロパティがあります。

金利を渡すことは実際には結合されません-クラスが金利を必要とする場合、それはそのAPIの一部です。クラスの1つが他のクラスがその金利をどのように使用するかを正確に心配し始めた場合にのみ、それは結合です。


金利を渡すこと結合であり、悪い結合ではありません。
vaughandroid

1

Martin Fowlerが静的グローバルをより柔軟なものにリファクタリングする方法について簡単に説明した記事を公​​開しています。基本的には、それをシングルトンにしてから、インスタンスのクラスをサブクラスでオーバーライドできるようにシングルトンを変更します(必要に応じて、インスタンスを作成するロジックを、サブクラス化できる別のクラスに移動します。スーパークラスのインスタンスを作成する場合、後でそれを置き換えるのは問題です)。

もちろん、シングルトン(置換可能なシングルトンでさえも)の問題と、同じオブジェクトをどこにでも渡すことの難しさを比較検討する必要があります。

「金融環境」オブジェクトに関する限り-最初のパスでプログラミングすると便利ですが、完了すると、いくつかの追加の依存関係が追加されています。金利が必要なクラスは、金融環境オブジェクトを渡されたときにのみ機能するようになりました。これにより、金融環境オブジェクトがたまたま存在していない場合に再利用することが難しくなります。だから私はそれを広く渡さないようにします。


0

金利データを中央キャッシュに入れませんか?

memcachedなどのいくつかのキャッシュライブラリのいずれかを、要件に最も適したものを使用して、同時実行性とコード管理の問題をすべて解決し、アプリケーションを複数のプロセスに拡張できます。

または、すべてを独占してデータベースに保存し、複数のサーバーに拡張できるようにします。


0

このような状況で、私は「コンテキスト」という用語をうまく導入(再利用)し、時には複数のレイヤーを使用しました。

これは、これらの種類のオブジェクトをリクエストできるシングルトン、つまり「グローバル」オブジェクトストアを意味します。それらを必要とし、ストアのヘッダーを含め、グローバル関数を使用してオブジェクトインスタンスを取得するコード(現在のように、金利プロバイダー)。

ストアは次のいずれかです。

  • 厳密に型指定:提供されるすべての型のヘッダーを含めるため、InterestRate getCurrentInterestRate();などの型指定されたアクセサーを作成できます。
  • またはジェネリック:Object getObject(enum obType); そして、obType enumを新しい種類(obtypeCurrentInterestRate)でのみ拡張します。

システムが大きければ大きいほど、後者のソリューションはより使いやすくなり、間違った列挙型を使用するリスクは非常に小さくなります。一方、前方型宣言を許可する言語では、ストアにすべてのヘッダーを含めなくても型付きアクセサーを使用できると思います。

もう1つの注意:GUIや印刷出力、グローバルログやセッションレベルのログなどの言語値が異なる場合など、さまざまな用途で同じオブジェクトタイプの複数のインスタンスが存在する可能性があるため、列挙型/アクセサー名には実際のタイプを反映しないでください、ただしリクエストされたインスタンスの役割(CurrentInterestRate)。

ストアの実装では、コンテキストレベルとコンテキストインスタンスコレクションを管理する必要があります。簡単な例は、グローバルコンテキスト(そのオブジェクトに対するすべてのリクエストに対して1つのインスタンス-サーバーファームがある場合に問題がある)と各WebセッションのコンテキストがあるWebサービスです。また、複数の並列セッションなどを持つ可能性のある各ユーザーのコンテキストを持つこともできます。複数のサーバーでは、そのようなことのために一種の分散キャッシュを使用する必要があります。

リクエストが来たら、リクエストされたオブジェクトがどのコンテキストレベルであるかを決定し、呼び出しのためにそのコンテキストを取得します。オブジェクトが存在する場合は、それを送り返します。そうでない場合は、そのコンテキストレベルで作成して保存し、それを返します。もちろん、作成セクションを同期します(それを分散キャッシュに公開します)。作成はプラグインのように構成可能で、クラス名(Java、Objective Cなど)でオブジェクトインスタンスを作成できる言語が最適ですが、ファクトリ関数を備えたプラグ可能なライブラリを使用してCでも行うことができます。

補足:呼び出し元は、自身のコンテキスト、および要求されたオブジェクトのコンテキストレベルについてあまり知らないはずです。理由:1:これらのパラメーターを操作することで、間違い(または「巧妙なトリック」)を犯しやすくなります。2:要求されたコンテキストレベルは後で変更される可能性があります。私は主にコンテキスト情報をスレッドに接続しているので、オブジェクトストアにはリクエストからの追加のパラメーターなしで情報が含まれます。

一方、リクエストにはインスタンスのヒントが含まれる場合があります。たとえば、特定の日付の金利を取得する場合などです。これは同じ「グローバル」アクセスである必要がありますが、日付に応じて複数のインスタンスがあり(レート変更の間に同じインスタンスに異なる日付値を導く)、そのため、リクエストに「ヒント」オブジェクトを追加することをお勧めします。ストアではなくインスタンスファクトリ。ストアで使用される、ファクトリーへのkeyForHint。これらの関数は後で追加できます。

あなたの場合、これは一種のやり過ぎです(グローバルレベルで提供されるオブジェクトは1つだけです)が、今のところ非常に小さく単純な追加コードの場合、さらに複雑な要件のためのメカニズムが得られます。

もう1つの朗報です。Javaを使用している場合、あまり考えずにSpringからこのサービスを入手できます。詳しく説明したいと思います。


0

グローバル(またはシングルトン)を使用しない理由は、最初は値が1つだけであると期待していても、同じプログラムで同じコードを複数回使用できると、意外と便利なことがよくあります。たとえば、次のようになります。

  • 金利が異なる場合に何が起こるかを計算する
  • 一部の要素は米国の金利に依存し、一部の要素は英国の金利に依存する

金利を「金融商品」クラスのメンバーにして、それをメンバークラスに渡す必要があることを受け入れます(計算ごと、または構築時にそれらにポインター/フックを与える)。


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