Ищите шаблон дизайна, чтобы заменить huuuge, если на объекте Тип - PullRequest
8 голосов
/ 20 ноября 2008

ОК, поэтому я ищу код, который выглядит примерно так:

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);

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

Ответы [ 6 ]

6 голосов
/ 20 ноября 2008

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

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

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

2 голосов
/ 20 ноября 2008

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

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

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

0 голосов
/ 01 апреля 2010

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

0 голосов
/ 20 ноября 2008

Я думаю, что это больше вопрос для подхода Аспектно-ориентированного программирования.

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

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

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

Кстати, Spring AOP хорош в этом. Я бы прочитал об этом.

0 голосов
/ 20 ноября 2008

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

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

0 голосов
/ 20 ноября 2008

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

...