MTI TEK
  • Home
  • About
  • LLMs
  • Docker
  • Kubernetes
  • Java
  • All Resources
Java Design Patterns | Strategy
  1. Overview and Definition
  2. Class Diagram
  3. Implementation Example
    • The Strategy Interface
    • The Concrete Strategies
    • The Context
    • The Domain Object
    • Testing the Strategy Pattern

  1. Overview and Definition
    The Strategy pattern is a behavioral design pattern that enables selecting an algorithm's behavior at runtime. It defines a family of algorithms, encapsulates each algorithm, and makes the algorithms interchangeable within that family. Strategy lets the algorithm vary independently from clients that use it.

    Key Components:
    • Strategy: Defines the interface common to all concrete strategies and declares a method the context uses to execute a strategy.
    • Concrete Strategy: Implements different algorithms using the Strategy interface.
    • Context: Maintains a reference to a Strategy object and delegates algorithm execution to the strategy.
    • Client: Creates and configures the context with a specific strategy.

    Benefits:
    • Runtime Algorithm Selection: Allows switching algorithms dynamically based on different conditions or requirements.
    • Open/Closed Principle: New strategies can be added without modifying existing code, making the system extensible.
    • Elimination of Conditional Statements: Replaces complex if-else or switch statements with a cleaner object-oriented approach.
  2. Class Diagram
    Strategy Pattern Components:
    • Strategy: LoadBalancingStrategy interface - defines the algorithm interface for server selection strategies.
    • Concrete Strategies: RandomStrategy and RoundRobinStrategy classes - implement different load balancing algorithms.
    • Context: ServiceDiscovery class - uses a strategy to execute server selection in a service discovery system.
    • Domain Object: Server class - represents the servers being managed by the service discovery system.
    ┌─────────────────────────────────────┐
    │            <<interface>>            │
    │        LoadBalancingStrategy        │  ← Strategy
    ├─────────────────────────────────────┤
    │ + getInstance(List<Server>): Server │
    └─────────────────────────────────────┘
                  △
                  │ implements
                  │
    ┌──────────────────┬────────────────────┐
    │  RandomStrategy  │ RoundRobinStrategy │  ← Concrete Strategies
    ├──────────────────┼────────────────────┤
    │+ getInstance()   │+ getInstance()     │
    └──────────────────┴────────────────────┘
    
    
    ┌────────────────────────────────────────────────┐
    │                ServiceDiscovery                │  ← Context
    ├────────────────────────────────────────────────┤
    │ - loadBalancingStrategy: LoadBalancingStrategy │
    │ + ServiceDiscovery(LoadBalancingStrategy)      │
    │ + doRequest(List<Server>): void                │
    └────────────────────────────────────────────────┘
    
    
    ┌─────────────────────────────┐
    │          Server             │  ← Domain Object
    ├─────────────────────────────┤
    │ - id: String                │
    │ + Server(String)            │
    │ + doRequest(): void         │
    └─────────────────────────────┘
                    
  3. Implementation Example
    • The Strategy Interface:
      The Strategy interface defines the contract that all concrete strategies must follow. It declares the method that the context will use to execute the load balancing algorithm.
      public interface LoadBalancingStrategy {
          Server getInstance(final List<Server> servers);
      }
    • The Concrete Strategies:
      Concrete Strategies implement different load balancing algorithms using the Strategy interface. Each strategy provides a different approach to selecting servers from the available list.

      Random Strategy:
      public class RandomStrategy implements LoadBalancingStrategy {
          private final Random random = new Random();
      
          public Server getInstance(final List<Server> servers) {
              final int index = random.nextInt(servers.size());
              return servers.get(index);
          }
      }
      Round Robin Strategy:
      public class RoundRobinStrategy implements LoadBalancingStrategy {
          private final AtomicInteger index = new AtomicInteger(0);
      
          public Server getInstance(final List<Server> servers) {
              final int index = Math.abs(this.index.getAndIncrement());
              return servers.get(index % servers.size());
          }
      }
    • The Context:
      The Context maintains a reference to a LoadBalancingStrategy object and delegates the algorithm execution to the strategy. It provides a unified interface for service discovery operations without knowing the specific load balancing implementation.
      public class ServiceDiscovery {
          private final LoadBalancingStrategy loadBalancingStrategy;
      
          public ServiceDiscovery(LoadBalancingStrategy loadBalancingStrategy) {
              this.loadBalancingStrategy = loadBalancingStrategy;
          }
      
          public void doRequest(final List<Server> servers) {
              loadBalancingStrategy.getInstance(servers).doRequest();
          }
      }
    • The Domain Object:
      The Domain Object represents the servers that are managed by the service discovery system. Different load balancing strategies will select from these servers based on their algorithms.
      public class Server {
          private final String id;
      
          public Server(final String id) {
              this.id = id;
          }
      
          public void doRequest() {
              System.out.println("Server: " + id);
          }
      }
    • Testing the Strategy Pattern:
      The client code demonstrates how different load balancing strategies can be used interchangeably in a service discovery system. It shows creating service discovery instances with different strategies and executing requests with different behaviors.
      public class StrategyPatternTest {
          public static void main(String[] args) {
              // list of available servers
              final List<Server> servers = new ArrayList<>();
              servers.add(new Server("server1"));
              servers.add(new Server("server2"));
              servers.add(new Server("server3"));
      
              // Service Discovery with different load balancing strategies
              final ServiceDiscovery serviceDiscovery1 = new ServiceDiscovery(new RoundRobinStrategy());
              final ServiceDiscovery serviceDiscovery2 = new ServiceDiscovery(new RandomStrategy());
      
              System.out.println("-- RoundRobin Strategy:");
              IntStream.iterate(0, i -> i + 2).limit(6).forEach(i -> serviceDiscovery1.doRequest(servers));
      
              System.out.println("-- Random Strategy:");
              IntStream.iterate(0, i -> i + 2).limit(6).forEach(i -> serviceDiscovery2.doRequest(servers));
          }
      }
© 2025 mtitek