-
Notifications
You must be signed in to change notification settings - Fork 1
Base Policy
Unit: Murphy.Base.Policy
This unit contains the foundational interfaces and base classes used by all Murphy policies.
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.
- IPolicy Interface
- IExecutablePolicy Interface
- TPolicy Base Class
- TPolicyBuilder Generic Base
- Usage Examples
The root interface for all policy types.
type
IPolicy = interface
['{245776D9-D6F2-4472-BD64-C08A9E41C568}']
function IsHandled(AException: Exception): Boolean;
end;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
InheritsFromcheck, so derived exception types are also handled - Returns
Falseif no exception types were configured - Typically you don't call this directly - policies call it internally
Extends IPolicy with a single Execute method, and is the contract required for a policy to participate in a Policy Wrap.
type
IExecutablePolicy = interface(IPolicy)
['{79460F38-81AD-48CF-8A2A-F10028F86F1B}']
procedure Execute(AProc: TProc);
end;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.
Abstract base class providing common functionality for all policy implementations.
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(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
virtualto 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
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
FExceptionTypesarray - Uses
InheritsFromto check exception hierarchy - Returns
Trueon first match - Returns
Falseif no match or ifFExceptionTypesis 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;Generic base class for all policy builders, providing the foundation for fluent configuration.
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;-
TPolicy: IPolicy- The policy interface type this builder creates (e.g.,IRetryPolicy)
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;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
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;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;// 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;- Reference Counting: Automatic memory management
- Abstraction: Decouple interface from implementation
- Testability: Easy to mock for unit tests
- Flexibility: Swap implementations without changing client code
- Type Safety: Compile-time type checking
- Code Reuse: Common functionality in base class
- Fluent Interface: Enable method chaining with correct return types
- Code Reuse: Share common logic across all policies
- Consistency: Ensure all policies handle exceptions the same way
- Extensibility: Easy to add new policies following the same pattern
- Architecture - Design and structure
- Retry Policy API - Example of derived policy
- Circuit Breaker Policy API - Another derived policy
-
Policy Wrap API - Composing
IExecutablePolicyimplementations together - Creating Custom Policies - Extension guide