return
(Container)
aContainer.getFeatures().contains(requiredFeature);
}
}
Now, let's extend the
Specification
interface by adding the three new operations:
public
interface Specification {
boolean isSatisfiedBy(Object candidate);
Specification and(Specification other);
Specification or(Specification other);
Specification not();
}
Recall that some
Container Specifications
were configured to require ventilated
Containers
and
others
to require armored
Containers
. A chemical that is both volatile
and
explosive would,
presumably, need
both
of these
SPECIFICATIONS
. Easily done, using the new methods.
Specification ventilated = new ContainerSpecification(VENTILATED);
Specification armored = new ContainerSpecification(ARMORED);
Specification both = ventilated.and(armored);
The
declaration defines a new
Specification
object with the expected properties. This combination
would have required a more complicated
Container Specification
, and would still have been
special purpose.
Suppose we had more
than one kind of ventilated
Container
. It might not matter for some items
which kind they were packed into. They could be placed in either type.
Specification ventilatedType1 =
new ContainerSpecification(VENTILATED_TYPE_1);
Specification ventilatedType2 =
new ContainerSpecification(VENTILATED_TYPE_2);
Specification either = ventilatedType1.or(ventilatedType2);
If it was considered wasteful to store sand in specialized containers, we could prohibit it by
SPECIFYING
a "cheap" container with no special features.
Specification cheap = (ventilated.not()).and(armored.not());
This constraint would have prevented some of the suboptimal
behavior of the prototype
warehouse packer discussed in Chapter 9.
The ability to build complex specifications out of simple elements increases the expressiveness of
the code. The combinations are written in a declarative style.
Depending on how
SPECIFICATIONS
are implemented, these operators
may be easy or difficult to
provide. What follows is a very simple implementation, which would be inefficient in some
situations and quite practical in others. It is meant as an
explanatory example
.
Like any pattern,
there are many ways to implement it.
public abstract class AbstractSpecification implements
Specification {
public Specification and(Specification other) {
return new AndSpecification(this, other);
}
public Specification or(Specification other) {
return new OrSpecification(this, other);
}
public Specification not() {
return new NotSpecification(this);
}
}
public class AndSpecification extends AbstractSpecification {
Specification one;
Specification other;
public AndSpecification(Specification x, Specification y) {
one = x;
other = y;
}
public boolean isSatisfiedBy(Object candidate) {
return one.isSatisfiedBy(candidate) &&
other.isSatisfiedBy(candidate);
}
}
public class OrSpecification extends AbstractSpecification {
Specification one;
Specification other;
public OrSpecification(Specification x, Specification y) {
one = x;
other = y;
}
public boolean isSatisfiedBy(Object candidate) {
return one.isSatisfiedBy(candidate) ||
other.isSatisfiedBy(candidate);
}
}
public class NotSpecification extends AbstractSpecification {
Specification wrapped;
public NotSpecification(Specification x) {
wrapped = x;
}
public boolean isSatisfiedBy(Object candidate) {
return !wrapped.isSatisfiedBy(candidate);
}
}
Do'stlaringiz bilan baham: