Pensa a [c1 OR c2] AND [d1 OR d2] come un singolo requisito. Considera c1, c2, d1, d2 come requisiti, diciamo requisiti secondari o requisiti atomici. Ora hanno tutti una cosa in comune. Possono essere controllati se sono soddisfatti. Quindi introduciamo un'interfaccia per questo:
public interface Requirement {
public boolean isMet();
}
Ora i requisiti atomici possono essere implementati come
public class AtomicRequirement implements Requirement {
private final boolean met;
public AtomicRequirement(boolean isMet) {
this.met = isMet;
}
public boolean isMet() {
return true;
}
}
È possibile aggiungere requisiti composti e creare un'interfaccia Requisito.
public class AndRequirement implements Requirement {
private final Requirement r1, r2;
public AndRequirement(Requirement r1, Requirement r2) {
this.r1 = r1;
this.r2 = r2;
}
public boolean isMet() {
return r1.isMet() && r2.isMet();
}
}
Lo stesso vale per OR. Puoi farlo con combinatori più complessi
public class AnyOfTheseRequirement implements Requirement {
private final List<Requirement> anyOfThese;
public AnyOfTheseRequirement(List<Requirement> anyOfThese) {
this.anyOfThese = anyOfThese;
}
public boolean isMet() {
for (Requirement r : anyofThese) {
if (r.isMet()) return true;
}
return false;
}
}
e, naturalmente
public class MakseeRequirement implements Requirement {
private final AndRequirement r;
public MakseeRequirement(Requirement c1, List<Requirement> dns) {
AnyOfTheseRequirement d = new AnyOfTheseRequirement(dns);
this.r = new AndRequirement(c1, d);
}
public boolean isMet() {
return r.isMet();
}
}
Questo approccio è abbastanza estensibile, ma puoi ridurlo ai tre tipi effettivi di requisiti che hai ottenuto.