# Fallback Policy API **Unit**: `Murphy.Policy.Fallback` The Fallback policy provides alternative results when operations fail, enabling graceful degradation. ## Overview The Fallback pattern answers the question: "What should happen when an operation fails completely?" Instead of failing, return: - Cached data - Default values - Simplified/degraded functionality - Data from an alternative source ## Table of Contents - [IFallbackPolicy Interface](#ifallbackpolicy-interface) - [TFallbackPolicy Class](#tfallbackpolicy-class) - [TFallbackBuilder Class](#tfallbackbuilder-class) - [Usage Examples](#usage-examples) --- ## IFallbackPolicy Interface Generic interface for the Fallback policy pattern. ### Declaration ```pascal type IFallbackPolicy = interface(IPolicy) ['{FAF09E9F-03E5-4E80-B1D2-09DDFB0492A3}'] function Execute(AFunc: TFunc): TResult; function Fallback(AFunc: TFunc): IFallbackPolicy; end; ``` ### Type Parameters - `TResult` - The return type of the operations (e.g., `string`, `Integer`, `TJSONObject`) ### Methods #### Execute ```pascal function Execute(AFunc: TFunc): TResult; ``` **Description**: Executes the provided function, returning the fallback result if it fails. **Parameters**: - `AFunc: TFunc` - Function to execute **Returns**: `TResult` - Either the result of `AFunc` or the fallback value **Behavior**: 1. Attempts to execute `AFunc` 2. On success: Returns the result 3. On handled exception: - If fallback configured: Returns fallback result - If no fallback: Re-raises exception 4. On unhandled exception: Re-raises immediately **Example**: ```pascal var Policy: IFallbackPolicy; Data: string; begin Policy := TFallbackBuilder .Handle(Exception) .Fallback(function: string begin Result := 'Default Value'; end) .Build; Data := Policy.Execute( function: string begin Result := FetchDataFromAPI; // May fail end); WriteLn(Data); // Either API data or 'Default Value' end; ``` #### Fallback ```pascal function Fallback(AFunc: TFunc): IFallbackPolicy; ``` **Description**: Configures the fallback function that provides alternative results. **Parameters**: - `AFunc: TFunc` - Function that returns the fallback value **Returns**: `IFallbackPolicy` - Self for method chaining **Notes**: - Must be configured before calling `Execute`, otherwise exceptions are re-raised - Fallback function should ideally not throw exceptions - Can access closures/captured variables **Example**: ```pascal var CachedData: string; Policy: IFallbackPolicy; begin CachedData := 'Cached Value'; Policy := TFallbackBuilder .Handle(Exception) .Fallback(function: string begin WriteLn('Using cached data'); Result := CachedData; // Access captured variable end) .Build; end; ``` --- ## TFallbackPolicy Class Generic concrete implementation of the Fallback policy pattern. ### Declaration ```pascal type TFallbackPolicy = class(TPolicy, IFallbackPolicy) private FFallbackFunc: TFunc; public function Execute(AFunc: TFunc): TResult; function Fallback(AFunc: TFunc): IFallbackPolicy; end; ``` ### Implementation Details The `Execute` method (Murphy.Policy.Fallback.pas:46): 1. Wraps `AFunc` execution in try-except 2. On exception: - Checks if exception is handled (`IsHandled`) - Checks if fallback function is assigned - Returns fallback result if both true - Re-raises exception otherwise --- ## TFallbackBuilder Class Generic builder class for creating fallback policies. ### Declaration ```pascal type TFallbackBuilder = class sealed(TPolicyBuilder>) public class function Handle(AExceptionTypes: TArray): IFallbackPolicy; override; end; ``` ### Class Methods #### Handle ```pascal class function Handle(AExceptionTypes: TArray): IFallbackPolicy; ``` **Description**: Creates a new fallback policy for the specified result type and exception types. **Usage**: ```pascal // For string results Policy := TFallbackBuilder .Handle(Exception) .Fallback(function: string begin Result := 'default'; end) .Build; // For integer results PolicyInt := TFallbackBuilder .Handle(Exception) .Fallback(function: Integer begin Result := 0; end) .Build; // For object results PolicyObj := TFallbackBuilder .Handle(Exception) .Fallback(function: TJSONObject begin Result := TJSONObject.Create; end) .Build; ``` --- ## Usage Examples ### Example 1: Simple Fallback to Default Value ```pascal var Policy: IFallbackPolicy; Username: string; begin Policy := TFallbackBuilder .Handle(Exception) .Fallback(function: string begin Result := 'Guest'; // Default username end) .Build; Username := Policy.Execute( function: string begin Result := GetUsernameFromSession; // May fail end); WriteLn('Welcome, ', Username); // Either real username or 'Guest' end; ``` ### Example 2: Fallback to Cached Data ```pascal type TDataService = class private FCache: TJSONObject; public function GetData: TJSONObject; end; function TDataService.GetData: TJSONObject; var Policy: IFallbackPolicy; begin Policy := TFallbackBuilder .Handle([EIdHTTPProtocolException, EIdSocketError]) .Fallback(function: TJSONObject begin WriteLn('API unavailable - using cached data'); Result := FCache.Clone as TJSONObject; end) .Build; Result := Policy.Execute( function: TJSONObject begin Result := FetchFromAPI; // Try live data first end); end; ``` ### Example 3: Fallback Chain (Multiple Fallbacks) ```pascal var Policy1, Policy2: IFallbackPolicy; Data: string; begin // Inner fallback: Try database, fall back to default Policy1 := TFallbackBuilder .Handle(EDatabaseError) .Fallback(function: string begin Result := 'System Default'; end) .Build; // Outer fallback: Try API, fall back to database/default Policy2 := TFallbackBuilder .Handle(EIdHTTPProtocolException) .Fallback(function: string begin Result := Policy1.Execute( function: string begin Result := GetFromDatabase; end); end) .Build; // Tries: API → Database → Default Data := Policy2.Execute( function: string begin Result := GetFromAPI; end); end; ``` ### Example 4: Fallback with Different Result Types ```pascal // String fallback var StringPolicy := TFallbackBuilder .Handle(Exception) .Fallback(function: string begin Result := ''; end) .Build; // Integer fallback var IntPolicy := TFallbackBuilder .Handle(Exception) .Fallback(function: Integer begin Result := -1; end) .Build; // Boolean fallback var BoolPolicy := TFallbackBuilder .Handle(Exception) .Fallback(function: Boolean begin Result := False; end) .Build; // Object fallback (be careful with memory management!) var ObjPolicy := TFallbackBuilder .Handle(Exception) .Fallback(function: TStringList begin Result := TStringList.Create; Result.Add('Fallback Data'); end) .Build; ``` ### Example 5: Conditional Fallback ```pascal var Policy: IFallbackPolicy; LastKnownGood: string; begin LastKnownGood := 'Last successful response'; Policy := TFallbackBuilder .Handle(Exception) .Fallback(function: string begin if LastKnownGood <> '' then begin WriteLn('Using last known good value'); Result := LastKnownGood; end else begin WriteLn('No cached data, using default'); Result := 'No data available'; end; end) .Build; var Data := Policy.Execute( function: string begin Result := FetchLiveData; end); end; ``` --- ## Important Notes ### Memory Management When using fallback with objects, ensure proper memory management: ```pascal // WRONG - Potential memory leak Policy := TFallbackBuilder .Handle(Exception) .Fallback(function: TStringList begin Result := TStringList.Create; // Who owns this? end) .Build; // BETTER - Clear ownership var FallbackList: TStringList; begin FallbackList := TStringList.Create; try Policy := TFallbackBuilder .Handle(Exception) .Fallback(function: TStringList begin Result := FallbackList; // Return existing instance end) .Build; var Data := Policy.Execute(function: TStringList begin ... end); finally // Clean up based on ownership rules end; end; ``` ### Type Safety Delphi's generic system ensures type safety: ```pascal // Compile-time type checking var Policy := TFallbackBuilder.Handle(Exception).Build; // This won't compile - type mismatch // Policy.Fallback(function: string begin Result := 'test'; end); // This compiles - correct type Policy.Fallback(function: Integer begin Result := 0; end); ``` --- ## See Also - [Fallback Pattern Guide](../Patterns/Fallback.md) - Comprehensive usage guide - [Combining Patterns](../Patterns/Combining-Patterns.md) - Using fallback with retry and circuit breaker - [Base Policy API](Base-Policy.md) - Inherited functionality --- [← Circuit Breaker Policy](CircuitBreaker-Policy.md) | [API Reference Index](README.md) | [Rate Limit Policy →](RateLimit-Policy.md)