2016-04-01 6 views
1

У меня есть ситуация, когда у меня есть IEnumerable, который мне нужен для повторения и выполнения некоторого кода для каждого элемента.Полиморфное расширение для сторонних классов

Этот код, который должен выполняться, зависит от фактического типа элемента, и то, что я ищу, является хорошим чистым способом сделать это без каких-либо условностей, поэтому, если количество производных типов, которые мне нужно обрабатывать, увеличивается. I просто нужно написать новый обработчик и не изменять какой-либо из моего существующего кода.

Чтобы проиллюстрировать это, я имею пример, где третья библиотека сторона содержит следующий код:

public abstract class BaseObject{ } 

public class Derived1: BaseObject { } 

public class Derived2 : BaseObject { } 

и мой местный код делает что-то вроде:

void Execute(IEnumerable<BaseObject> list) 
{ 
    foreach (var item in list) 
     item.DoWork(); 
} 

Я попытался создать методы расширения для этого:

public static class Extensions 
{ 
    public static int DoWork(this BaseObject obj) { return 0; } 

    public static int DoWork(this Derived1 obj) { return 1; } 

    public static int DoWork(this Derived2 obj) { return 2; } 
} 

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

У меня есть возможность создания нового производный типа для каждого производного типа всех реализации интерфейса:

public class MyDerived1: Derived1, IMyInterface 
{ 
    public int DoWork(){return 1;} 
} 

public class MyDerived2: Derived2, IMyInterface 
{ 
    public int DoWork(){return 2;} 
} 

public interface IMyInterface 
{ 
    int DoWork(); 
} 

тогда я мог бы итерацию и приведение к новому интерфейсу:

void Execute(IEnumerable<BaseObject> list) 
{ 
    foreach (var item in list) 
    { 
     var local = item as IMyInterface; 
     local?.DoWork(); 
    } 
} 

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

Очевидно, что использование фабричного шаблона здесь и его самое чистое решение, которое у меня есть до сих пор.

Паттерн команда предлагает решение, но в качестве команды для выполнения зависит от типа производного типа, условное по-прежнему необходимо, чтобы построить эту команду ...

Можно ли предложить такой подход, который бы полностью устранить любые условные условия, которые нуждаются в изменении при добавлении новых производных типов в микс?

+0

Вы можете создать словарь при запуске, с помощью отражения, что позволило бы вашему заводу построить право производного типа без необходимости какого-либо условного, и карта будет автоматически включать любые новые производные типы, которые были добавлены без необходимости модификации , – Baldrick

+0

благодарит @Baldrick, это подход, взятый в ответе в этом посте http://stackoverflow.com/questions/27724130/simulate-inheritance-by-extension. Честно говоря, я старался избегать размышлений. – MikeW

+0

Я не уверен, что это возможно. Необходимая информация о базовом типе объекта BaseObject не может быть показана программно без: (a) виртуального метода вызывает сам объект для переопределенного метода (но он является сторонним и не может быть изменен) или (b), отражающие, затем используя условные обозначения. Не уверен, что в вашей ситуации есть другой путь. – Baldrick

ответ

0

Одно из возможных решений использует структуру композиции, я выбрал MEF для моего примера.

Что вам нужно сделать, это создать обработчик, которые реализуют общий интерфейс, каждый из которых состоит содержатся в композиции контейнере с именем контракта, совпадающим с именем производного типа:

public interface IBaseObjectWorker 
{ 
    int DoWork(BaseObject obj); 
} 

[Export(contractType: typeof(IBaseObjectWorker), contractName: "UnitTestProject1.Derived1")] 
public class DerivedObject1Worker : IBaseObjectWorker 
{ 
    public int DoWork(BaseObject obj) 
    { 
     return 1; 
    } 
} 

[Export(contractType: typeof(IBaseObjectWorker), contractName: "UnitTestProject1.Derived2")] 
public class DerivedObject2Worker : IBaseObjectWorker 
{ 
    public int DoWork(BaseObject obj) 
    { 
     return 2; 
    } 
} 

, то вы можете получить обработчик из контейнера композиции и использовать его:

void Execute(IEnumerable<BaseObject> list) 
{ 
    foreach (var item in list) 
    { 
     var worker = DIContainer.Instance.GetObject<IBaseObjectWorker>(item.GetType().ToString()); 
     worker?.DoWork(item); 
    } 
} 

так, чтобы расширить это, чтобы справиться с новым производным типом будет простой означать создание нового типа обработчика.Если контейнер компоновки заполнен всеми типами сборки, новый обработчик будет там, как только он будет создан, и начнет обрабатывать новый производный тип без изменения текущего кода. Таким образом, соблюдая открытый-закрытый принцип.

Это своего рода реализация фабричного шаблона, но без необходимости беспокоиться о реализации завода.

как сторона не DIContainer - это моя личная реализация обертки одного шаблона вокруг контейнера MEF, это заполняется при запуске приложения, а GetObject (имя строки) просто вызывает GetExportedValue (string contractName).

0

Вы можете использовать природные особенности полиморфизма.

class Class1 
{ 
    public virtual void DoThis() 
    { 
     Console.WriteLine("Class one"); 
    } 
} 

class Class2 : Class1 
{ 
    public override void DoThis() 
    { 
     Console.WriteLine("Class two"); 
    } 
} 

class Class3 : Class1 // or Class2 
{ 
    public override void DoThis() 
    { 
     Console.WriteLine("Class three"); 
    } 
} 


    private static void Main(string[] args) 
    { 
     Class1 class1 = new Class1(); 
     Class2 class2 = new Class2(); 
     Class3 class3 = new Class3(); 
     class1.DoThis(); 
     class2.DoThis(); 
     class3.DoThis(); 
    } 

И так далее.

private static void Main(string[] args) 
{ 
    Class1 class1 = new Class1(); 
    Class2 class2 = new Class2(); 
    Class3 class3 = new Class3(); 
    class1.DoThis(); 
    class2.DoThis(); 
    class3.DoThis(); 
} 

Следовательно, нужно было бы быть не обращение, какой тип объекта он, она, естественно, попадают в перегруженной метод класса.

Текущий маршрут, который вы берете, требует некоторого изучения типа объекта, который станет беспорядочным, поскольку добавляются более производные классы и, скорее всего, потребуются некоторые условные утверждения где-то в коде, чтобы определить, куда вы хотите, чтобы ваша программа протекала , Используя переопределенный метод для каждого производного класса, он автоматически возьмет вашу программу, где бы вы хотели.

К моему способу мышления это лишнее увеличение рабочей нагрузки путем попытки повторения граней языка, относящихся к ООП. Иногда более простой подход может быть лучше и элегантнее.

+0

Вы здесь считаете, что «код сторонней библиотеки» может быть изменен. – Maarten

+0

@Maarten ах да, я. Мне понравилось ваше решение динамичного. Я думаю, что это проще, чем словарь, как упоминалось в комментариях. –

4

Вы можете использовать dynamic. Обычно я не поклонник этого, но в этом случае это хороший вариант.

public static class Worker 
{ 
    public static int DoWork(BaseObject obj) { return 0; } 
    public static int DoWork(Derived1 obj) { return 1; } 
    public static int DoWork(Derived2 obj) { return 2; } 
} 

void Execute(IEnumerable<BaseObject> list) { 
    foreach (dynamic item in list) { 
     Worker.DoWork(item); // Method resolution done at run-time 
    } 
} 
+1

Эффективно по-прежнему использует «отражение», но, по крайней мере, он скрыт без нагрузки нужного кода. +1 от меня. – Baldrick

+0

Я думаю, что если OP не может изменить классы, это хорошее решение +1 –

Смежные вопросы