web-dev-qa-db-ja.com

C#にはフレンドクラスがありません-より良いオプションは何ですか

私は2、3年ごとにこの質問に戻るので、ここで質問することで、一度にすべてを解決することにしました。

したがって、シーケンス:

  1. 私はJsonファイルを解析するシンプルなアプリケーション(サードパーティアプリへの構成、さらにconfigと名付けられている)を作成し、ユーザーがそれを使用して編集できるようにします。プラットフォームを選択し、いくつかの基本的なUIを作成し、いくつかのボタンをNewFileCommand、OpenFileCommand、SaveFileCommandなどにバインドしました。
  2. 次に、開いた(およびクラスに逆シリアル化した)構成をどこかに保存する必要があるため、新しいStorageServiceクラスを作成しました。 OpenFile()、ReloadFile()、SaveFile()などのメソッドがいくつかあります。これらはViewModelから呼び出され、StorageServiceはファイルを開き、設定を読み取ってフィールドとして保存します。必要に応じて、ファイルに保存します-これまでのところとても良いです。
  3. この時点で、設定自体にいくつかの操作を追加したいと思います。 StorageServiceには入れたくありません。 GetConfigPropertyByNameAndDoSomeMagicWithIt()などの一部のメソッドは、必ず別のサービスに配置する必要があります。
  4. そこで、別のサービス、OperationsServiceを作成しました。これで、StorageServiceに格納されている構成へのアクセスと、GetConfigPropertyByNameAndDoSomeMagicWithIt()メソッドの追加が必要になります。 StorageService.configをパブリックAPIに公開したくありません-必要なすべてのアクセサーを既に持っているためです。 OperationsService以外のすべての人向け。 StorageService.configにアクセスできるものを作成したいと思います。 OperationsServiceをStorageServiceのフレンドとして宣言し、その内部にアクセスできる、「フレンド」クラスの古き良きC++コンセプトの良いユースケースのように見えます。
  5. 私は以前に同様の問題に直面したことを思い出しているので、「C#フレンドリークラス」を探して、 最初のリンク を開いて、最新の解決策を見つけようとしました-そして、デジャヴ感じ。

それで、私の現在のアプローチの流れは何ですか? 「適切な現代OOPアーキテクチャー」の方法でそれを解決する方法は?
OperationsServiceをStorageServiceの内部クラスにしますか?これは、すべてのコードが同じファイルに格納されることを意味します(SOLIDのかなりの違反)。
OperationsServiceをStorageServiceの内部クラスにして、部分的にして別のファイルに移動しますか?ちょっと気味が悪いようです。

一般的には、アプリの残りの部分でOperationsServiceを公開したり(IoCコンテナーに登録したりすることすらしないように)したくありません。 OperationsServiceはそれほど大きくないので、3番目(ある種のラッパーサービス)も作成したくありません。

オブジェクトに関しては、「オブジェクトAにはプライベートフィールドがあり、オブジェクトBに対してのみアクセス可能である必要があります。オブジェクトのAの責任はIOであり、オブジェクトのBの責任は一部のロジックであるため、マージできません」と問題を言い換えることができます。

まとめると、次のようになります。

  • pOCOであり、Jsonからの逆シリアル化されたデータを含むconfig。
  • OpenFile()、SaveFile()を含むStorageService。また、構成の現在のインスタンスが含まれています。
  • 理論上のOperationsService。GetConfigPropertyByNameAndDoSomeMagicWithIt()にいくつかのロジックが含まれています。そのロジックを実行するには、構成へのアクセス権も必要です。問題は、OperationsServiceとStorageServiceを関連付ける方法です。
5

特定のデータに関してOperationsServiceをシステムの他の部分より優先する1つの方法は、必要に応じてそのようなデータを直接渡すことです。構成操作ロジックをOperationsService内に保持し、 StorageServiceにパブリックAcceptOperations()メソッドを追加します。これは、OperationsServiceオブジェクトを引数として受け取り、関連する構成操作ロジックを呼び出して、プライベートに保持されている関連構成データを直接渡します。

具体的には、GetConfigPropertyByNameAndDoSomeMagicWithIt()OperationsServiceのメンバーにすることで、関連するStorageServiceインスタンスでAcceptOperations()を呼び出し、thisを引数として。 AcceptOperations()自体は、パラメーターとして指定されたOperationsServiceインスタンスに対して具体的なManipulateConfig()メソッドを呼び出し、内部構成データをManipulateConfig()

この提案は一部、クラシックOOP Visitor Pattern に似ていますが、Double Dispatchイディオムですが、誰が何を訪問できるかを制限的に定義するという概念を維持し、そのような責任の分離を実行するパターンの背後にある元のアイデアを確実に維持します "... [それ構造]を変更せずに既存のオブジェクト構造に新しい操作を追加する機能を提供します。 "このようなパターンの全体的な考え方は、 Open-closed主義 に従うことを支援することですこれはSOLID原則の1つであり、それを維持することは、そもそもあなたの懸念事項であると述べたものです。

2
Geezer

友好的にしたいクラスを別のアセンブリに配置できます。次に、アセンブリの外部から利用可能にするものだけをpublicとして宣言します。他のアセンブリのクラスから非表示にするものは、宣言internalです。これはあなたが望むものをほとんどあなたに与えるはずです。

OOPの観点からは、友人はパブリックメンバー以外のアクセスを許可するため、これは本質的に反対のOOPなので、これは友人よりも優れています。

5
Martin Maat

オブジェクトは内部状態を処理することになっており、必要に応じてオブジェクト自体を変更する必要があります。オブジェクトを誤って抽出したと思います。 _Storage Service_は実際には永続性マネージャーであり、内部値として構成値を保持するべきではありません。ストレージサービスは、ファイルからオブジェクトへの変換、およびその逆の変換を管理するだけです。次に、構成値を処理したりそれらを公開したりするすべてのメソッドを持つ構成オブジェクト、または作成時に構成オブジェクトを受け入れ、必要なアクセスメソッドを公開する構成マネージャークラスを使用できます。

あなたが問題を抱えている理由は、_Storage Service_が現在単一責任の原則を破っているからです。 GetConfigPropertyByNameAndDoSomeMagicWithIt()でさらに分解しようとすると、より明確になります。そのような場合の適切な解決策は、より明確な責任を持つオブジェクトにリファクタリングすることです。友人は貧弱な設計に対する応急処置の解決策です。

1
Ryathal

私が見逃しているのは、IConfigurationServiceであり、その具体的なバージョンはIConfigStorageServiceに依存しています。ファイルから設定を取得するLocalConfigStorageService、またはクラウドバケットから取得するS3ConfigStorageServiceを使用できます。または、データベースから取得するDatabaseConfigStorageService。 3つのケースすべてで、IConfigStorageServiceは、構成を取得し、IConfigurationServiceの構成に対する更新を格納します。

IConfigurationServiceは、返された構成を使用して、コンシューマーの要求に応答します。

これは具体的には Bridge Pattern のインスタンスです

0
Michael Brown