web-dev-qa-db-ja.com

データを処理/操作するための設計パターン

データの解析または処理を処理する必要があるクラスまたはコードの一部を作成するとき、問題に対するより良い解決策があるかどうか、自分自身に尋ねなければならないことがあります。

例1:最近の試験では、カーシェアリングサービスのシミュレーションを記述する必要がありました。私たちはいくつかの(n * n)地区に分割された都市を与えられました。各地区は独立して働き、2つの機能を持っていた。最初の1つは借用された車の量を表し、2番目の1つは返された車の量を表します(この特定の地区では、1時間あたり)。次に、各地区のいくつかの値を計算する必要がありました。

明らかに私は都市クラスと地区クラスを持っていました。問題は次のとおりです。シミュレーション部分はどこにありますか?地区クラス内に関数を記述したり、すべての処理を処理する専用(シミュレーター)クラスを記述したりできます。

だから基本的に:

City.Simulate()
   foreach District in City
        District.Simulate
return results

または

Simulator(City)
   Simulate
      Get all district objects
         do simulation & collect results
   return results

結局、私は最初のオプションを選ぶことにしましたが、私はこのアプローチに本当に満足していません。

例2:この例で問題が明らかになるかもしれません。現在MQTTブローカーを作成しており、現在パケットパーサーに取り組んでいます。パケットの種類(接続、公開、購読など)ごとに1つのクラスがあります。 どこで解析を行うのですか?

このプロジェクトでは、タイプごとに機能を持つ解析用の専用クラスを作成しました。ただし、以前の(類似した)プロジェクトでは、各パケットクラスがカスタム解析関数を提供していました。


より一般的な質問は次のとおりです。どの時点でデータの処理を外部委託しますか/どの時点でクラス自体の内部でデータを処理しますか?

3
Camo

この種の問題に何度も取り組んできたため、2つの主な理由から、常にオプション#2を使用します。

1。 [〜#〜] srp [〜#〜]のほうが正しいと思います

「シミュレート」は基本的にそれ自体です。 「このクラスは何をするのか」という質問をすると、オプション#1のcityクラスには2つの答えがあります。「都市関連のデータを追跡し、そのデータに関連する統計を計算する」です。 「および」は、SRPに違反していることを意味します。それはひどい考えであり、そのようにしてはならないというわけではありませんが、OOPの基本原則から、それを独自のクラスに分離する方が良いと思います。より実用的な理由:

2。シミュレーション、統計、計算などは頻繁に変更されます

これが私にとって最大の理由です。現在、シミュレーションは「どの地区がいつオーバーサブスクライブされるのか」などの計算を行っています。それはかなり単純明快で、もしそれがあなたの都市のクラスに住んでいたとしても、それは世界の終わりにはなりません。しかし、さらに統計が必要であると判断した場合はどうでしょうか。現実のプロジェクトでは、そのようなことが起こることが保証されています。年末までに、12の異なる統計計算を実行する予定であり、シミュレーションは都市によって異なる場合があります。その場合、計算される統計情報の異なるkindごとに異なるsimulationクラスがあり、特定の都市に固有のクラスさえあると想像できます。このような状況は、すべてが都市のクラスに住んでいる場合、管理することは不可能ですが、物事を別々のクラスに分割し始めると、非常に簡単に管理できます。

つまり、簡単に言えば、解決している問題の現在のレベルでは、どちらの方法でも(実際には)うまく機能します。ただし、長期的には、統計とシミュレーションの計算は独自の種類のものであり、再利用性と保守性を最大化するために独自のクラス階層に配置する必要があります。

3
Conor Mancone

明らかに私は都市クラスと地区クラスを持っていました。問題は次のとおりです。シミュレーション部分はどこにあるのですか?地区クラス内に関数を記述したり、すべての処理を処理する専用(シミュレータ)クラスを記述したりできます。

私が見ているように、地区は自分のクラス内にある情報に関連する情報を計算することに対してのみ責任があります。このcanは、それ自体のクラスの他のインスタンスとの計算の実行、たとえば、等しいかどうかの比較やテストにまで拡張されますが、ほとんどの場合、世界外(または地区内)には関心がないはずです。この場合)。

したがって、計算を実行するのは市の仕事です。 Cityは間違いなくDistrictインスタンスの単なるコンテナーと考え​​ることができ、Cityが多くのロジックによって行き詰まっている場合、シミュレーターであるCityとコンテナーであるCityを分離することを真剣に検討します。実際、CitySimulatorはそのようなクラスの適切な名前かもしれません。しかし、あなたが述べたように、これは簡単な例なので、Cityだけで十分です。

例2:この例で問題が明らかになるかもしれません。現在MQTTブローカーを作成しており、現在パケットパーサーに取り組んでいます。パケットの種類(接続、公開、購読など)ごとに1つのクラスがあります。解析はどこで行うのですか?

構文解析が十分に単純であれば、メッセージのタイプを判別するParserクラスを作成し、メッセージの残りの部分を別々のメソッドに渡し、タイプごとに1つずつ残りの部分を解析します。しかし、試験では、それはおそらく彼らが求めている答えではありません。

CanHandleとhandleの2つのメソッドを使用して、各メッセージタイプを処理するクラスのリストを呼び出す以外に何もしないParserクラスを作成できます。 canHandleが最初に呼び出され、その特定のタイプを処理するクラスは、そのタイプのメッセージである場合にのみtrueを返す必要があります。その後、ハンドルが呼び出され、適切に行われると、各クラスが独自のメッセージタイプを処理することを意味します。その他、それらとParserクラスの間の結合を効果的に減らします。

0
Neil