MVC: Как мне уменьшить количество полиморфных методов? - PullRequest
1 голос
/ 10 марта 2012

Я работаю над MVC-фреймворком в PHP, но в основном я работаю с Java, и я ищу решение этой проблемы, которое соответствует принципам ООП и может быть перенесено на другие языки.

У меня есть модельный абстрактный класс, который взаимодействует с некоторыми данными (база данных, xml и т. Д.). Наследующие классы должны реализовывать эти методы:

abstract class Model {

abstract public function nextItem();

abstract public function insert(Map $item);

abstract public function update(Map $item);

abstract public function delete(Map $item);

abstract public function exists(Map $item);

abstract public function countItems();

abstract public function allItems();

Контроллер передает объект Map, который содержит информацию об элементе, который должен быть вставлен, обновлен, удален и т. Д. Это означает, что модель и контроллер разъединены, и любая Модель может быть введена в контроллер при условии реализации эти методы.

При использовании этого класса на практике я обнаружил, что были случаи, когда операции, необходимые для контроллера, были совершенно уникальными, такими как переупорядочение данных особым образом. Это ПЛОХОЕ решение:

abstract public function reorder(Map $item);

Это решение означает, что КАЖДАЯ Модель должна реализовывать этот метод, который не является необходимым. Также представьте, что если бы мне были нужны другие методы, количество абстрактных методов просто росло бы и росло, каждый из которых нуждался в реализации.

Другое решение будет таким:

abstract public function action(Map $item, $action)

Переменная $ action будет строкой, которая определяет действие. Таким образом, вы можете реализовать разные методы, но вызывать их только с помощью метода polymorphic action ():

if ($action === "reorder") { $this->reorder($item); }

Единственная проблема этого решения состоит в том, что правильные команды не очевидны из сигнатуры метода. Например, строка $ action может быть чем угодно, и другому разработчику придется изучить тело метода (реализацию), чтобы найти допустимые строки. Просто указав их в документации, кажется хрупким решением. Кроме того, что, если модель вводится в контроллер, который не выполняет все необходимые действия? Бросить исключение?

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

Edit: Использование нескольких интерфейсов кажется наилучшим решением. Однако существует проблема безопасности типов. Если я хочу внедрить Модель, которая реализует интерфейс ReOrderable, в мой класс Controller, я хочу иметь возможность делать __construct (Любая Модель, которая реализует эти интерфейсы $ model). Я мог бы создать более абстрактные классы, такие как ReOrderableModel, а затем сделать __construct (ReOrderableModel $ model), но может быть любое количество комбинаций интерфейсов, и мне пришлось бы определять дополнительный абстрактный класс для каждого из них. Я также мог бы превратить Model в интерфейс и использовать множественное наследование интерфейса, но в сущности возникает та же проблема. Я должно быть что-то упустил.

Ответы [ 3 ]

1 голос
/ 10 марта 2012

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

Я использую один abstract Model класс, а затем тонну Interfaces для фактических Model реализаций.

1 голос
/ 10 марта 2012

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

Если вы используете ваше второе решение, где 2-й аргумент указывает на действие, которое нужно предпринять, я настоятельно рекомендую Enum. (Есть ли у php аналог ???) Другой программист облегчает поиск возможностей, и он определенно избегает многих ошибок.

0 голосов
/ 10 марта 2012

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

Чтобы проиллюстрировать вышеизложенное, вы можете иметь

 public void function reorder(Map $item){
     //this is a concrete function that does nothing
 }

Поскольку функция не является абстрактной, ее не нужно переопределятьдети.

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