I am currently working on incorporating a generic Specification pattern and a generic Visitor pattern simultaneously. Below are the base interfaces I have developed for this implementation.
export interface Specification<T, TVisitor extends SpecificationVisitor<TVisitor, T>> {
accept(visitor: TVisitor): void;
isSatisfiedBy(candidate: T): boolean;
and(other: Specification<T, TVisitor>): Specification<T, TVisitor>;
andNot(other: Specification<T, TVisitor>): Specification<T, TVisitor>;
or(other: Specification<T, TVisitor>): Specification<T, TVisitor>;
orNot(other: Specification<T, TVisitor>): Specification<T, TVisitor>;
not(): Specification<T, TVisitor>;
}
export interface SpecificationVisitor<TVisitor extends SpecificationVisitor<TVisitor, T>, T> {
visit(specification: AndSpecification<T, TVisitor>): void;
visit(specification: AndNotSpecification<T, TVisitor>): void;
visit(specification: OrSpecification<T, TVisitor>): void;
visit(specification: OrNotSpecification<T, TVisitor>): void;
visit(specification: NotSpecification<T, TVisitor>): void;
}
To facilitate this setup, I have implemented some base classes along with an abstract class for the fundamental boolean operators.
export abstract class CompositeSpecification<T, TVisitor extends SpecificationVisitor<TVisitor, T>> implements Specification<T, TVisitor> {
abstract isSatisfiedBy(candidate: T): boolean;
abstract accept(visitor: TVisitor): void;
and(other: Specification<T, TVisitor>): Specification<T, TVisitor> {
return new AndSpecification<T, TVisitor>(this, other);
}
andNot(other: Specification<T, TVisitor>): Specification<T, TVisitor> {
return new AndNotSpecification<T, TVisitor>(this, other);
}
or(other: Specification<T, TVisitor>): Specification<T, TVisitor> {
return new OrSpecification<T, TVisitor>(this, other);
}
orNot(other: Specification<T, TVisitor>): Specification<T, TVisitor> {
return new OrNotSpecification<T, TVisitor>(this, other);
}
not(): Specification<T, TVisitor> {
return new NotSpecification<T, TVisitor>(this);
}
}
export class AndSpecification<T, TVisitor extends SpecificationVisitor<TVisitor, T>> extends CompositeSpecification<
T,
TVisitor
> {
constructor(readonly left: Specification<T, TVisitor>, readonly right: Specification<T, TVisitor>) {
super();
}
accept(visitor: TVisitor): void {
visitor.visit(this);
}
isSatisfiedBy(candidate: T): boolean {
return this.left.isSatisfiedBy(candidate) && this.right.isSatisfiedBy(candidate);
}
}
export class AndNotSpecification<T, TVisitor extends SpecificationVisitor<TVisitor, T>> extends CompositeSpecification<T, TVisitor> {
constructor(readonly left: Specification<T, TVisitor>, readonly right: Specification<T, TVisitor>) {
super();
}
accept(visitor: TVisitor): void {
visitor.visit(this);
}
isSatisfiedBy(candidate: T): boolean {
return this.left.isSatisfiedBy(candidate) && !this.right.isSatisfiedBy(candidate);
}
}
export class OrSpecification<T, TVisitor extends SpecificationVisitor<TVisitor, T>> extends CompositeSpecification<
T,
TVisitor
> {
constructor(readonly left: Specification<T, TVisitor>, readonly right: Specification<T, TVisitor>) {
super();
}
accept(visitor: TVisitor): void {
visitor.visit(this);
}
isSatisfiedBy(candidate: T): boolean {
return this.left.isSatisfiedBy(candidate) || this.right.isSatisfiedBy(candidate);
}
}
export class OrNotSpecification<T, TVisitor extends SpecificationVisitor<TVisitor, T>> extends CompositeSpecification<
T,
TVisitor
> {
constructor(readonly left: Specification<T, TVisitor>, readonly right: Specification<T, TVisitor>) {
super();
}
accept(visitor: TVisitor): void {
visitor.visit(this);
}
isSatisfiedBy(candidate: T): boolean {
return this.left.isSatisfiedBy(candidate) || !this.right.isSatisfiedBy(candidate);
}
}
export class NotSpecification<T, TVisitor extends SpecificationVisitor<TVisitor, T>> extends CompositeSpecification<
T,
TVisitor
> {
constructor(readonly other: Specification<T, TVisitor>) {
super();
}
accept(visitor: TVisitor): void {
visitor.visit(this);
}
isSatisfiedBy(candidate: T): boolean {
return !this.other.isSatisfiedBy(candidate);
}
}
The code above compiles successfully without any errors. However, issues arise when attempting to create an interface that extends the base SpecificationVisitor interface and implementing a class that extends the abstract CompositeSpecification.
export interface NumberComparatorVisitor extends SpecificationVisitor<NumberComparatorVisitor, number> {
visit(specification: GreaterThan): void;
}
export class GreaterThan extends CompositeSpecification<number, NumberComparatorVisitor> {
constructor(readonly value: number) {
super();
}
accept(visitor: NumberComparatorVisitor): void {
visitor.visit(this);
}
isSatisfiedBy(candidate: number): boolean {
return candidate > this.value;
}
}
The following errors occur:
Type 'NumberComparatorVisitor' does not satisfy the constraint 'SpecificationVisitor<NumberComparatorVisitor, number>'.ts(2344)
Interface 'NumberComparatorVisitor' incorrectly extends interface 'SpecificationVisitor<NumberComparatorVisitor, number>'.
Types of property 'visit' are incompatible.
Type '(specification: GreaterThan) => void' is not assignable to type '{ (specification: AndSpecification<number, NumberComparatorVisitor>): void; (specification: AndNotSpecification<number, NumberComparatorVisitor>): void; (specification: OrSpecification<...>): void; (specification: OrNotSpecification<...>): void; (specification: NotSpecification<...>): void; }'.
Types of parameters 'specification' and 'specification' are incompatible.
Type 'AndSpecification<number, NumberComparatorVisitor>' is not assignable to type 'GreaterThan'.ts(2430)
Type 'NumberComparatorVisitor' does not satisfy the constraint 'SpecificationVisitor<NumberComparatorVisitor, number>'.
Types of property 'visit' are incompatible.
Type '(specification: GreaterThan) => void' is not assignable to type '{ (specification: AndSpecification<number, NumberComparatorVisitor>): void; (specification: AndNotSpecification<number, NumberComparatorVisitor>): void; (specification: OrSpecification<...>): void; (specification: OrNotSpecification<...>): void; (specification: NotSpecification<...>): void; }'.
Types of parameters 'specification' and 'specification' are incompatible.
Property 'value' is missing in type 'AndSpecification<number, NumberComparatorVisitor>' but required in type 'GreaterThan'.ts(2344)
I am unclear about the reason for these complaints. What modifications should be made to achieve the desired outcome?