Я работаю над 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 в интерфейс и использовать множественное наследование интерфейса, но в сущности возникает та же проблема. Я должно быть что-то упустил.