Skip to content

Base Policy

Marco Breveglieri edited this page Jul 20, 2026 · 2 revisions

Base Policy API

Unit: Murphy.Base.Policy

This unit contains the foundational interfaces and base classes used by all Murphy policies.

Overview

The base policy layer provides:

  • IPolicy - The root interface implemented by all policies
  • IExecutablePolicy - Interface for policies that execute a parameterless TProc, and can therefore participate in a Policy Wrap
  • TPolicy - Abstract base class with common functionality
  • TPolicyBuilder - Generic base class for fluent builders

All specific policy implementations (Retry, Circuit Breaker, etc.) inherit from these base types.

Table of Contents


IPolicy Interface

The root interface for all policy types.

Declaration

type
  IPolicy = interface
    ['{245776D9-D6F2-4472-BD64-C08A9E41C568}']
    function IsHandled(AException: Exception): Boolean;
  end;

Methods

IsHandled

function IsHandled(AException: Exception): Boolean;

Description: Determines if the policy should handle a given exception.

Parameters:

  • AException: Exception - The exception to check

Returns: Boolean

  • True - The policy handles this exception type
  • False - The policy does not handle this exception type

Usage:

var
  Policy: IRetryPolicy;
  Ex: Exception;
begin
  Policy := TRetryBuilder.Handle(EIdHTTPProtocolException).Retry(3).Build;

  Ex := EIdHTTPProtocolException.Create('Test');
  try
    if Policy.IsHandled(Ex) then
      WriteLn('This exception will be handled');
  finally
    Ex.Free;
  end;
end;

Notes:

  • Uses InheritsFrom check, so derived exception types are also handled
  • Returns False if no exception types were configured
  • Typically you don't call this directly - policies call it internally

IExecutablePolicy Interface

Extends IPolicy with a single Execute method, and is the contract required for a policy to participate in a Policy Wrap.

Declaration

type
  IExecutablePolicy = interface(IPolicy)
    ['{79460F38-81AD-48CF-8A2A-F10028F86F1B}']
    procedure Execute(AProc: TProc);
  end;

Methods

Execute

procedure Execute(AProc: TProc);

Description: Executes a parameterless action under the policy's resilience logic.

Parameters:

  • AProc: TProc - Anonymous procedure to execute

Implementers: IRetryPolicy, ICircuitBreakerPolicy, IRateLimitPolicy, ITimeoutPolicy, IBulkheadPolicy, IHedgingPolicy, and IPolicyWrap (which is itself composable, allowing wraps to nest inside other wraps).

Non-implementers: IFallbackPolicy<TResult> and ICachePolicy<TResult> do not implement this interface, since their Execute method takes a TFunc<TResult> and returns a value instead of taking a TProc. As a consequence, Fallback and Cache policies cannot be combined via Policy Wrap.


TPolicy Base Class

Abstract base class providing common functionality for all policy implementations.

Declaration

type
  TPolicy = class abstract(TInterfacedObject, IPolicy)
  private
    FExceptionTypes: TArray<ExceptClass>;
  public
    constructor Create(AExceptionTypes: TArray<ExceptClass>); virtual;
    function IsHandled(AException: Exception): Boolean;
  end;

Constructor

Create

constructor Create(AExceptionTypes: TArray<ExceptClass>); virtual;

Description: Initializes the policy with exception types to handle.

Parameters:

  • AExceptionTypes: TArray<ExceptClass> - Array of exception types this policy will handle

Usage:

// Typically called by builders, not directly by users
var
  ExceptionTypes: TArray<ExceptClass>;
begin
  SetLength(ExceptionTypes, 2);
  ExceptionTypes[0] := EIdHTTPProtocolException;
  ExceptionTypes[1] := EIdSocketError;

  // Policy subclass constructor
  Policy := TRetryPolicy.Create(ExceptionTypes, ...);
end;

Notes:

  • Marked virtual to allow descendant classes to override
  • Stores the exception types array for later use by IsHandled
  • Users typically don't call this directly - use builders instead

Methods

IsHandled

function IsHandled(AException: Exception): Boolean;

Description: Implements the IPolicy.IsHandled method by checking if the exception inherits from any configured exception type.

Implementation Details:

  • Iterates through FExceptionTypes array
  • Uses InheritsFrom to check exception hierarchy
  • Returns True on first match
  • Returns False if no match or if FExceptionTypes is empty

Source (Murphy.Base.Policy.pas:57):

function TPolicy.IsHandled(AException: Exception): Boolean;
begin
  if Assigned(FExceptionTypes) then
    for var LExceptClass in FExceptionTypes do
      if AException.InheritsFrom(LExceptClass) then
      begin
        Result := True;
        Exit;
      end;
  Result := False;
end;

TPolicyBuilder Generic Base

Generic base class for all policy builders, providing the foundation for fluent configuration.

Declaration

type
  TPolicyBuilder<TPolicy: IPolicy> = class
  public
    class function Handle(AExceptionType: ExceptClass): TPolicy; overload; virtual;
    class function Handle(AExceptionTypes: TArray<ExceptClass>): TPolicy; overload; virtual; abstract;
  end;

Type Parameters

  • TPolicy: IPolicy - The policy interface type this builder creates (e.g., IRetryPolicy)

Class Methods

Handle (Single Exception)

class function Handle(AExceptionType: ExceptClass): TPolicy; overload; virtual;

Description: Configures the policy to handle a single exception type.

Parameters:

  • AExceptionType: ExceptClass - Exception type to handle

Returns: TPolicy - The builder instance for method chaining

Usage:

var
  Policy: IRetryPolicy;
begin
  Policy := TRetryBuilder
    .Handle(EIdHTTPProtocolException)  // Handle single exception type
    .Retry(3)
    .Build;
end;

Implementation: Calls the array overload with a single-element array:

class function TPolicyBuilder<TPolicy>.Handle(AExceptionType: ExceptClass): TPolicy;
begin
  Result := Handle([AExceptionType]);
end;

Handle (Multiple Exceptions)

class function Handle(AExceptionTypes: TArray<ExceptClass>): TPolicy; overload; virtual; abstract;

Description: Configures the policy to handle multiple exception types.

Parameters:

  • AExceptionTypes: TArray<ExceptClass> - Array of exception types to handle

Returns: TPolicy - The builder instance for method chaining

Usage:

var
  Policy: IRetryPolicy;
begin
  Policy := TRetryBuilder
    .Handle([
      EIdHTTPProtocolException,
      EIdSocketError,
      EIdConnClosedGracefully
    ])
    .Retry(3)
    .Build;
end;

Notes:

  • Marked abstract - must be implemented by descendant builders
  • Allows specifying multiple exception types in one call
  • All specified exceptions will be handled by the policy

Usage Examples

Example 1: Understanding Exception Handling

uses
  Murphy.Base.Policy,
  Murphy.Policy.Retry;

var
  Policy: IRetryPolicy;
  HttpEx: EIdHTTPProtocolException;
  SocketEx: EIdSocketError;
  OtherEx: Exception;
begin
  // Create policy that handles HTTP exceptions
  Policy := TRetryBuilder
    .Handle(EIdHTTPProtocolException)
    .Retry(3)
    .Build;

  // Test exception handling
  HttpEx := EIdHTTPProtocolException.Create('HTTP error');
  try
    WriteLn('HTTP Exception: ', Policy.IsHandled(HttpEx));  // True
  finally
    HttpEx.Free;
  end;

  SocketEx := EIdSocketError.Create('Socket error');
  try
    WriteLn('Socket Exception: ', Policy.IsHandled(SocketEx));  // False
  finally
    SocketEx.Free;
  end;

  OtherEx := Exception.Create('Other error');
  try
    WriteLn('Generic Exception: ', Policy.IsHandled(OtherEx));  // False
  finally
    OtherEx.Free;
  end;
end;

Example 2: Handling Multiple Exception Types

uses
  Murphy.Base.Policy,
  Murphy.Policy.CircuitBreaker;

var
  Policy: ICircuitBreakerPolicy;
begin
  // Handle multiple exception types
  Policy := TCircuitBreakerBuilder
    .Handle([
      EIdHTTPProtocolException,
      EIdSocketError,
      EIdConnClosedGracefully
    ])
    .Fail(5)
    .Within(TTimeSpan.FromSeconds(30))
    .Build;

  // All three exception types will trigger the circuit breaker
  Policy.Execute(
    procedure
    begin
      MakeNetworkRequest;  // May throw any of the handled exceptions
    end);
end;

Example 3: Exception Inheritance

// Demonstrates that InheritsFrom works with exception hierarchies
var
  Policy: IRetryPolicy;
  SpecificEx: EIdHTTPProtocolException;
  GeneralEx: EIdException;
begin
  // Handle base exception class
  Policy := TRetryBuilder
    .Handle(EIdException)  // Base class
    .Retry(2)
    .Build;

  // Derived exceptions are also handled
  SpecificEx := EIdHTTPProtocolException.Create('HTTP error');
  try
    WriteLn('Specific exception handled: ', Policy.IsHandled(SpecificEx));  // True
  finally
    SpecificEx.Free;
  end;

  GeneralEx := EIdException.Create('General Indy error');
  try
    WriteLn('General exception handled: ', Policy.IsHandled(GeneralEx));  // True
  finally
    GeneralEx.Free;
  end;
end;

Design Rationale

Why Use Interfaces?

  1. Reference Counting: Automatic memory management
  2. Abstraction: Decouple interface from implementation
  3. Testability: Easy to mock for unit tests
  4. Flexibility: Swap implementations without changing client code

Why Use Generic Builder Base?

  1. Type Safety: Compile-time type checking
  2. Code Reuse: Common functionality in base class
  3. Fluent Interface: Enable method chaining with correct return types

Why Abstract Base Class?

  1. Code Reuse: Share common logic across all policies
  2. Consistency: Ensure all policies handle exceptions the same way
  3. Extensibility: Easy to add new policies following the same pattern

See Also


← API Reference Index | Retry Policy →

Clone this wiki locally