views:

127

answers:

1

I'm implementing the specification pattern. The NotSpecification seems simple at first:

NotSpecification.IsSpecialCaseOf(otherSpecification)
    return !this.specification.isSpecialCaseOf(otherSpecification)

But it doesn't work for all Specifications:

Not(LesserThan(4)).IsSpecialCaseOf(Equals(5))

This should return false instead of true. So far I think that the only way to accomplish the isSpecialCaseOf the NotSpecification is to implement the remainderUnsatisfiedBy (partial subsumption in the paper on the specification pattern). But maybe I am missing something more simple or a logical insight that makes this unnecessary.

Question: Is there another way of implementing this by not using remainderUnsatisfiedBy?

+2  A: 

I have tried to implement this in Java and it went without problems and remainderUnsatisfiedBy(). Probably you have some problem in your implementation, here is mine:

public boolean isSpecialCaseOf(Specification spec) {
    if (spec instanceof GreaterThan) {
        return ((GreaterThan) spec).boundary > this.boundary;
    }
    return false;
}

The catch is in the Not() method, which should correctly construct opposite type of its argument.

static final Specification Not(Specification spec) {
    return spec.not();
}

Then all I need is to have correct implementation of not() for every Specification, e.g. for LesserThan:

    @Override
public Specification not() {
    return new GreaterThan(boundary);
}

If you have any problems, please provide your implementation of GreatherThan.isSpecialCaseOf and of Not, I will try to help.

Gabriel Ščerbák
Comparisons between GreaterThan/LesserThan/Equals works as expected. They are not the problem. The problem is the NotSpecification.isSpecialCaseOf. What is your implementation of that, and how does it solve the problem case I stated above?
koen
I use a trick, I have no NotSpecification, instead I have defined in Specification abstract class a static method Not(), which returns spec.not() for given Specification. Than in LesserThan.not I simply return GreaterThan with the same boundary and it works. So instead of defining allmighty NotSpecification, I have every Specification type to know how to negate itself.
Gabriel Ščerbák
If it is still unclear, I can send you full source.
Gabriel Ščerbák
Thanks, I understand it now. I remember I have played with the thought but I had it rejected for some reason (which may have been bad or good reason, don't know anymore). I'll certainly take this as an option.
koen
Tell why this would not be a valid solution and I will try to help. I think I can help you in a way worth the reputation you offered.
Gabriel Ščerbák
A case where this approach wouldn't work: decide wether a post fits the specification inCategory(nameOrId). The reason it won't work is that the specification doesn't know what all other categories are (and it would be bad design anyway): following the suggested approach, not in category('programming') must return an orSpecification containing a specification inCategory for all remaining categories.
koen
But this is a problem ,which cannot be solved. From formal logic, either you avoid negation or you need a way to get the "universe" of all possible other specifications for a specific specification type. IMHO good design would be to either include that information into the concrete specification class as I proposed or to create a service, which will do that for all concrete specifications.
Gabriel Ščerbák
BTW. the paper by Fowler suggestes also conversion to same type of specification for subsumption.
Gabriel Ščerbák
There really is not a way to write isSpecialCaseOf in a general manner, because firstly as I mentioned and referenced in the paper, you have to have specifications of the same type to implement isSpecialCaseOf and that is dependent on the type of the specification and the argument of isSpecialCaseOf. This design means, that there should be some service - maybe subclass of NotSpecification, which will convert wrapped Specification based on the argument of isSpecialCaseOf. The coupling will be between that NotSpecification subclass and corresponding specification types.
Gabriel Ščerbák
I don't think the paper is thorough enough as the numerical range specifications show. My implementation currently has isSpecialCaseOf look for isSpecialCaseOfSubclass, else it returns false. So LesserThan and Equals know of each other. The notSpecification, I'm currently trying to solve that with remainderUnsatisfiedBy. If A.remainderUnsatisfiedBy(B) == A, then Not(A) is special case of B. But remainderUnsatisfied seems to have problems of its own (especially leafSpecification.isSatisfiedBy(AndSpecification))
koen
The remainderUnsatisfiedBy is the remainder of the "universe" when you take out the A. I think not() method for specifications, which will return new negated specification has some advantages over your solution. First of all, you will need negation anyway. The second point is, that for example for the InCategorySpecification example you proided, you can create NotInCategorySpecification which will in isSatisfiedBy check for inequality of the categories in those specifications. With the remainderUnsatisfiedBy you would have to create large OrSpecification with all categories as you suggested.
Gabriel Ščerbák
If there still is a problem or any other reason for not accepting this solution, just comment, I will do my best.
Gabriel Ščerbák
I appreciate the effort you have put in and the bounty will be yours by number of votes (mine included). I don't see why the remainder of the universe is a problem. Suppose I want to select objects where property A is not greater than 5, objects with property A = a car fit. It fits my goal so it's not a problem (not meaning: let it be anything but not a number greater than 5). I think I also may solve the problem (true not specifications) if leaf specifications implement the method haveElementsInCommon, which is simpler than remainderUnsatisfied.
koen
IMHO not fair from you to not accept my answer. Show me than how do you intend to implement e.g. OddNumberSpecification vs. EvenNumberSpecification - they would need to know about each other or they must be some Mediator. haveElementsInCommon will be also complicated, just imagine PrimeNumberSpecification and SatisfiesSomePrimalityTestSpecification. It is all about non trivial infinite sets.
Gabriel Ščerbák
I don't accept the answer because I'm trying to find a way to implement it with 'real' NotSpecifications. I'm still convinced it is possible. You are right in that my design won't be optimal. Numerical specifications will have to know about each other: isSpecialCaseOf looks for a method like isSpecialCaseOfOtherClass. I hope the design flaw will be limited because only related specifications will know about each other. I know I won't be able to handle all cases that way, but I think your solution can't do this also (eg equals(5) seems already a problem to negate and compare against some).
koen
My solution can be easily integrated into one class called NotSpecification. The point I am trying to communicate is, that the complex part is inherent in the problem, you cannot create easy solution. For specifications of attributes which can have only finite number of values, you can test for inclusion. For infinite set of possible values, you need to have one normal form, instances of which could be easily compared.
Gabriel Ščerbák
Equals(5) is an example of specification with infinite set of possible values (e.g. for integers). This means you have to decide if negation of Equals(5) is NotEquals(5) or LessThan(5).Or(GreaterThan(5)).
Gabriel Ščerbák
I believe LessThan(5)OrGreaterThan(5) is wrong. That leaves NotEquals(5).
koen