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:
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:
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:
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:
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:
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:
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:
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:
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
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
Gamma, Erich et al. (1994). Design Patterns: Elements of Reusable Object-Oriented Software. Addison-Wesley Professional. ISBN 0-201-63361-2.
Freeman, Eric et al. (2004). Head First Design Patterns. O'Reilly Media. ISBN 0-596-00712-4.
Fowler, Martin (2002). Patterns of Enterprise Application Architecture. Addison-Wesley Professional. ISBN 0-321-12742-0.
Aho, Alfred V. et al. (2006). Compilers: Principles, Techniques, and Tools. 2nd ed. Addison-Wesley. ISBN 0-321-48681-1.
Parr, Terence (2013). The Definitive ANTLR 4 Reference. Pragmatic Bookshelf. ISBN 1-934-35699-5.
Vlissides, John (1998). Pattern Hatching: Design Patterns Applied. Addison-Wesley Professional. ISBN 0-201-43293-5.
Beck, Kent (2002). Test Driven Development: By Example. Addison-Wesley Professional. ISBN 0-321-14653-0.
Evans, Eric (2003). Domain-Driven Design: Tackling Complexity in the Heart of Software. Addison-Wesley Professional. ISBN 0-321-12521-5.
Buschmann, Frank et al. (1996). Pattern-Oriented Software Architecture Volume 1: A System of Patterns. Wiley. ISBN 0-471-95869-7.