I am working on an MVC framework in PHP but I work with java primarily and I am looking for a solution to this problem which adheres to OOP principles and is transferable to other languages.
I have a model abstract class that interacts with some data (database, xml, whatever). Inheriting classes have to implement these methods:
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();
The controller passes a Map object which holds information about an item that is to be inserted, updated, deleted etc. This means that the model and controller are decoupled and any Model can be injected into a controller provided there is an implementation of these methods.
When using this class in practice I have found that there have been instances where the operation needed by a controller is highly unique such as re-ordering data in a specific way. This is a BAD solution:
abstract public function reorder(Map $item);
This solution means that EVERY Model must implement this method which isn’t necessary. Also Imagine if I needed other methods, the amount of abstract methods would simply grow and grow, each needing an implementation.
Another solution would be this:
abstract public function action(Map $item, $action)
The $action variable would be a string that defines an action. So you could implement different methods but only call them with the polymorphic action() method:
if ($action === "reorder") { $this->reorder($item); }
The only problem with this solution is that the correct commands are not obvious from the method signature. For example the $action string could be anything and another developer would have to examine the method body (implementation) to find the admissible strings. Simply stating them in the documentation seems a flimsy solution. Also, what if a model is injected into a controller which doesn’t implement all the required actions? Throw an exception?
It seems like I must be missing some kind of really obvious solution and I don’t want to go ahead and implement the one above and then have to heavily refactor later when I find a better one. Any ideas?
Edit:
Using multiple interfaces seems to be the best solution so far. There is a type safety issue though. If I am to inject a Model which implements interface ReOrderable into my Controller class I want to be able to do __construct(Any Model that implements these interfaces $model). I could make more abstract classes such as ReOrderableModel and then do __construct(ReOrderableModel $model) but there could be any number of combinations of interfaces and I would have to define an additional abstract class for each one. I could also turn Model into an interface and use multiple interface inheritance but the same problem essentially arises. I must be missing something.
I would define a,
IReOrderableinterface, and have your actual Model class implement this interface. It doesn’t need to be done at the abstract class level if it is not meant for all inheriting classes.I use one
abstract Modelclass, and then a ton ofInterfacesfor the actualModelimplementations.