Объектный дизайн: как организовать / структурировать "коллекционный класс"
В настоящее время я пытаюсь понять, как мне организовать / структурировать класс, который я уже создал. Класс делает следующее:
- В качестве входных данных в конструктор, он принимает коллекцию логов
- В конструкторе он проверяет и фильтрует журналы с помощью ряда алгоритмов, реализующих мою бизнес-логику.
- После того, как вся фильтрация и проверка завершены, он возвращает коллекцию (Список) действительных и отфильтрованных журналов, которые могут быть представлены пользователю графически в пользовательском интерфейсе.
Вот некоторый упрощенный код, описывающий то, что я делаю:
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;
Другие ключевые элементы ввода:
- Я предвижу только одну из этих отфильтрованных коллекций, существующих в приложении (поэтому я должен сделать это статическим классом? Или, возможно, одноэлементным?)
- Тестируемость и гибкость в создании объекта важны (возможно, поэтому я должен оставить этот экземпляр класса для тестируемости?)
- Я бы предпочел упростить разыменование журналов, если это вообще возможно, поскольку фактические имена переменных довольно длинные, и для того, чтобы просто получить реальные данные, требуется около 60-80 символов.
- Моя попытка сделать этот класс простым - единственная цель этого класса - создать эту коллекцию проверенных данных.
Я знаю, что здесь не может быть "идеального" решения, но я действительно пытаюсь улучшить свои навыки с этим дизайном, и я был бы очень признателен за совет сделать это. Заранее спасибо.
РЕДАКТИРОВАТЬ:
Благодаря всем ответчикам, Dynami Le-Savard и Heinzi определили подход, который я в итоге использовал - методы расширения. В итоге я создал статический класс MyLogsFilter
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);
4 ответа
На мой взгляд, вы смотрите на метод, который возвращает коллекцию отфильтрованного журнала, а не класс коллекции, обертывающий вашу бизнес-логику. Вот так:
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();
}
}
}
Похоже, вы делаете три вещи с вашими журналами:
- Утвердите их
- Фильтруйте их и
- Доступ к ним
Вы хотите хранить журналы в коллекции. Стандартная коллекция List хорошо подходит, так как ей все равно, что в ней содержится, она предоставляет вам LINQ и позволяет заблокировать коллекцию с помощью обертки только для чтения.
Я хотел бы предложить вам разделить ваши проблемы на три шага выше.
Рассматривать
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));
Разделение проблем делает тестирование и ремонтопригодность намного лучше. И держись подальше от одиноких. По многим причинам, включая тестируемость, они не в фаворе.
Некоторые мысли:
Как вы правильно заметили, использование экземпляра класса улучшает тестируемость.
Singletons следует использовать, если (A) существует только один экземпляр класса во всей вашей системе и (B) вам нужно получить доступ к этому экземпляру в нескольких разных местах вашего приложения без необходимости передавать объект. Следует избегать ненужного использования шаблона Singleton (или любого другого вида "глобального состояния"), поэтому, если (B) также не выполняется в вашем случае, я бы не использовал здесь singleton.
Для простого разыменования рассмотрите возможность использования индексатора. Это позволит вам написать:
FilteredCollection Filterlogs = new FilteredCollection( secialArrayInput); // Пример доступа к данным фильтрованных журналов [5].MyLogData;
- Если ваш класс состоит только из конструктора и поля для доступа к результату, использование простого метода может быть более подходящим, чем использование класса. Если вы хотите сделать это причудливым способом, вы можете написать его как метод расширения для
SpecialArray<MyLog>
, позволяя вам получить к нему доступ так:
ListFilterlogs = secialArrayInput.Filter(); // Пример доступа к данным filteredlogs[5].MyLogData;
Если вы хотите унаследовать интерфейс SpecialArray для окончательно отфильтрованного массива, тогда производите от SpecialArray, в котором есть элемент экземпляра. Это позволило бы:
filteredCollecction [5].MyLogData; так далее..