Spec<T> class abstract base

The Specification Pattern: compose business rules as reusable, testable predicates.

A Spec represents a single business rule that an object either satisfies or doesn't. Instead of embedding validation logic directly in methods, specifications are first-class objects that can be created, combined, tested, and reused independently.

This pattern is ideal for:

  • Domain rules: Business rules, filtering, and search criteria
  • Reusability: The same rule applies in multiple contexts without duplication
  • Testability: Each rule is isolated and easy to unit test
  • Composability: Combine simple rules into complex business logic
  • Readability: Express business intent clearly through method names

Compose specs using:

  • & / and — Both must be satisfied (AND logic)
  • | / or — At least one must be satisfied (OR logic)
  • ^ / xor — Exactly one must be satisfied, not both (XOR logic)
  • nand — At least one must not be satisfied (NAND logic)
  • nor — Neither must be satisfied (NOR logic)
  • xnor — Both must have the same satisfaction state (XNOR logic)
  • negated or ~ — Inverts the rule (NOT logic)

AND, OR, NAND, and NOR use left-to-right short-circuit evaluation. XOR and XNOR evaluate both operands because both results are needed.

Creating a Spec:

/// Business rule: person is old enough to vote
final class IsEligibleVoter extends Spec<Person> {
  @override
  bool isSatisfiedBy(Person person) => person.age >= 18;
}

/// Business rule: person has valid contact info
final class HasValidEmail extends Spec<Person> {
  @override
  bool isSatisfiedBy(Person person) => person.email.contains('@');
}

Using a Spec:

// Single rule
final isVoter = IsEligibleVoter();
if (isVoter.isSatisfiedBy(person)) {
  registerToVote(person);
}

// Composed rules
final validVoter = IsEligibleVoter() & HasValidEmail();
final voters = people.where((p) => validVoter.isSatisfiedBy(p)).toList();

// Complex compositions
final canReceiveNewsletter = HasValidEmail() & (IsEligibleVoter() | HasOptedIn());
Annotations
  • @immutable

Constructors

Spec()
Creates a specification.
const
Spec.allOf(Iterable<Spec<T>> specs)
Creates a spec satisfied when every spec in specs is satisfied.
factory
Spec.always()
Creates a spec that is satisfied by every candidate.
const
factory
Spec.anyOf(Iterable<Spec<T>> specs)
Creates a spec satisfied when at least one spec in specs is satisfied.
factory
Spec.never()
Creates a spec that is not satisfied by any candidate.
const
factory
Spec.noneOf(Iterable<Spec<T>> specs)
Creates a spec satisfied when none of the specs in specs is satisfied.
factory
Spec.predicate(bool predicate(T candidate))
Creates a spec from predicate.
const
factory

Properties

hashCode → int
The hash code for this object.
no setterinherited
runtimeType → Type
A representation of the runtime type of the object.
no setterinherited

Methods

and(Spec<T> other) → Spec<T>
Combines this spec with another using AND logic.
call(T candidate) → bool
Evaluates this spec for candidate.
contramap<R>(T projection(R candidate)) → Spec<R>
Adapts this spec to candidates of type R using projection.
iff(Spec<T> other) → Spec<T>
Combines this spec with other using logical equivalence.
implies(Spec<T> other) → Spec<T>
Combines this spec with other using implication logic.
isSatisfiedBy(T candidate) → bool
Checks if candidate satisfies this specification.
nand(Spec<T> other) → Spec<T>
Combines this spec with another using NAND logic (NOT AND).
negated() → Spec<T>
Returns the logical negation of this spec.
nor(Spec<T> other) → Spec<T>
Combines this spec with another using NOR logic (NOT OR).
noSuchMethod(Invocation invocation) → dynamic
Invoked when a nonexistent method or property is accessed.
inherited
or(Spec<T> other) → Spec<T>
Combines this spec with another using OR logic.
toString() → String
A string representation of this object.
inherited
xnor(Spec<T> other) → Spec<T>
Combines this spec with another using XNOR logic (NOT XOR / equivalence).
xor(Spec<T> other) → Spec<T>
Combines this spec with another using XOR logic (exclusive OR).

Operators

operator &(Spec<T> other) → Spec<T>
Combines this spec with another using AND logic (&).
operator ==(Object other) → bool
The equality operator.
inherited
operator ^(Spec<T> other) → Spec<T>
Combines this spec with another using XOR logic (^).
operator |(Spec<T> other) → Spec<T>
Combines this spec with another using OR logic (|).
operator ~() → Spec<T>
Returns the logical negation of this spec.