Методы, интерфейсы, отражение и сложный дизайн ООП - PullRequest
0 голосов
/ 21 января 2009

Я не совсем уверен, как это объяснить (вот почему название довольно странное), но я попробую. В основном я занимаюсь Oject-Oriented Design, и я хочу представить различные типы объектов, каждый из которых может иметь различные действия, которые он может выполнять. Пример может помочь: такие вещи, как Файл, который может иметь действия по удалению, переименованию и открытию, и Приложение, которое может запускать, закрывать, удалять и перемещать к другим действиям монитора.

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

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

Нужно ли это делать с отражением? Я почти уверен, что это невозможно сделать со стандартными интерфейсами, но я немного новичок во всем этом дизайне ООП, поэтому я не совсем уверен.

Ответы [ 6 ]

7 голосов
/ 21 января 2009

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

Чтобы ответить на ваш конкретный вопрос, вам следует быть здесь немного осторожнее, поскольку вы можете входить на территорию Архитектурный астронавт (не очень хорошая вещь). Если интерфейсы для каждого из ваших объектов не перекрываются естественным образом, то принуждение их делиться концептуальным интерфейсом через нечетные махинации просто приведет вас в замешательство. Вы можете решить эту проблему, внедрив интерфейсы, которые вызывают "достаточно близкую" функцию для очень широких концептуальных категорий (например, файлы и приложения оба "открыты", но результат у каждого разный). Вы также можете использовать отражение, чтобы узнать больше о ваших объектах в том месте, где вам нужно отобразить доступные опции. Таким образом, вы можете сделать это. Вопрос в том, должен вы это делаете?

2 голосов
/ 21 января 2009

Я согласен с предупреждением Марка долго и усердно думать о том, зачем вам это нужно. Вызов методов / действий, личность которых неизвестна до тех пор, пока среда выполнения не покажется мне плохой идеей.

Но если нужно, вот еще одна мысль: создайте свой интерфейс IAction с помощью метода getActions(), который возвращает список указателей на функции или что-то в этом роде. Если у вас нет эквивалента указателей на функции на вашем языке, попросите getActions() вернуть список строк и добавить метод callAction(string) в интерфейс IAction, который каждый класс реализует для вызова соответствующего метода на основе параметра. Это может быть немного быстрее, чем при использовании встроенного механизма отражения.

2 голосов
/ 21 января 2009
public interface IDiscoverableAction {
    string Name { get; }
    void Execute();
}

public interface IHasDiscoverableActions {
   IEnumerable<IDiscoverableAction> DiscoverActions();
}
0 голосов
/ 24 января 2009

Может быть, вы ошиблись. Должны ли объекты действительно реализовывать эти действия?

Взять файл. Разве не естественнее, когда объект FileSystem реализует действие удаления?

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

Допустим, вы реализуете DeleteAction как UnaryOperation в зависимости от Deletable.

Добавление DeleteAction в ActionRepository регистрирует его как доступный для удаляемых объектов.

Если вы хотите избежать статической привязки File к Deletable, вы можете добавить AdaptorRepositor, где вы добавите адаптер File2Deletable.

ActionRepository.listActions (Object o) затем предоставит вам список всех действий, которые можно напрямую использовать с Файлом или любым из доступных адаптеров Файла.

0 голосов
/ 24 января 2009

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

Предполагая, что это правильно, вы можете использовать наследование для наследования от базового класса, например, FrameworkObject, или вы можете реализовать общий интерфейс, например IFrameworkObject. Вы можете использовать интерфейс как не что иное, как полиморфный «маркерный» интерфейс. Это было необходимо при программировании компонентов ActiveX в тот же день. Я предпочитаю интерфейс маркера, потому что наследование классов не является чем-то тривиальным с точки зрения жизненного цикла рефакторинга фреймворка, и он может иметь тенденцию создавать код с ошибками из-за раздувания базового класса, так как добавляются некоторые функции, но не все, производные классы нужны.

Если у вас есть общий механизм, т. Е. Контейнер типа через реализацию интерфейса для каждого из классов, вы можете использовать отражение или, возможно, просто оператор "is" (в C #), чтобы выяснить, реализует ли конкретный объект конкретный интерфейс, который относится, например, к объекту File или к приложению.

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

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

0 голосов
/ 21 января 2009

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

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

Возможно, есть случаи, когда несвязанные классы могут иметь одну или две общие черты. В этом случае вы можете попытаться определить интерфейс для идентификации этого. Пример: ISupportsDelete. Работа с вещами на этом гранулярном уровне - единственный способ, с помощью которого вы сможете обрабатывать несвязанные объекты, подобные этому, любым типичным способом.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...