Заводские шаблоны для экземпляров сортировщиков безопасны для многопоточности? - PullRequest
0 голосов
/ 20 июля 2011

Во-первых, это хороший заводской образец? Обновление: нет - ISorter технически не соответствует правильному определению для заводского шаблона
Учитывая следующий интерфейс:

public interface ISorter
{
    ISorter Initialize(IEnumerable<int> Source);
    void Sort( );

    string Name { get; }
    IEnumerable<int> SortedList { get; }
}

... и 3 сортировщика - InsertionSort, MergeSort и QuickSort. Вот реализация, использующая MergeSort:

[Export(typeof(ISorter))]
public class MergeSorter : ISorter
{
    int[] leftArray;
    int[] rightArray;

    private MergeSorter(IEnumerable<int> source)
    {
        //  independent copies of array
        leftArray = source.ToArray( );
        rightArray = source.ToArray( );
    }

    public ISorter Initialize(IEnumerable<int> Source)
    {
        return new MergeSorter(Source);
    }

    public void Sort( )
    {   /* call merge sort method */   }

    //    assume the rest of ISorter is implemented
    //    as well as the main Sort( ... ) method
}  

Классы InsertionSort и QuickSort следуют идентичной схеме. Конечно, SortController совершенно не знает, какими Сортировщиками он даже управляет. Он просто предоставляет список SorterNames для всех заинтересованных сторон. Теперь мой вопрос вращается вокруг реализации ISorter.Initialize( ). Я возвращаю вновь созданный объект ISorter с инициализированными внутренними массивами. Предполагается, что SortController может обрабатывать несколько запросов к экземпляру нескольких сортировщиков; Кроме того, метод Sort будет выполняться с помощью метода SortController.StartSorter ( ):

public static void StartSorter(string SorterName, IEnumerable<int> Source)
{
    //  find ISorter by name and initialize
    //  'sorters' is an internal SorterCollection initialized from MEF discovery
    ISorter sorter = sorters.Sorters
                            .Where(key => key.Name == SorterName)
                            .FirstOrDefault( )
                            .Initialize(Source);

    if (sorter == null)
    { return; }

    SortInfo info = new SortInfo
    {
        Collection = sorter.SortedList,
        Invoker = sorter.Sort, //   delegate for asynchronous execution
        Sorter = sorter,
        Timer = new System.Diagnostics.Stopwatch( )
    };

    info.Timer.Start( );
    info.Invoker.BeginInvoke(new AsyncCallback(SortCompleted), info);

    RunningSorters.Add(sorter);
}

Тогда в обратном вызове вы увидите мое единственное событие, определенное следующим образом:

public static event EventHandler<SortCompletedEventArgs> OnSortCompleted = delegate { };

static void SortCompleted ( IAsyncResult iaResult )
{
    //  retrieve SortInfo object from IAsyncResult
    var info = (SortInfo)iaResult.AsyncState;

    //  delegate - EndInvoke
    info.Invoker.EndInvoke ( iaResult );

    //  raise event
    OnSortCompleted ( info, new SortCompletedEventArgs ( info ) );

    //  remove from running sorters list
    RunningSorters.Remove(info.Sorter);
}

Это хороший заводской шаблон для MEF? И является ли это поточно-ориентированная и стабильная реализация, способная обрабатывать несколько сортировщиков (скажем, до 100 000 целых чисел на сортировщик)?

Редактировать
Примечание: MergeSorter требует, чтобы исходный массив был разбит на 2 массива; следовательно, leftArray и rightArray. Как только массив назначен, мы закончили с Source, и его можно свободно изменять.
3 алгоритма сортировки, которые я использовал, были демонстрационными библиотеками, а не моими собственными. Моя собственная процедура сортировки будет использовать метод Compare.
Поскольку это расширяемый SortController, использующий MEF, все алгоритмы сортировки имеют открытые, не имеющие параметров конструкторы, удовлетворяющие требованию создания экземпляров.

1 Ответ

2 голосов
/ 20 июля 2011

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

1) Фабричный интерфейс должен быть простой и отвечать только за создание , имя ISorter нене предположить, что это фабрика

2) ISorter Initialize(IEnumerable<int> Source);Я бы сказал, что метод, который возвращает экземпляр текущего интерфейса, не является лучшей практикой, здесь вы сталкиваетесь с какой-то бесконечной рекурсией

3) IEnumerable<int> SortedList { get; } имя предполагает, что это отсортированный список, которыйна самом деле имя типа в .NET.Такой код может запутать других разработчиков

4) MergeSorter > constructor содержит две копии массива - вам нужно вдвое больше памяти для одного массива, что удваивает потребление памяти (действительно ли это требуется для этого алгоритма сортировки?)

5) Это не потокобезопасно, так как вы используете ToArray () для одного и того же массива (в два приватных поля).Там нет никакой синхронизации, поэтому массив может измениться между этими двумя вызовами, что приведет к неприятным исключениям

Подводя итог

Я бы предложил сделать:

1) тонкий и выделенный заводской интерфейс

2) Переместить функцию сортировки в реализацию IComparer

3) Это требует довольномного рефакторинга, поэтому, если этот код работает, просто добавьте блокировки, в которые вы копируете / удаляете / модифицируете массивы (чтобы сделать их потокобезопасными), и удаляйте (если это возможно) дублированные копии

...