### Spring4D Mocking Framework Example in Pascal Source: https://context7.com/sglienke/spring4d/llms.txt Demonstrates the usage of the Spring4D mocking framework in Pascal for unit testing. It shows how to create mocks for interfaces, set up mock behaviors (stubbing, return sequences, exception throwing), and verify method calls on mocked objects. This example requires the Spring4D library. ```pascal program MockingExample; uses Spring, Spring.Mocking, SysUtils; type IEmailService = interface ['{F1234567-89AB-CDEF-0123-456789ABCDEF}'] function SendEmail(const Recipient, Subject, Body: string): Boolean; function GetUnreadCount(const UserId: string): Integer; procedure MarkAsRead(MessageId: Integer); end; IUserRepository = interface ['{A1234567-89AB-CDEF-0123-456789ABCDEF}'] function GetUserEmail(UserId: Integer): string; function UserExists(UserId: Integer): Boolean; end; TNotificationService = class private FEmailService: IEmailService; FUserRepo: IUserRepository; public constructor Create(const EmailService: IEmailService; const UserRepo: IUserRepository); function NotifyUser(UserId: Integer; const Message: string): Boolean; end; constructor TNotificationService.Create(const EmailService: IEmailService; const UserRepo: IUserRepository); begin FEmailService := EmailService; FUserRepo := UserRepo; end; function TNotificationService.NotifyUser(UserId: Integer; const Message: string): Boolean; var Email: string; begin Result := False; if not FUserRepo.UserExists(UserId) then Exit; Email := FUserRepo.GetUserEmail(UserId); Result := FEmailService.SendEmail(Email, 'Notification', Message); end; var MockEmail: Mock; MockUserRepo: Mock; Service: TNotificationService; Success: Boolean; begin // Create mocks with dynamic behavior (returns default values for unconfigured methods) MockEmail := Mock.Create; MockUserRepo := Mock.Create; // Setup mock behaviors MockUserRepo.Setup.Returns(True).When.UserExists(Arg.IsAny); MockUserRepo.Setup.Returns('test@example.com').When.GetUserEmail(123); MockEmail.Setup.Returns(True).When.SendEmail( Arg.IsAny, Arg.IsAny, Arg.IsAny); // Create service with mocked dependencies Service := TNotificationService.Create(MockEmail, MockUserRepo); try // Execute Success := Service.NotifyUser(123, 'Hello World'); // Verify result Writeln('Notification sent: ', Success); // True // Verify mock interactions MockUserRepo.Received(Times.Once).UserExists(123); MockUserRepo.Received(Times.Once).GetUserEmail(123); MockEmail.Received(Times.Once).SendEmail('test@example.com', 'Notification', 'Hello World'); Writeln('All verifications passed!'); finally Service.Free; end; Writeln; Writeln('--- Testing with non-existent user ---'); // Reset mocks for next test MockEmail.Reset; MockUserRepo.Reset; // Setup for non-existent user MockUserRepo.Setup.Returns(False).When.UserExists(999); Service := TNotificationService.Create(MockEmail, MockUserRepo); try Success := Service.NotifyUser(999, 'Hello'); Writeln('Notification sent: ', Success); // False // Verify email was never called MockEmail.Received(Times.Never).SendEmail( Arg.IsAny, Arg.IsAny, Arg.IsAny); Writeln('Correctly skipped email for non-existent user'); finally Service.Free; end; Writeln; Writeln('--- Testing return sequences ---'); MockEmail.Reset; // Return different values on successive calls MockEmail.Setup.Returns([5, 3, 0]).When.GetUnreadCount(Arg.IsAny); Writeln('Unread count 1: ', MockEmail.Instance.GetUnreadCount('user1')); // 5 Writeln('Unread count 2: ', MockEmail.Instance.GetUnreadCount('user1')); // 3 Writeln('Unread count 3: ', MockEmail.Instance.GetUnreadCount('user1')); // 0 Writeln; Writeln('--- Testing exception throwing ---'); MockEmail.Reset; MockEmail.Setup.Raises('Service unavailable') .When.SendEmail('bad@email.com', Arg.IsAny, Arg.IsAny); try MockEmail.Instance.SendEmail('bad@email.com', 'Test', 'Body'); except on E: EInvalidOperation do Writeln('Caught expected exception: ', E.Message); end; end. ``` -------------------------------- ### Delphi DI Container Example Source: https://context7.com/sglienke/spring4d/llms.txt Demonstrates the usage of Spring4D's Dependency Injection container in Delphi. It shows how to register types, build the container, and resolve services with automatic dependency injection. Requires Spring4D framework. ```Delphi program DIContainerExample; uses Spring.Container, SysUtils; type // Define interfaces ILogger = interface ['{A1B2C3D4-E5F6-7890-ABCD-EF1234567890}'] procedure Log(const Message: string); end; IOrderRepository = interface ['{B2C3D4E5-F6A7-8901-BCDE-F12345678901}'] procedure Save(const OrderId: Integer); end; IOrderService = interface ['{C3D4E5F6-A7B8-9012-CDEF-123456789012}'] procedure ProcessOrder(OrderId: Integer); end; // Implement classes TConsoleLogger = class(TInterfacedObject, ILogger) procedure Log(const Message: string); end; TOrderRepository = class(TInterfacedObject, IOrderRepository) private FLogger: ILogger; public constructor Create(const Logger: ILogger); procedure Save(const OrderId: Integer); end; TOrderService = class(TInterfacedObject, IOrderService) private FRepository: IOrderRepository; FLogger: ILogger; public constructor Create(const Repository: IOrderRepository; const Logger: ILogger); procedure ProcessOrder(OrderId: Integer); end; procedure TConsoleLogger.Log(const Message: string); begin Writeln('[LOG] ', Message); end; constructor TOrderRepository.Create(const Logger: ILogger); begin FLogger := Logger; end; procedure TOrderRepository.Save(const OrderId: Integer); begin FLogger.Log('Saving order: ' + IntToStr(OrderId)); end; constructor TOrderService.Create(const Repository: IOrderRepository; const Logger: ILogger); begin FRepository := Repository; FLogger := Logger; end; procedure TOrderService.ProcessOrder(OrderId: Integer); begin FLogger.Log('Processing order: ' + IntToStr(OrderId)); FRepository.Save(OrderId); FLogger.Log('Order processed successfully'); end; var Container: TContainer; OrderService: IOrderService; begin // Use global container or create a new one Container := TContainer.Create; try // Register types with the container Container.RegisterType.Implements.AsSingleton; Container.RegisterType.Implements; Container.RegisterType.Implements; // Build the container (validates and prepares registrations) Container.Build; // Resolve and use the service - dependencies are automatically injected OrderService := Container.Resolve; OrderService.ProcessOrder(12345); // Output: // [LOG] Processing order: 12345 // [LOG] Saving order: 12345 // [LOG] Order processed successfully finally Container.Free; end; end. ``` -------------------------------- ### Spring4D Collections Framework Example Source: https://context7.com/sglienke/spring4d/llms.txt Demonstrates the usage of the Spring4D Collections Framework, including creating lists, filtering, ordering, projecting, aggregating, and using dictionaries. It showcases fluent method chaining for common collection operations. ```pascal program CollectionsExample; uses Spring, Spring.Collections, SysUtils; type TCustomer = class public Id: Integer; Name: string; Country: string; TotalOrders: Integer; constructor Create(AId: Integer; const AName, ACountry: string; AOrders: Integer); end; constructor TCustomer.Create(AId: Integer; const AName, ACountry: string; AOrders: Integer); begin Id := AId; Name := AName; Country := ACountry; TotalOrders := AOrders; end; var Customers: IList; Customer: TCustomer; USCustomers: IEnumerable; TopCustomers: IEnumerable; Names: IEnumerable; TotalOrders: Integer; begin // Create a list that owns its objects (automatic cleanup) Customers := TCollections.CreateObjectList(True); // Add items Customers.Add(TCustomer.Create(1, 'Alice', 'USA', 50)); Customers.Add(TCustomer.Create(2, 'Bob', 'Canada', 30)); Customers.Add(TCustomer.Create(3, 'Charlie', 'USA', 75)); Customers.Add(TCustomer.Create(4, 'Diana', 'UK', 20)); Customers.Add(TCustomer.Create(5, 'Eve', 'USA', 100)); // Filter with Where USCustomers := Customers.Where( function(const C: TCustomer): Boolean begin Result := C.Country = 'USA'; end); Writeln('US Customers:'); for Customer in USCustomers do Writeln(' ', Customer.Name, ' - ', Customer.TotalOrders, ' orders'); // Output: Alice - 50, Charlie - 75, Eve - 100 // Chain operations: filter, order, take TopCustomers := Customers .Where(function(const C: TCustomer): Boolean begin Result := C.TotalOrders >= 30; end) .OrderByDescending( function(const C: TCustomer): Integer begin Result := C.TotalOrders; end) .Take(3); Writeln('Top 3 Customers (30+ orders):'); for Customer in TopCustomers do Writeln(' ', Customer.Name, ': ', Customer.TotalOrders); // Output: Eve: 100, Charlie: 75, Alice: 50 // Project to different type with Select Names := Customers.Select( function(const C: TCustomer): string begin Result := C.Name + ' (' + C.Country + ')'; end); Writeln('Customer Names:'); for var S in Names do Writeln(' ', S); // Aggregate functions TotalOrders := Customers.Sum( function(const C: TCustomer): Integer begin Result := C.TotalOrders; end); Writeln('Total orders across all customers: ', TotalOrders); // 275 // Check conditions if Customers.Any(function(const C: TCustomer): Boolean begin Result := C.TotalOrders > 90; end) then Writeln('We have premium customers!'); // First/Last with predicates Customer := Customers.First( function(const C: TCustomer): Boolean begin Result := C.Country = 'UK'; end); Writeln('First UK customer: ', Customer.Name); // Diana // Dictionary example var Dict := TCollections.CreateDictionary; Dict.Add('apples', 10); Dict.Add('oranges', 5); Dict['bananas'] := 7; for var Pair in Dict do Writeln(Pair.Key, ': ', Pair.Value); end. ``` -------------------------------- ### Observer Pattern Example in Delphi using Spring4D Source: https://context7.com/sglienke/spring4d/llms.txt This Pascal code demonstrates the Observer pattern using Spring4D's TObservable. It shows how to create observers and a subject (TStockTicker) that notifies observers of price updates. Observers can subscribe and unsubscribe from notifications. ```pascal program ObserverExample; uses Spring.DesignPatterns, SysUtils; type TStockPrice = record Symbol: string; Price: Double; Change: Double; end; TStockObserver = class private FName: string; public constructor Create(const Name: string); procedure OnPriceUpdate(const Stock: TStockPrice); property Name: string read FName; end; TStockTicker = class(TObservable) public procedure UpdatePrice(const Symbol: string; Price, Change: Double); procedure DoNotify(const Observer: TStockObserver); override; private FCurrentStock: TStockPrice; end; constructor TStockObserver.Create(const Name: string); begin FName := Name; end; procedure TStockObserver.OnPriceUpdate(const Stock: TStockPrice); var Direction: string; begin if Stock.Change >= 0 then Direction := '+' else Direction := ''; Writeln(Format(' [%s] %s: $%.2f (%s%.2f%%)', [FName, Stock.Symbol, Stock.Price, Direction, Stock.Change])); end; procedure TStockTicker.UpdatePrice(const Symbol: string; Price, Change: Double); begin FCurrentStock.Symbol := Symbol; FCurrentStock.Price := Price; FCurrentStock.Change := Change; Writeln('Stock update: ', Symbol); Notify; // Notifies all observers end; procedure TStockTicker.DoNotify(const Observer: TStockObserver); begin Observer.OnPriceUpdate(FCurrentStock); end; var Ticker: TStockTicker; Trader1, Trader2, Analyst: TStockObserver; begin Ticker := TStockTicker.Create; Trader1 := TStockObserver.Create('Trader1'); Trader2 := TStockObserver.Create('Trader2'); Analyst := TStockObserver.Create('Analyst'); try // Subscribe observers Ticker.Subscribe(Trader1); Ticker.Subscribe(Trader2); Ticker.Subscribe(Analyst); // Price updates notify all subscribers Ticker.UpdatePrice('AAPL', 150.25, 2.5); Writeln; Ticker.UpdatePrice('GOOGL', 2750.00, -1.2); Writeln; // Unsubscribe one observer Ticker.Unsubscribe(Trader2); Writeln('Trader2 unsubscribed'); Writeln; Ticker.UpdatePrice('MSFT', 305.50, 0.8); // Output: // Stock update: AAPL // [Trader1] AAPL: $150.25 (+2.50%) // [Trader2] AAPL: $150.25 (+2.50%) // [Analyst] AAPL: $150.25 (+2.50%) // // Stock update: GOOGL // [Trader1] GOOGL: $2750.00 (-1.20%) // [Trader2] GOOGL: $2750.00 (-1.20%) // [Analyst] GOOGL: $2750.00 (-1.20%) // // Trader2 unsubscribed // // Stock update: MSFT // [Trader1] MSFT: $305.50 (+0.80%) // [Analyst] MSFT: $305.50 (+0.80%) finally Analyst.Free; Trader2.Free; Trader1.Free; Ticker.Free; end; end. ``` -------------------------------- ### Implement Multicast Events with Spring4D in Pascal Source: https://context7.com/sglienke/spring4d/llms.txt This Pascal code demonstrates the implementation and usage of Spring4D's multicast event system. It shows how to define events, subscribe multiple handlers (EmailNotifier, SMSNotifier, DashboardUpdater), and manage subscriptions by adding, removing, enabling, disabling, and clearing handlers. The example utilizes a TOrderManager class to manage order status changes and trigger events. ```pascal program EventsExample; uses Spring, Spring.Events, SysUtils, Classes; type TOrderStatus = (osPending, osProcessing, osShipped, osDelivered); TOrderStatusChangedEvent = procedure(Sender: TObject; OrderId: Integer; NewStatus: TOrderStatus) of object; TOrderManager = class private FOnStatusChanged: Event; public procedure ChangeStatus(OrderId: Integer; Status: TOrderStatus); property OnStatusChanged: Event read FOnStatusChanged; end; TEmailNotifier = class public procedure HandleStatusChange(Sender: TObject; OrderId: Integer; NewStatus: TOrderStatus); end; TSMSNotifier = class public procedure HandleStatusChange(Sender: TObject; OrderId: Integer; NewStatus: TOrderStatus); end; TDashboardUpdater = class public procedure HandleStatusChange(Sender: TObject; OrderId: Integer; NewStatus: TOrderStatus); end; procedure TOrderManager.ChangeStatus(OrderId: Integer; Status: TOrderStatus); const StatusNames: array[TOrderStatus] of string = ('Pending', 'Processing', 'Shipped', 'Delivered'); begin Writeln('Order ', OrderId, ' status changing to: ', StatusNames[Status]); // Invoke fires all subscribed handlers if FOnStatusChanged.CanInvoke then FOnStatusChanged.Invoke(Self, OrderId, Status); end; procedure TEmailNotifier.HandleStatusChange(Sender: TObject; OrderId: Integer; NewStatus: TOrderStatus); begin Writeln(' [Email] Sending notification for order ', OrderId); end; procedure TSMSNotifier.HandleStatusChange(Sender: TObject; OrderId: Integer; NewStatus: TOrderStatus); begin Writeln(' [SMS] Texting customer about order ', OrderId); end; procedure TDashboardUpdater.HandleStatusChange(Sender: TObject; OrderId: Integer; NewStatus: TOrderStatus); begin Writeln(' [Dashboard] Refreshing display for order ', OrderId); end; var OrderManager: TOrderManager; EmailNotifier: TEmailNotifier; SMSNotifier: TSMSNotifier; Dashboard: TDashboardUpdater; begin OrderManager := TOrderManager.Create; EmailNotifier := TEmailNotifier.Create; SMSNotifier := TSMSNotifier.Create; Dashboard := TDashboardUpdater.Create; try // Subscribe multiple handlers to the same event OrderManager.OnStatusChanged.Add(EmailNotifier.HandleStatusChange); OrderManager.OnStatusChanged.Add(SMSNotifier.HandleStatusChange); OrderManager.OnStatusChanged.Add(Dashboard.HandleStatusChange); // All handlers are called when event fires OrderManager.ChangeStatus(1001, osProcessing); // Output: // Order 1001 status changing to: Processing // [Email] Sending notification for order 1001 // [SMS] Texting customer about order 1001 // [Dashboard] Refreshing display for order 1001 Writeln; // Remove a specific handler OrderManager.OnStatusChanged.Remove(SMSNotifier.HandleStatusChange); OrderManager.ChangeStatus(1001, osShipped); // Output: // Order 1001 status changing to: Shipped // [Email] Sending notification for order 1001 // [Dashboard] Refreshing display for order 1001 Writeln; // Disable event temporarily OrderManager.OnStatusChanged.Enabled := False; OrderManager.ChangeStatus(1001, osDelivered); // No handlers called // Output: Order 1001 status changing to: Delivered (no handler output) // Re-enable OrderManager.OnStatusChanged.Enabled := True; // Clear all handlers OrderManager.OnStatusChanged.Clear; finally Dashboard.Free; SMSNotifier.Free; EmailNotifier.Free; OrderManager.Free; end; end. ``` -------------------------------- ### Lazy Initialization with Factory Function in Pascal Source: https://context7.com/sglienke/spring4d/llms.txt This Pascal code demonstrates lazy initialization of an expensive resource using Spring4D's `Lazy` type. It includes a factory function to create the resource and optional ownership for automatic cleanup. The example shows how the resource is only created upon its first access. ```Pascal program LazyExample; uses Spring, SysUtils; type TExpensiveResource = class private FData: string; public constructor Create; destructor Destroy; override; procedure DoWork; property Data: string read FData; end; TResourceManager = class private FResource: Lazy; public constructor Create; function GetResource: TExpensiveResource; function IsResourceCreated: Boolean; end; constructor TExpensiveResource.Create; begin inherited; Writeln('TExpensiveResource.Create - Expensive initialization!'); FData := 'Loaded at ' + TimeToStr(Now); Sleep(100); // Simulate expensive operation end; destructor TExpensiveResource.Destroy; begin Writeln('TExpensiveResource.Destroy'); inherited; end; procedure TExpensiveResource.DoWork; begin Writeln('Working with: ', FData); end; constructor TResourceManager.Create; begin inherited; // Initialize lazy with factory function and ownership (True = auto-free) FResource := Lazy.Create( function: TExpensiveResource begin Result := TExpensiveResource.Create; end, True // ownsObject - automatically freed when Lazy goes out of scope ); end; function TResourceManager.GetResource: TExpensiveResource; begin // First access triggers creation Result := FResource.Value; end; function TResourceManager.IsResourceCreated: Boolean; begin Result := FResource.IsValueCreated; end; var Manager: TResourceManager; Resource: TExpensiveResource; LazyInt: Lazy; begin // Example 1: Lazy with factory function Manager := TResourceManager.Create; try Writeln('Manager created'); Writeln('Resource created yet? ', Manager.IsResourceCreated); // False Writeln('First access to resource...'); Resource := Manager.GetResource; // Creation happens here Writeln('Resource created now? ', Manager.IsResourceCreated); // True Resource.DoWork; Writeln('Second access (no new creation)...'); Resource := Manager.GetResource; // Returns cached instance Resource.DoWork; finally Manager.Free; end; Writeln; Writeln('--- Simple Lazy Example ---'); // Example 2: Simple value type with factory LazyInt := Lazy.Create( function: Integer begin Writeln('Computing value...'); Result := 42 * 2; end); Writeln('LazyInt defined but not accessed'); Writeln('IsValueCreated: ', LazyInt.IsValueCreated); // False Writeln('Accessing value: ', LazyInt.Value); // Triggers computation Writeln('IsValueCreated: ', LazyInt.IsValueCreated); // True Writeln('Accessing again: ', LazyInt.Value); // Returns cached value // Example 3: Direct value assignment var DirectLazy: Lazy; DirectLazy := 'Hello Lazy World'; // Implicit conversion Writeln('Direct value: ', DirectLazy.Value); end. ``` -------------------------------- ### Pascal Argument Validation with Guard Class Source: https://context7.com/sglienke/spring4d/llms.txt Demonstrates how to use the Guard class in Pascal for validating method arguments. It covers checks for null, range, and custom conditions, along with handling exceptions. This promotes defensive programming and self-documenting code. ```pascal program GuardExample; uses Spring, SysUtils, Classes; type TCustomer = class private FName: string; FEmail: string; FAge: Integer; FOrders: TStringList; public constructor Create(const Name, Email: string; Age: Integer); destructor Destroy; override; procedure AddOrder(const OrderId: string); procedure ProcessOrders(Count: Integer); procedure UpdateProfile(const NewName: string; NewAge: Integer); end; constructor TCustomer.Create(const Name, Email: string; Age: Integer); begin inherited Create; // Validate arguments Guard.CheckNotNull(Name, 'Name'); // Raises EArgumentNilException if empty Guard.CheckNotNull(Email, 'Email'); Guard.CheckRange(Age >= 0, 'Age'); // Raises EArgumentOutOfRangeException Guard.CheckRange(Age <= 150, 'Age'); // More expressive validation Guard.CheckTrue(Pos('@', Email) > 0, 'Email must contain @'); FName := Name; FEmail := Email; FAge := Age; FOrders := TStringList.Create; end; destructor TCustomer.Destroy; begin FOrders.Free; inherited; end; procedure TCustomer.AddOrder(const OrderId: string); begin // Validate order ID format Guard.CheckNotNull(OrderId, 'OrderId'); Guard.CheckTrue(Length(OrderId) >= 5, 'OrderId must be at least 5 characters'); FOrders.Add(OrderId); Writeln('Added order: ', OrderId); end; procedure TCustomer.ProcessOrders(Count: Integer); begin // Validate count is within valid range Guard.CheckRange(Count > 0, 'Count'); Guard.CheckRange(Count <= FOrders.Count, 'Count'); Writeln('Processing ', Count, ' orders'); end; procedure TCustomer.UpdateProfile(const NewName: string; NewAge: Integer); begin // Combined validations Guard.CheckNotNull(NewName, 'NewName'); Guard.CheckRangeInclusive(NewAge, 0, 150); // Must be between 0 and 150 FName := NewName; FAge := NewAge; Writeln('Profile updated: ', FName, ', age ', FAge); end; // Example with generic type validation procedure ProcessItems(const Items: TArray; Index: Integer); begin Guard.CheckNotNull>(Items, 'Items'); Guard.CheckIndex(Length(Items), Index); // Validates 0 <= Index < Length Writeln('Processing item at index ', Index); end; var Customer: TCustomer; Numbers: TArray; begin // Valid creation try Customer := TCustomer.Create('John Doe', 'john@example.com', 30); try Customer.AddOrder('ORD-12345'); Customer.AddOrder('ORD-67890'); Customer.ProcessOrders(2); Customer.UpdateProfile('John Smith', 31); finally Customer.Free; end; except on E: EArgumentException do Writeln('Argument error: ', E.Message); end; Writeln; Writeln('--- Testing validation failures ---'); // Test null name try Customer := TCustomer.Create('', 'test@test.com', 25); except on E: EArgumentNilException do Writeln('Caught: ', E.Message); // Name cannot be null end; // Test invalid email try Customer := TCustomer.Create('Test', 'invalid-email', 25); except on E: EArgumentException do Writeln('Caught: ', E.Message); // Email must contain @ end; // Test out of range try Customer := TCustomer.Create('Test', 'test@test.com', -5); except on E: EArgumentOutOfRangeException do Writeln('Caught: ', E.Message); // Age out of range end; // Test array index validation Numbers := [10, 20, 30]; try ProcessItems(Numbers, 5); // Invalid index except on E: EArgumentOutOfRangeException do Writeln('Caught: Index out of range'); end; end. ``` -------------------------------- ### Nullable Type Usage in Pascal (Spring4D) Source: https://context7.com/sglienke/spring4d/llms.txt Demonstrates the usage of Nullable types in Pascal with Spring4D. It covers initialization, assigning values, checking for presence of a value, safe retrieval, default value handling, comparisons, and string conversion. This is useful for scenarios requiring explicit representation of null states, such as database interactions. ```pascal program NullableExample; uses Spring, SysUtils; procedure ProcessOptionalValue(const Value: Nullable); begin if Value.HasValue then Writeln('Value is: ', Value.Value) else Writeln('Value is null'); end; procedure CalculateDiscount(const Discount: Nullable); begin // Use GetValueOrDefault to provide a fallback Writeln('Applying discount: ', Discount.GetValueOrDefault(0.0):0:2, '%'); end; var Age: Nullable; Score: Nullable; Name: Nullable; IsActive: Nullable; BirthDate: Nullable; begin // Initially null (no value) Age := nil; // Using nil assignment ProcessOptionalValue(Age); // Output: Value is null // Assign a value Age := 25; ProcessOptionalValue(Age); // Output: Value is: 25 // Check HasValue before accessing if Age.HasValue then Writeln('Age is set to: ', Age.Value); // Use TryGetValue for safe access var TempAge: Integer; if Age.TryGetValue(TempAge) then Writeln('Got age: ', TempAge); // GetValueOrDefault with custom default Score := nil; Writeln('Score: ', Score.GetValueOrDefault(100.0):0:1); // Output: 100.0 // Comparison with other nullables var Score2: Nullable; Score2 := nil; if Score = Score2 then // Both null = equal Writeln('Both scores are null'); Score := 85.5; Score2 := 85.5; if Score = Score2 then // Same values = equal Writeln('Scores are equal'); // Compare with regular value if Score = 85.5 then Writeln('Score matches!'); // String nullable Name := nil; Writeln('Name: ', Name.GetValueOrDefault('Unknown')); // Unknown Name := 'John'; Writeln('Name: ', Name.Value); // John // Boolean nullable (three-state logic) IsActive := nil; // Unknown state if not IsActive.HasValue then Writeln('Active status unknown'); IsActive := True; if IsActive.Value then Writeln('User is active'); // DateTime nullable BirthDate := nil; if not BirthDate.HasValue then Writeln('Birth date not provided'); BirthDate := EncodeDate(1990, 5, 15); Writeln('Birth date: ', DateToStr(BirthDate.Value)); // Using with procedures CalculateDiscount(nil); // Output: Applying discount: 0.00% CalculateDiscount(15.0); // Output: Applying discount: 15.00% // Convert to string representation Age := 30; Writeln('Age as string: ', Age.ToString); // "30" Age := nil; Writeln('Null age as string: ', Age.ToString); // "" (empty) end. ``` -------------------------------- ### Decorator Pattern with DI Container in Pascal Source: https://context7.com/sglienke/spring4d/llms.txt This Pascal code demonstrates the Decorator pattern using the Spring4D DI container. It defines an interface `IOrderEntry`, a base implementation `TOrderEntry`, and two decorators: `TOrderEntryLoggingDecorator` and `TOrderEntryTransactionDecorator`. The container is configured to automatically apply these decorators when resolving `IOrderEntry`, allowing for the addition of logging and transaction management around the core order entry functionality. ```pascal program DecoratorExample; uses Spring.Container, SysUtils; type IOrderEntry = interface ['{8D272909-3324-4849-A128-C85E249520CD}'] function EnterOrder(OrderId: Integer): Boolean; end; // Base implementation TOrderEntry = class(TInterfacedObject, IOrderEntry) public function EnterOrder(OrderId: Integer): Boolean; end; // Logging decorator TOrderEntryLoggingDecorator = class(TInterfacedObject, IOrderEntry) private FInner: IOrderEntry; public constructor Create(const Inner: IOrderEntry); function EnterOrder(OrderId: Integer): Boolean; end; // Transaction decorator TOrderEntryTransactionDecorator = class(TInterfacedObject, IOrderEntry) private FInner: IOrderEntry; public constructor Create(const Inner: IOrderEntry); function EnterOrder(OrderId: Integer): Boolean; end; function TOrderEntry.EnterOrder(OrderId: Integer): Boolean; begin Writeln(' [Core] Saving order ', OrderId, ' to database'); Result := True; end; constructor TOrderEntryLoggingDecorator.Create(const Inner: IOrderEntry); begin FInner := Inner; end; function TOrderEntryLoggingDecorator.EnterOrder(OrderId: Integer): Boolean; begin Writeln('[LOG] EnterOrder called with ID: ', OrderId); Result := FInner.EnterOrder(OrderId); Writeln('[LOG] EnterOrder completed, result: ', Result); end; constructor TOrderEntryTransactionDecorator.Create(const Inner: IOrderEntry); begin FInner := Inner; end; function TOrderEntryTransactionDecorator.EnterOrder(OrderId: Integer): Boolean; begin Writeln('[TXN] Beginning transaction'); try Result := FInner.EnterOrder(OrderId); Writeln('[TXN] Committing transaction'); except Writeln('[TXN] Rolling back transaction'); raise; end; end; var Container: TContainer; OrderEntry: IOrderEntry; begin Container := TContainer.Create; try // Register base implementation Container.RegisterType.Implements; // Register decorators - they wrap in reverse registration order // So: Transaction wraps Logging wraps Core Container.RegisterDecorator; Container.RegisterDecorator; Container.Build; // Resolve gets fully decorated instance OrderEntry := Container.Resolve; Writeln('Calling EnterOrder(12345):'); Writeln; OrderEntry.EnterOrder(12345); // Output: // [TXN] Beginning transaction // [LOG] EnterOrder called with ID: 12345 // [Core] Saving order 12345 to database // [LOG] EnterOrder completed, result: True // [TXN] Committing transaction finally Container.Free; end; end. ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.