SwadhyAI Notes

All Low-Level Design notes

Extra pattern · not GoF

Specification pattern

A specification is a boolean rule object — “is satisfied by this candidate?” — that you can combine with and, or, and not. Use it when rules grow and nest; keep simple checks as plain ifs.

Example: the lot now has EV-only, handicapped, compact, and “not overnight” rules. Someone starts adding booleans to allocate(vehicle, evOnly, handicap, compact, overnight). Named specification objects compose without a boolean soup parameter list.

Can I combine this rule with another without adding a boolean?

Diagram text: the exact Mermaid definition below preserves the nodes, labels, and connections. Enable JavaScript to view the rendered diagram.
Read the diagram definition
flowchart TB
  A[allocate vehicle] --> S[Specification: EV and fits and free]
  S --> C[candidates]
  C --> T[AllocationStrategy.choose]
  T --> Spot

Rules you can and together

java
interface SpotSpec {
  boolean isSatisfiedBy(ParkingSpot spot);

  default SpotSpec and(SpotSpec other) {
    return spot -> this.isSatisfiedBy(spot) && other.isSatisfiedBy(spot);
  }

  default SpotSpec or(SpotSpec other) {
    return spot -> this.isSatisfiedBy(spot) || other.isSatisfiedBy(spot);
  }

  default SpotSpec not() {
    return spot -> !this.isSatisfiedBy(spot);
  }
}

class EvOnly implements SpotSpec {
  public boolean isSatisfiedBy(ParkingSpot spot) { return spot.isEv(); }
}

class FitsVehicle implements SpotSpec {
  private final Vehicle vehicle;
  FitsVehicle(Vehicle vehicle) { this.vehicle = vehicle; }
  public boolean isSatisfiedBy(ParkingSpot spot) { return spot.fits(vehicle); }
}

SpotSpec forCar = new EvOnly().and(new FitsVehicle(vehicle));

Specification is not Strategy

Specification

Which spots are even . A with a name. Filter first.

Strategy

How to choose among spots that already fit — nearest, first-free, random. See Strategy.

You often use both. A policy is a cousin: one named business rule, often not composed. Specification is the compose story. A rule engine case study is this pattern at product size.

Smell

Boolean parameter explosion

Growing method

allocate(v, ev, handicap,
    compact, overnight)

You will miss a combination. The next rule is another flag.

Named rules

EvOnly.and(FitsVehicle)
    .and(NotReserved)

Compose the ones the use case needs. A new rule is a new class.

Keep SQL out of the spec

isSatisfiedBy should talk to the domain object. If every spec becomes a WHERE clause, you coupled rules to the database. Filter in memory for LLD, or map the spec at the repository — do not hide JDBC in the rule. That is the same boundary as pagination and filtering: the domain names the predicate; persistence may translate it later.

java
class Allocator {
  Optional<ParkingSpot> choose(List<ParkingSpot> spots, SpotSpec spec, AllocationStrategy strategy) {
    List<ParkingSpot> candidates = spots.stream()
        .filter(spec::isSatisfiedBy)
        .toList();
    return strategy.choose(candidates);
  }
}

Composite is the tree cousin

Composite treats a leaf and a group as the same type. AndSpec / OrSpec are composites of predicates. You do not need to say both names in a round. Say “rules I can and together” and show the interface.

Boolean parameter explosion

allocate(v, ev, handicap, compact, overnight) will miss a combination. Name each rule. Compose the ones the use case needs.

Putting SQL inside the spec

isSatisfiedBy should talk to the domain object. If every spec becomes a WHERE clause, you coupled rules to the database. Filter in memory for LLD, or map the spec at the repository.

or() and not() are easy to get wrong

EvOnly.or(HandicapOnly) is a union of candidates. EvOnly.and(HandicapOnly.not()) is “EV and not reserved for access.” Say the English sentence before you chain methods. A wrong and/or is a silent empty lot.

Library eligibility is the same pattern

Library rules — member in good standing, copy available, not a reference-only title — combine. A boolean checkout(member, copy, goodStanding, available, reference) will miss a combination. Name each spec.

java
interface LoanSpec {
  boolean isSatisfiedBy(LoanRequest request);
}

class MemberInGoodStanding implements LoanSpec {
  public boolean isSatisfiedBy(LoanRequest r) { return !r.member().isBlocked(); }
}

class CopyAvailable implements LoanSpec {
  public boolean isSatisfiedBy(LoanRequest r) { return r.copy().isOnShelf(); }
}

Worked filter: EV that fits, not reserved

The use case is “park an EV.” The spec is EvOnly.and(FitsVehicle).and(new NotReserved()). The strategy then picks nearest or first-free among candidates. A handicapped-only follow-up is a new class plus one and(), not a new boolean on allocate().

java
class NotReserved implements SpotSpec {
  public boolean isSatisfiedBy(ParkingSpot spot) { return !spot.isReserved(); }
}

class HandicapOnly implements SpotSpec {
  public boolean isSatisfiedBy(ParkingSpot spot) { return spot.isHandicap(); }
}

SpotSpec evJob = new EvOnly().and(new FitsVehicle(vehicle)).and(new NotReserved());
SpotSpec accessJob = new HandicapOnly().and(new FitsVehicle(vehicle)).and(new NotReserved());

When one or two ifs are enough

If the lot only has “must fit” and “must be free,” write two ifs. Specification is worth using when rules combine and the interviewer will add a third. Do not extract a framework for a boolean that will never grow. That is the same instinct as YAGNI.

Related pages