Объектный дизайн: как организовать / структурировать «коллекционный класс» - PullRequest
3 голосов
/ 26 мая 2010

В настоящее время я пытаюсь понять, как мне организовать / структурировать класс, который я уже создал. Класс делает следующее:

  1. В качестве входных данных в конструкторе он принимает коллекцию журналов
  2. В конструкторе он проверяет и фильтрует журналы с помощью ряда алгоритмов, реализующих мою бизнес-логику
  3. После того, как вся фильтрация и проверка завершены, он возвращает коллекцию (Список) действительных и отфильтрованных журналов, которые могут быть представлены пользователю графически в пользовательском интерфейсе.

Вот несколько упрощенных кодов, описывающих то, что я делаю:

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. Я предвижу только одну из этих отфильтрованных коллекций, существующих в приложении (поэтому я должен сделать это статическим классом? Или, возможно, одноэлементным?)
  2. Важна тестируемость и гибкость при создании объекта (возможно, поэтому я должен оставить этот экземпляр класса для тестируемости?)
  3. Я бы предпочел упростить разыменование журналов, если это вообще возможно, поскольку реальные имена переменных довольно длинные, и для того, чтобы просто получить реальные данные, требуется около 60-80 символов.
  4. Моя попытка сделать этот класс простым - единственная цель класса - создать эту коллекцию проверенных данных.

Я знаю, что здесь не может быть "идеального" решения, но я действительно пытаюсь улучшить свои навыки с этим дизайном, и я был бы очень признателен за совет сделать это. Заранее спасибо.


EDIT:

Благодаря всем ответчикам, 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 ]

3 голосов
/ 26 мая 2010

Звучит так, будто вы делаете три вещи в своих журналах:

  1. Подтвердите их
  2. Фильтруйте их и
  3. Доступ к ним

Вы хотите хранить журналы в коллекции. Стандартная коллекция 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));

Разделение задач делает тестирование и ремонтопригодность намного лучше. И держись подальше от одиноких. По многим причинам, включая тестируемость, они не в фаворе.

1 голос
/ 26 мая 2010

На мой взгляд, вы смотрите на метод, который возвращает коллекцию отфильтрованного журнала, а не класс коллекции, обертывающий вашу бизнес-логику. Вот так:

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();
        }
    }
}
1 голос
/ 26 мая 2010

Некоторые мысли:

  • Как вы правильно заметили, использование экземпляра класса улучшает тестируемость.

  • Синглтоны следует использовать, если (A) в вашей системе есть только один экземпляр класса и (B), вам нужно получить доступ к этому экземпляру в нескольких местах приложения необходимость передавать объект вокруг. Следует избегать ненужного использования шаблона Singleton (или любого другого вида «глобального состояния»), поэтому, если (B) также не выполняется в вашем случае, я бы не использовал здесь singleton.

  • Для простого разыменования рассмотрите возможность использования индексатора . Это позволит вам написать:

    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;
0 голосов
/ 26 мая 2010

Если вы хотите унаследовать интерфейс SpecialArray для вашего окончательно отфильтрованного массива, тогда наследуйте от SpecialArray вместо наличия члена экземпляра. Это позволило бы:
filteredCollecction [5] .MyLogData; и т.д ..

...