google-site-verification: google61fe8ba583a51912.html
Custom Pattern Implementations in Java

Overview

Custom patterns are reusable solutions to common problems that aren't covered by traditional Gang of Four patterns. These patterns address modern challenges in software development.

1. Repository Pattern with Caching

Generic Repository Interface

public interface Repository<T, ID> { Optional<T> findById(ID id); List<T> findAll(); T save(T entity); void deleteById(ID id); boolean existsById(ID id); long count(); }

Cached Repository Implementation

public class CachedRepository<T, ID> implements Repository<T, ID> { private final Repository<T, ID> delegate; private final Cache<ID, T> cache; private final Cache<String, List<T>> queryCache; public CachedRepository(Repository<T, ID> delegate, CacheConfig cacheConfig) { this.delegate = delegate; this.cache = Caffeine.newBuilder() .maximumSize(cacheConfig.getMaxSize()) .expireAfterWrite(cacheConfig.getTtl()) .build(); this.queryCache = Caffeine.newBuilder() .maximumSize(100) .expireAfterWrite(5, TimeUnit.MINUTES) .build(); } @Override public Optional<T> findById(ID id) { T cached = cache.getIfPresent(id); if (cached != null) { return Optional.of(cached); } Optional<T> result = delegate.findById(id); result.ifPresent(entity -> cache.put(id, entity)); return result; } @Override public List<T> findAll() { String cacheKey = "findAll"; List<T> cached = queryCache.getIfPresent(cacheKey); if (cached != null) { return cached; } List<T> result = delegate.findAll(); queryCache.put(cacheKey, result); return result; } @Override public T save(T entity) { T saved = delegate.save(entity); // Invalidate relevant caches cache.invalidateAll(); queryCache.invalidateAll(); return saved; } @Override public void deleteById(ID id) { delegate.deleteById(id); cache.invalidate(id); queryCache.invalidateAll(); } // Cache management methods public void evictById(ID id) { cache.invalidate(id); } public void evictAll() { cache.invalidateAll(); queryCache.invalidateAll(); } }

2. Pipeline Pattern

Pipeline Interface

public interface Pipeline<T> { T execute(T input); Pipeline<T> addStep(PipelineStep<T> step); Pipeline<T> addConditionalStep(Predicate<T> condition, PipelineStep<T> step); }

Pipeline Implementation

public class ProcessingPipeline<T> implements Pipeline<T> { private final List<PipelineStep<T>> steps; private final List<ConditionalStep<T>> conditionalSteps; public ProcessingPipeline() { this.steps = new ArrayList<>(); this.conditionalSteps = new ArrayList<>(); } @Override public T execute(T input) { T current = input; // Execute regular steps for (PipelineStep<T> step : steps) { current = step.execute(current); } // Execute conditional steps for (ConditionalStep<T> conditional : conditionalSteps) { if (conditional.getCondition().test(current)) { current = conditional.getStep().execute(current); } } return current; } @Override public Pipeline<T> addStep(PipelineStep<T> step) { steps.add(step); return this; } @Override public Pipeline<T> addConditionalStep(Predicate<T> condition, PipelineStep<T> step) { conditionalSteps.add(new ConditionalStep<>(condition, step)); return this; } // Builder for fluent pipeline creation public static <T> Builder<T> builder() { return new Builder<>(); } public static class Builder<T> { private final ProcessingPipeline<T> pipeline; private Builder() { this.pipeline = new ProcessingPipeline<>(); } public Builder<T> withStep(PipelineStep<T> step) { pipeline.addStep(step); return this; } public Builder<T> withConditionalStep(Predicate<T> condition, PipelineStep<T> step) { pipeline.addConditionalStep(condition, step); return this; } public ProcessingPipeline<T> build() { return pipeline; } } }

Pipeline Steps

@FunctionalInterface public interface PipelineStep<T> { T execute(T input); default PipelineStep<T> andThen(PipelineStep<T> next) { return input -> next.execute(this.execute(input)); } } // Concrete pipeline steps public class ValidationStep<T> implements PipelineStep<T> { private final Validator<T> validator; public ValidationStep(Validator<T> validator) { this.validator = validator; } @Override public T execute(T input) { ValidationResult result = validator.validate(input); if (!result.isValid()) { throw new ValidationException("Validation failed: " + result.getErrors()); } return input; } } public class TransformationStep<T> implements PipelineStep<T> { private final Function<T, T> transformer; public TransformationStep(Function<T, T> transformer) { this.transformer = transformer; } @Override public T execute(T input) { return transformer.apply(input); } } public class LoggingStep<T> implements PipelineStep<T> { private final Logger logger; private final String stepName; public LoggingStep(Logger logger, String stepName) { this.logger = logger; this.stepName = stepName; } @Override public T execute(T input) { logger.info("Executing step: {} with input: {}", stepName, input); T result = input; // Pass-through for logging logger.info("Step {} completed with result: {}", stepName, result); return result; } }

3. Specification Pattern

Specification Interface

public interface Specification<T> { boolean isSatisfiedBy(T candidate); default Specification<T> and(Specification<T> other) { return new AndSpecification<>(this, other); } default Specification<T> or(Specification<T> other) { return new OrSpecification<>(this, other); } default Specification<T> not() { return new NotSpecification<>(this); } static <T> Specification<T> of(Predicate<T> predicate) { return predicate::test; } }

Composite Specifications

public class AndSpecification<T> implements Specification<T> { private final Specification<T> left; private final Specification<T> right; public AndSpecification(Specification<T> left, Specification<T> right) { this.left = left; this.right = right; } @Override public boolean isSatisfiedBy(T candidate) { return left.isSatisfiedBy(candidate) && right.isSatisfiedBy(candidate); } } public class OrSpecification<T> implements Specification<T> { private final Specification<T> left; private final Specification<T> right; public OrSpecification(Specification<T> left, Specification<T> right) { this.left = left; this.right = right; } @Override public boolean isSatisfiedBy(T candidate) { return left.isSatisfiedBy(candidate) || right.isSatisfiedBy(candidate); } } public class NotSpecification<T> implements Specification<T> { private final Specification<T> specification; public NotSpecification(Specification<T> specification) { this.specification = specification; } @Override public boolean isSatisfiedBy(T candidate) { return !specification.isSatisfiedBy(candidate); } }

Concrete Specifications

public class UserSpecifications { public static Specification<User> isActive() { return candidate -> candidate.isActive(); } public static Specification<User> hasRole(String role) { return candidate -> candidate.getRoles().contains(role); } public static Specification<User> createdAfter(Instant date) { return candidate -> candidate.getCreatedAt().isAfter(date); } public static Specification<User> withMinimumAge(int age) { return candidate -> Period.between(candidate.getBirthDate(), LocalDate.now()) .getYears() >= age; } public static Specification<User> withEmailDomain(String domain) { return candidate -> candidate.getEmail().endsWith("@" + domain); } } // Usage example public class UserService { public List<User> findUsers(Specification<User> spec) { return userRepository.findAll().stream() .filter(spec::isSatisfiedBy) .collect(Collectors.toList()); } // Complex query example public List<User> findEligibleAdmins() { Specification<User> spec = UserSpecifications.isActive() .and(UserSpecifications.hasRole("ADMIN")) .and(UserSpecifications.withMinimumAge(18)) .and(UserSpecifications.createdAfter(Instant.now().minus(365, ChronoUnit.DAYS))); return findUsers(spec); } }

4. Retry Pattern

Retry Policy

public class RetryPolicy { private final int maxAttempts; private final Duration initialDelay; private final Duration maxDelay; private final double backoffMultiplier; private final Predicate<Exception> retryCondition; private RetryPolicy(Builder builder) { this.maxAttempts = builder.maxAttempts; this.initialDelay = builder.initialDelay; this.maxDelay = builder.maxDelay; this.backoffMultiplier = builder.backoffMultiplier; this.retryCondition = builder.retryCondition; } public static Builder builder() { return new Builder(); } public static class Builder { private int maxAttempts = 3; private Duration initialDelay = Duration.ofSeconds(1); private Duration maxDelay = Duration.ofMinutes(1); private double backoffMultiplier = 2.0; private Predicate<Exception> retryCondition = e -> true; public Builder maxAttempts(int maxAttempts) { this.maxAttempts = maxAttempts; return this; } public Builder initialDelay(Duration initialDelay) { this.initialDelay = initialDelay; return this; } public Builder maxDelay(Duration maxDelay) { this.maxDelay = maxDelay; return this; } public Builder backoffMultiplier(double multiplier) { this.backoffMultiplier = multiplier; return this; } public Builder retryOn(Predicate<Exception> condition) { this.retryCondition = condition; return this; } public Builder retryOn(Class<? extends Exception> exceptionType) { this.retryCondition = e -> exceptionType.isInstance(e); return this; } public RetryPolicy build() { return new RetryPolicy(this); } } // Getters public int getMaxAttempts() { return maxAttempts; } public Duration getInitialDelay() { return initialDelay; } public Duration getMaxDelay() { return maxDelay; } public double getBackoffMultiplier() { return backoffMultiplier; } public Predicate<Exception> getRetryCondition() { return retryCondition; } }

Retry Template

public class RetryTemplate { private final RetryPolicy retryPolicy; private final ScheduledExecutorService scheduler; public RetryTemplate(RetryPolicy retryPolicy) { this.retryPolicy = retryPolicy; this.scheduler = Executors.newSingleThreadScheduledExecutor(); } public <T> T execute(Supplier<T> operation) { Exception lastException = null; for (int attempt = 1; attempt <= retryPolicy.getMaxAttempts(); attempt++) { try { return operation.get(); } catch (Exception e) { lastException = e; if (!shouldRetry(e) || attempt == retryPolicy.getMaxAttempts()) { break; } Duration delay = calculateDelay(attempt); sleep(delay); } } throw new RetryExhaustedException("Operation failed after " + retryPolicy.getMaxAttempts() + " attempts", lastException); } public void execute(Runnable operation) { execute(() -> { operation.run(); return null; }); } public <T> CompletableFuture<T> executeAsync(Supplier<T> operation) { return CompletableFuture.supplyAsync(() -> execute(operation)); } private boolean shouldRetry(Exception e) { return retryPolicy.getRetryCondition().test(e); } private Duration calculateDelay(int attempt) { long delayMillis = (long) (retryPolicy.getInitialDelay().toMillis() * Math.pow(retryPolicy.getBackoffMultiplier(), attempt - 1)); return Duration.ofMillis(Math.min(delayMillis, retryPolicy.getMaxDelay().toMillis())); } private void sleep(Duration duration) { try { Thread.sleep(duration.toMillis()); } catch (InterruptedException e) { Thread.currentThread().interrupt(); throw new RetryInterruptedException("Retry was interrupted", e); } } }

5. Circuit Breaker Pattern

Circuit Breaker State

public enum CircuitBreakerState { CLOSED, // Normal operation OPEN, // Circuit is open, requests fail fast HALF_OPEN // Testing if service has recovered }

Circuit Breaker Implementation

public class CircuitBreaker { private final String name; private final int failureThreshold; private final Duration timeout; private final Duration resetTimeout; private final Clock clock; private volatile CircuitBreakerState state = CircuitBreakerState.CLOSED; private volatile int failureCount = 0; private volatile Instant lastFailureTime; private volatile Instant halfOpenTime; public CircuitBreaker(String name, int failureThreshold, Duration timeout, Duration resetTimeout) { this(name, failureThreshold, timeout, resetTimeout, Clock.systemUTC()); } public CircuitBreaker(String name, int failureThreshold, Duration timeout, Duration resetTimeout, Clock clock) { this.name = name; this.failureThreshold = failureThreshold; this.timeout = timeout; this.resetTimeout = resetTimeout; this.clock = clock; } public <T> T execute(Supplier<T> supplier) { if (!allowRequest()) { throw new CircuitBreakerOpenException("Circuit breaker is open: " + name); } try { T result = supplier.get(); onSuccess(); return result; } catch (Exception e) { onFailure(); throw e; } } public void execute(Runnable runnable) { execute(() -> { runnable.run(); return null; }); } private boolean allowRequest() { switch (state) { case CLOSED: return true; case OPEN: // Check if reset timeout has elapsed if (lastFailureTime.plus(resetTimeout).isBefore(clock.instant())) { state = CircuitBreakerState.HALF_OPEN; halfOpenTime = clock.instant(); return true; } return false; case HALF_OPEN: // Allow limited requests in half-open state if (halfOpenTime.plus(timeout).isBefore(clock.instant())) { // Too much time passed, go back to open state = CircuitBreakerState.OPEN; return false; } return true; default: return false; } } private void onSuccess() { switch (state) { case HALF_OPEN: // Successful request in half-open state, reset circuit reset(); break; case CLOSED: // Reset failure count on consecutive successes if (failureCount > 0) { failureCount = 0; } break; } } private void onFailure() { failureCount++; lastFailureTime = clock.instant(); switch (state) { case CLOSED: if (failureCount >= failureThreshold) { state = CircuitBreakerState.OPEN; } break; case HALF_OPEN: // Failure in half-open state, go back to open state = CircuitBreakerState.OPEN; break; } } private void reset() { state = CircuitBreakerState.CLOSED; failureCount = 0; lastFailureTime = null; halfOpenTime = null; } // Getters for monitoring public CircuitBreakerState getState() { return state; } public int getFailureCount() { return failureCount; } public String getName() { return name; } public Instant getLastFailureTime() { return lastFailureTime; } }

Circuit Breaker Registry

@Component public class CircuitBreakerRegistry { private final Map<String, CircuitBreaker> breakers; private final CircuitBreakerConfig defaultConfig; public CircuitBreakerRegistry(CircuitBreakerConfig defaultConfig) { this.breakers = new ConcurrentHashMap<>(); this.defaultConfig = defaultConfig; } public CircuitBreaker getBreaker(String name) { return breakers.computeIfAbsent(name, this::createCircuitBreaker); } public CircuitBreaker getBreaker(String name, CircuitBreakerConfig config) { return breakers.computeIfAbsent(name, k -> createCircuitBreaker(k, config)); } public Map<String, CircuitBreaker> getBreakers() { return Collections.unmodifiableMap(breakers); } public CircuitBreakerStats getStats(String name) { CircuitBreaker breaker = breakers.get(name); if (breaker == null) { return null; } return new CircuitBreakerStats( breaker.getName(), breaker.getState(), breaker.getFailureCount(), breaker.getLastFailureTime() ); } private CircuitBreaker createCircuitBreaker(String name) { return createCircuitBreaker(name, defaultConfig); } private CircuitBreaker createCircuitBreaker(String name, CircuitBreakerConfig config) { return new CircuitBreaker( name, config.getFailureThreshold(), config.getTimeout(), config.getResetTimeout(), config.getClock() ); } }

6. Event Sourcing Pattern

Event Interface

public interface DomainEvent { String getAggregateId(); Instant getTimestamp(); String getType(); int getVersion(); } public abstract class BaseEvent implements DomainEvent { private final String aggregateId; private final Instant timestamp; private final int version; protected BaseEvent(String aggregateId, int version) { this.aggregateId = aggregateId; this.timestamp = Instant.now(); this.version = version; } @Override public String getAggregateId() { return aggregateId; } @Override public Instant getTimestamp() { return timestamp; } @Override public int getVersion() { return version; } }

Aggregate Root

public abstract class AggregateRoot { private final String id; private int version = 0; private final List<DomainEvent> changes = new ArrayList<>(); protected AggregateRoot(String id) { this.id = id; } public String getId() { return id; } public int getVersion() { return version; } public List<DomainEvent> getUncommittedChanges() { return Collections.unmodifiableList(changes); } public void markChangesAsCommitted() { changes.clear(); } public void loadFromHistory(List<DomainEvent> history) { for (DomainEvent event : history) { applyChange(event, false); version = event.getVersion(); } } protected void applyChange(DomainEvent event) { applyChange(event, true); } private void applyChange(DomainEvent event, boolean isNew) { try { Method method = getClass().getDeclaredMethod("apply", event.getClass()); method.setAccessible(true); method.invoke(this, event); } catch (Exception e) { throw new RuntimeException("Error applying event", e); } if (isNew) { changes.add(event); } } }

Event Store

public interface EventStore { void saveEvents(String aggregateId, List<DomainEvent> events, int expectedVersion); List<DomainEvent> getEvents(String aggregateId); List<DomainEvent> getEventsAfter(String aggregateId, int version); List<String> getAggregateIds(); } public class InMemoryEventStore implements EventStore { private final Map<String, List<DomainEvent>> eventStore = new ConcurrentHashMap<>(); private final Map<String, Integer> currentVersions = new ConcurrentHashMap<>(); @Override public void saveEvents(String aggregateId, List<DomainEvent> events, int expectedVersion) { synchronized (aggregateId.intern()) { int currentVersion = currentVersions.getOrDefault(aggregateId, -1); if (currentVersion != expectedVersion) { throw new ConcurrencyException("Concurrent modification detected for aggregate: " + aggregateId); } List<DomainEvent> aggregateEvents = eventStore.computeIfAbsent(aggregateId, k -> new ArrayList<>()); int version = expectedVersion; for (DomainEvent event : events) { version++; // Create new event with correct version aggregateEvents.add(event); } currentVersions.put(aggregateId, version); } } @Override public List<DomainEvent> getEvents(String aggregateId) { return eventStore.getOrDefault(aggregateId, Collections.emptyList()); } @Override public List<DomainEvent> getEventsAfter(String aggregateId, int version) { List<DomainEvent> allEvents = getEvents(aggregateId); return allEvents.stream() .filter(event -> event.getVersion() > version) .collect(Collectors.toList()); } @Override public List<String> getAggregateIds() { return new ArrayList<>(eventStore.keySet()); } }

7. Builder Pattern with Fluent Interface

Advanced Builder Pattern

public class HttpRequest { private final String url; private final HttpMethod method; private final Map<String, String> headers; private final String body; private final Duration timeout; private final int maxRetries; private final boolean followRedirects; private HttpRequest(Builder builder) { this.url = builder.url; this.method = builder.method; this.headers = Collections.unmodifiableMap(new HashMap<>(builder.headers)); this.body = builder.body; this.timeout = builder.timeout; this.maxRetries = builder.maxRetries; this.followRedirects = builder.followRedirects; } public static Builder builder() { return new Builder(); } // Getters... public static class Builder { private String url; private HttpMethod method = HttpMethod.GET; private Map<String, String> headers = new HashMap<>(); private String body; private Duration timeout = Duration.ofSeconds(30); private int maxRetries = 3; private boolean followRedirects = true; public Builder url(String url) { this.url = Objects.requireNonNull(url, "URL cannot be null"); return this; } public Builder method(HttpMethod method) { this.method = Objects.requireNonNull(method, "Method cannot be null"); return this; } public Builder header(String name, String value) { this.headers.put(Objects.requireNonNull(name), Objects.requireNonNull(value)); return this; } public Builder headers(Map<String, String> headers) { this.headers.putAll(Objects.requireNonNull(headers)); return this; } public Builder body(String body) { this.body = body; return this; } public Builder jsonBody(Object object) { try { ObjectMapper mapper = new ObjectMapper(); this.body = mapper.writeValueAsString(object); header("Content-Type", "application/json"); return this; } catch (JsonProcessingException e) { throw new IllegalArgumentException("Failed to serialize object to JSON", e); } } public Builder timeout(Duration timeout) { this.timeout = Objects.requireNonNull(timeout); return this; } public Builder timeout(long amount, TimeUnit unit) { return timeout(Duration.ofMillis(unit.toMillis(amount))); } public Builder maxRetries(int maxRetries) { if (maxRetries < 0) { throw new IllegalArgumentException("Max retries cannot be negative"); } this.maxRetries = maxRetries; return this; } public Builder followRedirects(boolean followRedirects) { this.followRedirects = followRedirects; return this; } public HttpRequest build() { Objects.requireNonNull(url, "URL must be specified"); if (method == HttpMethod.POST || method == HttpMethod.PUT) { if (body == null) { throw new IllegalStateException("Body must be specified for " + method + " requests"); } } return new HttpRequest(this); } } }

Usage Examples

Pipeline Pattern Usage

public class DataProcessingExample { public static void main(String[] args) { Pipeline<String> pipeline = ProcessingPipeline.<String>builder() .withStep(new ValidationStep<>(new DataValidator())) .withStep(new TransformationStep<>(String::toUpperCase)) .withStep(new LoggingStep<>(LoggerFactory.getLogger(DataProcessingExample.class), "uppercase")) .withStep(new TransformationStep<>(s -> s.replaceAll("\\s+", "_"))) .withConditionalStep(s -> s.length() > 10, new TransformationStep<>(s -> s.substring(0, 10))) .build(); String result = pipeline.execute("hello world"); System.out.println("Result: " + result); // "HELLO_WORL" } }

Circuit Breaker Usage

public class ExternalServiceClient { private final CircuitBreaker circuitBreaker; private final RestTemplate restTemplate; public ExternalServiceClient(CircuitBreakerRegistry registry) { this.circuitBreaker = registry.getBreaker("external-service", CircuitBreakerConfig.builder() .failureThreshold(5) .resetTimeout(Duration.ofMinutes(1)) .build()); this.restTemplate = new RestTemplate(); } public String callExternalService() { return circuitBreaker.execute(() -> { ResponseEntity<String> response = restTemplate.getForEntity( "https://api.example.com/data", String.class); return response.getBody(); }); } }

These custom patterns provide robust, reusable solutions for common challenges in modern Java applications, including resilience, data processing, domain modeling, and API design.

Leave a Reply

Your email address will not be published. Required fields are marked *


Macro Nepal Helper