Extensibility & Pattern Selection Skill
Purpose
Guide appropriate application of software design patterns (Adapter, Factory, Strategy) based on requirements and trade-off analysis.
1. Pattern Selection Guidelines
CONDITIONAL PATTERN APPLICABILITY
Adapter Pattern:
- When to use: Multiple external providers/channels expose incompatible interfaces and a stable internal domain abstraction reduces coupling.
- When NOT to use: Only a single static interface exists with no planned alternative implementations.
Strategy Pattern:
- When to use: Business rules/algorithms (e.g. risk checks, fee calculations, matching rules) vary dynamically by product, type, or runtime metadata and need independent testability/replaceability.
- When NOT to use: Business rules are static and uniform across all entities.
Factory / Registry Pattern:
- When to use: Runtime selection among multiple implementation instances (adapters or strategies) is required based on request metadata or configuration.
- When NOT to use: Direct bean injection or single implementation suffices.
2. Reference Design Patterns
// Adapter abstraction
public interface PaymentRailAdapter {
RailType getSupportedRail();
ProcessResult executePayment(PaymentRequest request);
}
// Strategy abstraction
public interface RiskAssessmentStrategy {
String getStrategyName();
RiskScore evaluate(TransactionContext context);
}
// Registry / Factory resolution
@Component
public class PaymentRailFactory {
private final Map<RailType, PaymentRailAdapter> railMap;
public PaymentRailFactory(List<PaymentRailAdapter> adapters) {
this.railMap = adapters.stream()
.collect(Collectors.toMap(PaymentRailAdapter::getSupportedRail, Function.identity()));
}
public PaymentRailAdapter getAdapter(RailType railType) {
PaymentRailAdapter adapter = railMap.get(railType);
if (adapter == null) throw new UnsupportedRailException(railType);
return adapter;
}
}