I have a class in charge of responding to an input event and maybe triggering another event. To decide, it has several rules to check.
I'm trying to get away from a class looking like this:
public class Translator {
public void maybeTranslate() {
if (!condition1 || !condition2 || !condition3) {
return;
}
// All rules have passed
translate();
}
}
I'd like this class to know that it has rules to pass, but not know about the implementations of rules. My attempt was to transform it like so:
public class Translator {
private Rule[] rules;
public Translator (Rule[] rules) { this.rules = rules; }
public void maybeTranslate() {
for (Rule rule : rules) {
if (!rule.passes()) {
return;
}
}
translate();
}
}
where a Rule
has this interface:
public interface Rule {
boolean passes();
}
Here comes my problem: I now have a new rule that takes in a parameter. Its passes
method would require a String
parameter. Is my foundation wrong with this new requirement? I don't see how I can adapt this pattern to accommodate rules having various parameters.
This parameter would indicate the source of the input event. I can modify maybeTranslate
to receive this value from whoever calls it, and change the Rule
interface to be boolean passes(String str)
. Only now, the majority of my rules are getting a parameter they don't care about.
This is in the context of an Android app, where Translator
would be instantiated on different fragments.
UPDATE: Some changes from my original question.
This Translator
will be used on multiple screens. There is only one screen active at a time, and each screen can have a different set of rules.
Instead of making a rule take in a parameter and act differently, I created multiple screen-dependent rules (ScreenOneRule, ScreenTwoRule, etc.).
Now I would use the class like this:
public class ScreenOne {
Translator translator;
public Screen(Translator translator) { ... }
public void onSomeEvent() { translator.maybeTranslate(); }
}
and I would create the set of rules for ScreenOne in my IoC configuration (this is an attempt at pseudo code):
Rule[] screenOneRules = new Rule[] { SharedRule1, SharedRule2, ScreenOneRule1 }
ScreenOne screenOne = new Screen(new Translator(screenOneRules))
This solves my problem, though I feel like the design is not perfect. What if I get a rule later on that depends on a value only known after the screen has been instantiated? For example, say a rule needs to know the (x,y) coordinates of where the user tapped on screen to trigger onSomeEvent. I have a feeling ScreenOne
would need to construct the rules itself in onSomeEvent
. I think this is coming back to my original question.
In that case, I would want to have the instantiation of rules somewhere else. Is the solution to have a factory like this?
public class RuleFactory {
public Rule[] getRulesForScreenOne(int x, int y, String anotherParameter) {
return new Rule[] { new SharedRule1(x, y), new SharedRule2(anotherParameter) };
}
}
public void onSomeEvent() {
Rule[] rules = RuleFactory.getRulesForScreenOne(x, y, str);
translator.maybeTranslate(rules);
}
At that point, Translator would not receive rules in its constructor. I can't put my finger on it, but I feel like the factory implementation isn't right. Something about that method name.