So I want to do the following and I'm really just looking for advice on how to design it.
- I have
Filter
s which can be run on certain entity types Filter
s haveRule
s to define their behaviorFilter
s process entities, using their rules, and return a boolean- As a concrete example I may want to filter which says that some regular expression on a given field is rejected, and I can define rules for it. I could also have different rules that use simple prefix matching in the same filter.
So I've come up with the following
interface FilterInterface
{
/**
* Load the rules for this filter
*/
function loadRules();
/**
* Filter an entity in an optional context
* @param mixed $entity Should return true for $this->supportsEntity($entity)
* @param array $context Other information
*
* @return boolean True if the $entity passes this filter
*/
function filter($entity, $context = array());
/**
* Check if filter supports the entity type
* @param mixed $entity
*
* @return boolean true if this Filter can be run for that entity type
*/
function supportsEntity($entity);
}
Then I plan to make:
class ImageFilter implements FilterInterface
Which will load rules from the database and implement the filter.
So initially I though of having an Entity like
FilterRule
:
- scope (enum type to define which filter uses this)
- type (can have different types of rules, like the regex vs. prefix I mentioned)
- value (string, depends on the type of rule)
And ImageFilter
would load FilterRule
s where the scope is set to some constant. Then ImageFilter
would do the heavy lifting of checking the type of filter and applying it to the entity, one after another. The following psuedocode demonstrates this.
public function filter($entity)
{
foreach ($this->rules as $rule) {
switch($rule->getType()) {
case RULE_TYPE_REGEX:
$this->doRegex();
break;
case RULE_TYPE_PREFIX:
$this->doPrefix();
break;
}
}
}
But then I wondered, should the entity be responsible for the above switch
block? So it would become something like:
public function filter($entity)
{
foreach ($this->rules as $rule) {
$rule->process($entity);
}
}
This seems cleaner to me, the Filter
doesn't care about new rules and how to handle them as long as the "scope" is correct. But I was under the impression that entities should be simple POPO so it seems like having this kind of logic is a bad pattern (but I was never 100% sure on how much an entity should really do).
I had also considered using Inheritance Mapping so each Rule
"type" could be a different class and it's logic would be pretty simple (only a couple of lines at most). Finally, I was thinking maybe the different type of Rule
should somehow use composition to have a "behavior" or something which would do the actual work...
So to summarize, here are my current thoughts:
Filter
does all the work checking the type ofRule
and acting accordingly.Rule
is just a "dumb" entity to hold some stringsRule
does all the work of checking its own type and doing something to a given entity. Now theRule
entity is a bit "fatter" but it hides the implementation fromFilter
andFilter
doesn't need to change to add a newRule
typeRule
uses inheritance to do the above, effectively the same idea but no moreswitch
statement- Either 2. or 3. but
Rule
instantiates a different "behavior" object for each type (not really using dependency injection seems bad, encapsulating the behavior seems like a good design though) - Combine 1. and 4. so the
Filter
can get the proper behavior from theRule
(can now use dependency injection with like a "behavior" factory)
I guess I'm leaning more toward the last few options the more I think about it, but I don't know the best practices in this area. I'm sure this is a relatively common example so I'm hoping to get some input on which direction to take. (Sorry for such a long question, hopefully it's complete enough to answer)