2008-11-20 4 views
8

ОК, так что я смотрю на некоторый код, который выглядит примерно так:Глядя на дизайн шаблона, чтобы заменить huuuge если на объекте Тип

void DoSomething(object o) 
{ 
    if (o is Sometype1) { 
    //cast o to Sometype and do something to it 
    } 
    else if (o is Sometype2) { 
    //cast o to Sometype2 and do something to it 
    } 
    ... 
    else if (o is SometypeN) { 
    //cast o to SometypeN and do something to it 
    } 
} 

Теперь один подход должен был бы сделать все объекты, которые o используются в качестве параметров реализации интерфейса, как

interface ICanHaveSomethingDoneToMe 
{ 
    //expose various properties that the DoSomething method wants to access 
} 

Но проблема состоит в том, что я не хочу, чтобы все мои объекты для реализации этого интерфейса - логика для метода делать что-то на самом деле не принадлежит им. Какую схему я должен использовать, чтобы справиться с этим?

Я подозреваю, что-то вроде серии реализаций

interface IPropertiesForDoingSomethingTo<T> 
{ 
    //expose various properties that the DoSomething method wants to access 
} 

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

IPropertiesForDoingSomethingTo<T> GetPropsGeneric(T t); 

, но нужно ли здесь массово переключиться? Должен ли я определить класс с нагрузками методов, как

IPropertiesForDoingSomethingTo<Someobject1> GetProps(Someobject1 t); 
... 
IPropertiesForDoingSomethingTo<Someobject1> GetProps(SomeobjectN t); 

Это проблема по сравнению с общей версией, что вы не смогли бы добавлять новые типы во время выполнения. Ts есть что-то хитрое, которое можно сделать с контейнером DI в GetPropsGeneric для разрешения контейнера? Благодаря!

ответ

6

В любое время, когда вы видите оператор switch (или ряд операторов if), проверяющий тип объекта, это большой красный флаг для отсутствующего базового класса или интерфейса. Другими словами, код должен полагаться на полиморфизм, а не на тестирование типа объекта

, если вы не можете изменить базовый класс или реализовать интерфейс, вероятно, у вас есть словарь для имитации динамической диспетчеризации. В C# вы можете использовать анонимный делегат для метода, который включает в себя листинг

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

+1

Вы можете реализовать оболочку вокруг любого класса, который вы не можете изменить, и реализовать его интерфейс. Затем обрабатывайте только те объекты, которые были обернуты. Я делаю это с помощью DataContext, например, чтобы упростить модульный тест. – tvanfosson 2008-11-20 18:00:30

2

Похоже, вы используете C#. Я считаю, что вы можете создать «методы расширения», которые присоединяются к уже установленным классам.

Другим подходом было бы создание делегатов-обработчиков для каждого типа и сохранение ссылок на делегаты в хэш-таблице с ключом по типу объекта.

Тогда ваш метод «DoSomething» мог бы просто найти подходящего делегата по типу переданного и выполняемого объекта.

0

Настоящим примером было бы более полезным. Если вы просто меняете реализацию метода для семейства связанных классов, то, как говорит @Steven A. Lowe, вам лучше использовать полиморфизм и использовать для этого отношения подкласса. Если классы не участвуют в отношениях «есть», то более подходящими могут быть другие шаблоны, такие как Visitor.

0

Полиморфизм - это ответ, в котором передается базовый объект. Однако это значительно более сложная и семантическая сложность, чем вы хотите.

Это потребует, чтобы ваши функции как-то были шунтированы в производные классы и «виртуальную» функцию, которая будет реализована.

0

Я думаю, что это скорее вопрос для подхода с ориентацией на ориентированный на ASP.

Я бы хотел, чтобы ваш метод DoSomething принял в качестве параметра интерфейс ICanHaveSomethingDone. Затем я определяю интерфейс ICanHaveSomethinhgDone и получаю от него подклассы (по одному для каждого объекта, который вы хотели бы сделать до DoSomething), который реализует DoSomethingToMe, по-разному для каждого класса реализации. Каждый из них просто принимает конструктор типа, в котором вы хотите что-то сделать, так что, когда вы идете на вызов DoSomething, вы фактически вызываете Factory (очень просто, просто создавая экземпляр вашего класса ICanHaveSomethingDone из типа ввода в создайте экземпляр класса, который реализует метод DoSomethingToMe и который имеет правильный код для базового объекта.

По существу, подумайте об этом так: вы определяете контракт интерфейса, который вы хотите реализовать объектами параметров и определение «украшения» в виде подклассов вашего объекта и интерфейса, которые реализуют конкретные реализации поведения интерфейса (поэтому выполняют контракт) для вашего конкретного класса. Таким образом, вы можете сделать свою реализацию метода DoSomething для каждый из классов, полностью отделенный от источника этих разделов s.

Еще одна вещь: это означает, что вы можете добавлять новые типы во время выполнения, если вы делаете свою фабрику в контейнер DI; введя в свой заводской контейнер новые типы, с которыми вы хотели бы что-либо сделать, до тех пор, пока у вас есть реализация действия, которое вы хотите принять, определяемого как класс, полученный из вашего интерфейса и из этого класса , ты в порядке. Вы даже можете определить поведение во время выполнения без реализации производного класса, если вы реализуете полный подход АОП; определяя ваш интерфейс, определяя поведение вашего поведения и определяя реализацию ваших производных классов для компоновки вместе во время выполнения поведения, которое вы хотите, с объектом, в котором вы проходите. Но это сложно ... :-)

Кстати, Spring AOP отлично относится к этому материалу. Я читал об этом.

0

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