オブジェクトデザイン:「コレクションクラス」を整理/構成する方法
-
04-10-2019 - |
質問
私は現在、私がすでに作成したクラスをどのように整理/構築するべきかを理解するのに苦労しています。クラスは次のことを行います。
- コンストラクターでの入力として、ログのコレクションが必要です
- コンストラクターでは、私のビジネスロジックを実装する一連のアルゴリズムを介してログを検証およびフィルタリングします
- すべてのフィルタリングと検証が完了した後、UIでグラフィカルにユーザーに提示できる有効なフィルタリングログのコレクション(リスト)を返します。
ここに私がしていることを説明するいくつかの簡略化されたコードがあります:
class FilteredCollection
{
public FilteredCollection( SpecialArray<MyLog> myLog)
{
// validate inputs
// filter and validate logs in collection
// in end, FilteredLogs is ready for access
}
Public List<MyLog> FilteredLogs{ get; private set;}
}
ただし、このコレクションにアクセスするには、次のことを行う必要があります。
var filteredCollection = new FilteredCollection( specialArrayInput );
//Example of accessing data
filteredCollection.FilteredLogs[5].MyLogData;
その他の重要な入力:
- アプリケーションに存在するこれらのフィルタリングされたコレクションの1つだけを予測します(したがって、静的クラスにする必要がありますか?
- オブジェクトの作成におけるテスト可能性と柔軟性は重要です(したがって、これをテスト可能性のためにインスタンスクラスに保つ必要がありますか?)
- 実際の変数名は非常に長く、実際のデータに到達するには60〜80文字が必要なため、可能な限りログの繰り返しを簡素化したいと思います。
- このクラスをシンプルに保つための私の試みは、クラスの唯一の目的は、この検証済みのデータのコレクションを作成することであるということです。
ここには「完璧な」解決策はないかもしれないことを知っていますが、このデザインでスキルを向上させようとしているので、それを行うためのアドバイスに大いに感謝しています。前もって感謝します。
編集:
すべての応答者のおかげで、Dynami Le -SavardとHeinziの両方が、私が最終的に使用したアプローチである拡張方法を特定しました。結局、mylogsfilter staticクラスを作成しました
namespace MyNamespace.BusinessLogic.Filtering
{
public static class MyLogsFilter
{
public static IList<MyLog> Filter(this SpecialArray<MyLog> array)
{
// filter and validate logs in collection
// in end, return filtered logs, as an enumerable
}
}
}
そして、私はこれをコードで読む唯一のコレクションを作成することができます
IList<MyLog> filteredLogs = specialArrayInput.Filter();
ReadOnlyCollection<MyLog> readOnlyFilteredLogs = new ReadOnlyCollection<MyLog>(filteredLogs);
解決
私の見方では、ビジネスロジックをラッピングするコレクションクラスではなく、フィルタリングされたログのコレクションを返す方法を見ています。そのようです:
class SpecialArray<T>
{
[...]
public IEnumerable<T> Filter()
{
// validate inputs
// filter and validate logs in collection
// in end, return filtered logs, as an enumerable
}
[...]
}
しかし、あなたが本当に望んでいるのは、実際にログのフィルタリングを担当するビジネスロジックを分離することであるように見えます。 SpecialArray
クラス、おそらくあなたはロジックが実際には関心のない多くのものに触れていると感じているからです SpecialArray
, 、または Filter
のすべての一般的なケースには適用されません SpecialArray
.
その場合、私の提案はあなたのビジネスロジックを別のものに分離することです namespace
, 、おそらく、上記のビジネスロジックを適用するために他のコンポーネントを使用および/または必要とし、機能性を拡張方法として提供するものを具体的に提供します。
namespace MyNamespace.Collections
{
public class SpecialArray<T>
{
// Shenanigans
}
}
namespace MyNamespace.BusinessLogic.Filtering
{
public static class SpecialArrayExtensions
{
public static IEnumerable<T> Filter<T>(this SpecialArray<T> array)
{
// validate inputs
// filter and validate logs in collection
// in end, return filtered logs, as an enumerable
}
}
}
そして、あなたがそのビジネスロジックを使用する必要があるとき、それは次のようになります:
using MyNamespace.Collections; // to use SpecialArray
using MyNamespace.BusinessLogic.Filtering; // to use custom log filtering business logic
namespace MyNamespace
{
public static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main2()
{
SpecialArray<Logs> logs;
var filteredLogs = logs.Filter();
}
}
}
他のヒント
ログに3つのことをしているように聞こえます:
- それらを検証します
- それらをフィルタリングします
- それらにアクセスします
ログをコレクションに保存します。標準リストコレクションは、その中にあるものを気にしない、LINQを提供し、読み取り専用のラッパーでコレクションをロックできるため、ぴったりです
上記の3つのステップに懸念を分けることをお勧めします。
検討
interface ILog
{
MarkAsValid(bool isValid);
... whatever data you need to access...
}
検証ロジックを別のインターフェイスクラスに配置します
interface ILogValidator
{
Validate(ILog);
}
そして、あなたのフィルタリングロジックはさらに別のものです
interface ILogFilter
{
Accept(ILog);
}
次に、linqで、次のようなもの
List<MyLog> myLogs = GetInitialListOfLogsFromSomeExternalSystem();
myLogs.ForEach(x => MyLogValidator(x));
List<MyLog> myFilteredLogs = myLogs.Where(x => MyLogFilter(x));
懸念の分離により、テストと保守性がはるかに優れています。シングルトンから離れてください。テスト責任を含む多くの理由で、彼らは好意的ではありません。
いくつかの考え:
正しく指摘しているように、インスタンスクラスを使用すると、テスト可能性が向上します。
(a)システム全体にクラスの1つしかない場合は、シングルトンを使用する必要があります と (b)オブジェクトを渡すことなく、アプリケーションの複数の異なる場所でこのインスタンスにアクセスする必要があります。シングルトンパターン(または他の種類の「グローバルステート」)の不必要な使用は避ける必要があります。したがって、(b)あなたの場合にも満足していない限り、ここではシングルトンを使用しません。
単純な繰り返しの場合は、anを使用することを検討してください インデクサー. 。これにより、次のように書くことができます。
FilteredCollection filteredlogs = new FilteredCollection( secialArrayInput ); //Example of accessing data filteredlogs[5].MyLogData;
- クラスがコンストラクターと結果にアクセスするフィールドのみで構成されている場合、単純なものを使用して 方法 使用するよりも適切かもしれません クラス. 。あなたがそれを派手な方法でやりたいなら、あなたはそれを 拡張法 にとって
SpecialArray<MyLog>
, 、このようにアクセスできるようにします。
List<MyLog> filteredlogs = secialArrayInput.Filter(); //Example of accessing data filteredlogs[5].MyLogData;
最終フィルタリングアレイのためにSpecialArrayのインターフェイスを継承したい場合は、インスタンスメンバーを持つSpecialArray Instadから派生します。それは許可されます:
filteredCollection [5] .Mylogdata;等..