Skip to content

Advanced Patterns

Explore sophisticated specification patterns that solve complex software design challenges. These patterns extend the basic specification concepts into more advanced architectural territories.

Meta-Specifications

Specifications That Generate Other Specifications

Meta-specifications can dynamically create other specifications based on runtime conditions:

php
class ConditionalSpecificationBuilder extends AbstractSpecification
{
    private array $rules = [];
    
    public function addRule(callable $condition, callable $specFactory): self
    {
        $this->rules[] = ['condition' => $condition, 'factory' => $specFactory];
        return $this;
    }
    
    public function buildFor(mixed $context): SpecificationInterface
    {
        $specifications = [];
        
        foreach ($this->rules as $rule) {
            if (call_user_func($rule['condition'], $context)) {
                $specifications[] = call_user_func($rule['factory'], $context);
            }
        }
        
        if (empty($specifications)) {
            return new AlwaysTrueSpecification();
        }
        
        return array_reduce(
            array_slice($specifications, 1),
            fn($carry, $spec) => $carry->and($spec),
            $specifications[0]
        );
    }
    
    public function isSatisfiedBy(mixed $candidate): bool
    {
        $dynamicSpec = $this->buildFor($candidate);
        return $dynamicSpec->isSatisfiedBy($candidate);
    }
}

// Usage: Dynamic business rules based on customer type
$builder = new ConditionalSpecificationBuilder();

$builder->addRule(
    condition: fn($customer) => $customer->type === 'enterprise',
    specFactory: fn($customer) => new EnterpriseValidationSpecification()
);

$builder->addRule(
    condition: fn($customer) => $customer->type === 'premium',
    specFactory: fn($customer) => new PremiumValidationSpecification()
);

$spec = $builder->buildFor($customer);

Composite Pattern Extensions

Specification Trees

Build hierarchical specification structures:

php
class SpecificationTree extends AbstractSpecification
{
    private SpecificationInterface $root;
    private array $children = [];
    
    public function __construct(SpecificationInterface $root)
    {
        $this->root = $root;
    }
    
    public function addChild(SpecificationInterface $child): self
    {
        $this->children[] = $child;
        return $this;
    }
    
    public function isSatisfiedBy(mixed $candidate): bool
    {
        // Root must be satisfied first
        if (!$this->root->isSatisfiedBy($candidate)) {
            return false;
        }
        
        // Then all children must be satisfied
        foreach ($this->children as $child) {
            if (!$child->isSatisfiedBy($candidate)) {
                return false;
            }
        }
        
        return true;
    }
    
    public function getViolations(mixed $candidate): array
    {
        $violations = [];
        
        if (!$this->root->isSatisfiedBy($candidate)) {
            $violations[] = 'Root requirement not met: ' . get_class($this->root);
        }
        
        foreach ($this->children as $child) {
            if (!$child->isSatisfiedBy($candidate)) {
                $violations[] = 'Child requirement not met: ' . get_class($child);
            }
        }
        
        return $violations;
    }
}

Strategy Pattern Integration

Pluggable Specification Strategies

Combine specifications with the Strategy pattern:

php
interface ValidationStrategy
{
    public function getSpecification(): SpecificationInterface;
    public function getValidationLevel(): string;
}

class StrictValidationStrategy implements ValidationStrategy
{
    public function getSpecification(): SpecificationInterface
    {
        return (new RequiredFieldsSpecification())
            ->and(new DataTypeValidationSpecification())
            ->and(new BusinessRuleValidationSpecification())
            ->and(new SecurityValidationSpecification());
    }
    
    public function getValidationLevel(): string
    {
        return 'strict';
    }
}

class LenientValidationStrategy implements ValidationStrategy
{
    public function getSpecification(): SpecificationInterface
    {
        return (new RequiredFieldsSpecification())
            ->and(new BasicBusinessRuleValidationSpecification());
    }
    
    public function getValidationLevel(): string
    {
        return 'lenient';
    }
}

class ValidationContext
{
    private ValidationStrategy $strategy;
    
    public function __construct(ValidationStrategy $strategy)
    {
        $this->strategy = $strategy;
    }
    
    public function validate(mixed $data): bool
    {
        return $this->strategy->getSpecification()->isSatisfiedBy($data);
    }
    
    public function setStrategy(ValidationStrategy $strategy): void
    {
        $this->strategy = $strategy;
    }
}

Chain of Responsibility Pattern

Specification Chains

Process specifications in a chain until one succeeds or all fail:

php
class SpecificationChain extends AbstractSpecification
{
    private array $specifications = [];
    private string $mode = 'first_success'; // or 'all_must_pass'
    
    public function addSpecification(SpecificationInterface $spec): self
    {
        $this->specifications[] = $spec;
        return $this;
    }
    
    public function setMode(string $mode): self
    {
        $this->mode = $mode;
        return $this;
    }
    
    public function isSatisfiedBy(mixed $candidate): bool
    {
        if ($this->mode === 'first_success') {
            return $this->firstSuccessMode($candidate);
        }
        
        return $this->allMustPassMode($candidate);
    }
    
    private function firstSuccessMode(mixed $candidate): bool
    {
        foreach ($this->specifications as $spec) {
            if ($spec->isSatisfiedBy($candidate)) {
                return true;
            }
        }
        return false;
    }
    
    private function allMustPassMode(mixed $candidate): bool
    {
        foreach ($this->specifications as $spec) {
            if (!$spec->isSatisfiedBy($candidate)) {
                return false;
            }
        }
        return true;
    }
    
    public function getFirstSatisfiedSpecification(mixed $candidate): ?SpecificationInterface
    {
        foreach ($this->specifications as $spec) {
            if ($spec->isSatisfiedBy($candidate)) {
                return $spec;
            }
        }
        return null;
    }
}

Visitor Pattern Integration

Specification Visitors

Apply operations across specification hierarchies:

php
interface SpecificationVisitor
{
    public function visitAnd(AndSpecification $spec): mixed;
    public function visitOr(OrSpecification $spec): mixed;
    public function visitNot(NotSpecification $spec): mixed;
    public function visitLeaf(AbstractSpecification $spec): mixed;
}

class SpecificationComplexityCalculator implements SpecificationVisitor
{
    private int $complexity = 0;
    
    public function visitAnd(AndSpecification $spec): int
    {
        $this->complexity += 2; // AND operations add complexity
        return $this->complexity;
    }
    
    public function visitOr(OrSpecification $spec): int
    {
        $this->complexity += 3; // OR operations add more complexity
        return $this->complexity;
    }
    
    public function visitNot(NotSpecification $spec): int
    {
        $this->complexity += 1; // NOT operations add minimal complexity
        return $this->complexity;
    }
    
    public function visitLeaf(AbstractSpecification $spec): int
    {
        $this->complexity += 1; // Base case
        return $this->complexity;
    }
    
    public function getComplexity(): int
    {
        return $this->complexity;
    }
    
    public function reset(): void
    {
        $this->complexity = 0;
    }
}

trait VisitableSpecification
{
    public function accept(SpecificationVisitor $visitor): mixed
    {
        if ($this instanceof AndSpecification) {
            return $visitor->visitAnd($this);
        }
        
        if ($this instanceof OrSpecification) {
            return $visitor->visitOr($this);
        }
        
        if ($this instanceof NotSpecification) {
            return $visitor->visitNot($this);
        }
        
        return $visitor->visitLeaf($this);
    }
}

Command Pattern Integration

Specification Commands

Encapsulate specification operations as commands:

php
interface SpecificationCommand
{
    public function execute(mixed $target): bool;
    public function undo(mixed $target): void;
    public function canExecute(mixed $target): bool;
}

class ApplySpecificationCommand implements SpecificationCommand
{
    private SpecificationInterface $specification;
    private callable $onSuccess;
    private callable $onFailure;
    private array $executionLog = [];
    
    public function __construct(
        SpecificationInterface $specification,
        callable $onSuccess,
        callable $onFailure
    ) {
        $this->specification = $specification;
        $this->onSuccess = $onSuccess;
        $this->onFailure = $onFailure;
    }
    
    public function execute(mixed $target): bool
    {
        if (!$this->canExecute($target)) {
            return false;
        }
        
        $result = $this->specification->isSatisfiedBy($target);
        
        $this->executionLog[] = [
            'target' => $target,
            'result' => $result,
            'timestamp' => now(),
        ];
        
        if ($result) {
            call_user_func($this->onSuccess, $target);
        } else {
            call_user_func($this->onFailure, $target);
        }
        
        return $result;
    }
    
    public function canExecute(mixed $target): bool
    {
        return is_object($target);
    }
    
    public function undo(mixed $target): void
    {
        // Remove last execution from log
        array_pop($this->executionLog);
    }
    
    public function getExecutionLog(): array
    {
        return $this->executionLog;
    }
}

Interpreter Pattern

Specification DSL

Create a domain-specific language for specifications:

php
class SpecificationInterpreter
{
    private array $context = [];
    
    public function setVariable(string $name, mixed $value): void
    {
        $this->context[$name] = $value;
    }
    
    public function interpret(string $expression): SpecificationInterface
    {
        // Simple DSL parser
        // Examples: "user.status = 'active' AND user.verified = true"
        //           "product.price > 100 OR product.category = 'electronics'"
        
        $tokens = $this->tokenize($expression);
        return $this->parseTokens($tokens);
    }
    
    private function tokenize(string $expression): array
    {
        $patterns = [
            'IDENTIFIER' => '/[a-zA-Z_][a-zA-Z0-9_.]*/',
            'OPERATOR' => '/=|!=|>|<|>=|<=/',
            'LOGICAL' => '/AND|OR|NOT/',
            'VALUE' => '/\'[^\']*\'|\d+(\.\d+)?/',
            'PAREN' => '/[()]/',
        ];
        
        $tokens = [];
        $position = 0;
        
        while ($position < strlen($expression)) {
            $matched = false;
            
            foreach ($patterns as $type => $pattern) {
                if (preg_match($pattern, $expression, $matches, PREG_OFFSET_CAPTURE, $position)) {
                    if ($matches[0][1] === $position) {
                        $tokens[] = ['type' => $type, 'value' => $matches[0][0]];
                        $position += strlen($matches[0][0]);
                        $matched = true;
                        break;
                    }
                }
            }
            
            if (!$matched) {
                $position++; // Skip whitespace and unknown characters
            }
        }
        
        return $tokens;
    }
    
    private function parseTokens(array $tokens): SpecificationInterface
    {
        // Simplified parser - in production, use a proper parser generator
        if (count($tokens) >= 3) {
            $field = $tokens[0]['value'];
            $operator = $tokens[1]['value'];
            $value = trim($tokens[2]['value'], "'");
            
            return match($operator) {
                '=' => new WhereSpecification($field, $value),
                '!=' => new WhereSpecification($field, '!=', $value),
                '>' => new WhereSpecification($field, '>', $value),
                '<' => new WhereSpecification($field, '<', $value),
                '>=' => new WhereSpecification($field, '>=', $value),
                '<=' => new WhereSpecification($field, '<=', $value),
                default => new AlwaysTrueSpecification()
            };
        }
        
        return new AlwaysTrueSpecification();
    }
}

// Usage
$interpreter = new SpecificationInterpreter();
$spec = $interpreter->interpret("status = 'active'");

Temporal Specifications

Time-Aware Specifications

Handle time-based business rules:

php
class TemporalSpecification extends AbstractSpecification
{
    private SpecificationInterface $baseSpec;
    private ?Carbon $validFrom;
    private ?Carbon $validUntil;
    
    public function __construct(
        SpecificationInterface $baseSpec,
        ?Carbon $validFrom = null,
        ?Carbon $validUntil = null
    ) {
        $this->baseSpec = $baseSpec;
        $this->validFrom = $validFrom;
        $this->validUntil = $validUntil;
    }
    
    public function isSatisfiedBy(mixed $candidate): bool
    {
        if (!$this->isTemporallyValid()) {
            return false;
        }
        
        return $this->baseSpec->isSatisfiedBy($candidate);
    }
    
    public function isTemporallyValid(?Carbon $atTime = null): bool
    {
        $checkTime = $atTime ?? now();
        
        if ($this->validFrom && $checkTime->isBefore($this->validFrom)) {
            return false;
        }
        
        if ($this->validUntil && $checkTime->isAfter($this->validUntil)) {
            return false;
        }
        
        return true;
    }
    
    public function getValidityPeriod(): array
    {
        return [
            'from' => $this->validFrom,
            'until' => $this->validUntil,
        ];
    }
    
    public static function validBetween(
        SpecificationInterface $spec,
        Carbon $from,
        Carbon $until
    ): self {
        return new self($spec, $from, $until);
    }
    
    public static function validFrom(SpecificationInterface $spec, Carbon $from): self
    {
        return new self($spec, $from);
    }
    
    public static function validUntil(SpecificationInterface $spec, Carbon $until): self
    {
        return new self($spec, null, $until);
    }
}

// Usage
$promoSpec = TemporalSpecification::validBetween(
    new PromotionalDiscountSpecification(),
    Carbon::parse('2025-01-01'),
    Carbon::parse('2025-01-31')
);

$isEligible = $promoSpec->isSatisfiedBy($customer);

Advanced Architectural Patterns

These patterns demonstrate how specifications integrate with sophisticated software architectures:

Event-Driven Specifications

php
class EventDrivenSpecification extends AbstractSpecification
{
    private SpecificationInterface $baseSpec;
    private array $eventListeners = [];
    
    public function addEventListener(string $event, callable $listener): self
    {
        $this->eventListeners[$event][] = $listener;
        return $this;
    }
    
    public function isSatisfiedBy(mixed $candidate): bool
    {
        $this->fireEvent('before_evaluation', $candidate);
        
        $result = $this->baseSpec->isSatisfiedBy($candidate);
        
        $eventName = $result ? 'specification_satisfied' : 'specification_failed';
        $this->fireEvent($eventName, $candidate, $result);
        
        $this->fireEvent('after_evaluation', $candidate, $result);
        
        return $result;
    }
    
    private function fireEvent(string $event, ...$args): void
    {
        if (isset($this->eventListeners[$event])) {
            foreach ($this->eventListeners[$event] as $listener) {
                call_user_func($listener, ...$args);
            }
        }
    }
}

Practical Applications

These advanced patterns are useful for:

  • Dynamic rule systems where business rules change based on context
  • Complex validation scenarios requiring multiple validation strategies
  • Audit and compliance systems needing detailed specification tracking
  • A/B testing frameworks where different rule sets are applied to different user groups
  • Time-sensitive business logic with validity periods
  • Domain-specific languages for non-technical users to define business rules

Advanced Pattern Usage

These patterns solve real architectural challenges. Use them when basic specifications aren't sufficient for your complex business requirements, but avoid over-engineering simple scenarios.

Explore Mathematical Foundations →

References

  1. Gamma, Erich et al. (1994). Design Patterns: Elements of Reusable Object-Oriented Software. Addison-Wesley Professional. ISBN 0-201-63361-2.

  2. Freeman, Eric et al. (2004). Head First Design Patterns. O'Reilly Media. ISBN 0-596-00712-4.

  3. Fowler, Martin (2002). Patterns of Enterprise Application Architecture. Addison-Wesley Professional. ISBN 0-321-12742-0.

  4. Aho, Alfred V. et al. (2006). Compilers: Principles, Techniques, and Tools. 2nd ed. Addison-Wesley. ISBN 0-321-48681-1.

  5. Parr, Terence (2013). The Definitive ANTLR 4 Reference. Pragmatic Bookshelf. ISBN 1-934-35699-5.

  6. Vlissides, John (1998). Pattern Hatching: Design Patterns Applied. Addison-Wesley Professional. ISBN 0-201-43293-5.

  7. Beck, Kent (2002). Test Driven Development: By Example. Addison-Wesley Professional. ISBN 0-321-14653-0.

  8. Evans, Eric (2003). Domain-Driven Design: Tackling Complexity in the Heart of Software. Addison-Wesley Professional. ISBN 0-321-12521-5.

  9. Buschmann, Frank et al. (1996). Pattern-Oriented Software Architecture Volume 1: A System of Patterns. Wiley. ISBN 0-471-95869-7.

Released under the MIT License.