Key Takeaways: Interfaces, Abstract Classes, and Design Principles
-
Interfaces define contracts, not implementations. An interface declares what methods a class must provide, but contains no code, no fields, and no constructors. This makes interfaces the purest form of abstraction in Object Pascal.
-
A class can implement multiple interfaces. Unlike single inheritance (one parent class), a class can implement as many interfaces as needed. This models "can-do" relationships: a
TExpensecan be exported, compared, and validated — all at once. -
Always assign a GUID to your interfaces. Without a GUID, you cannot use
Supports(),as, orQueryInterfaceat runtime. Generate GUIDs with Ctrl+Shift+G in the IDE. -
TInterfacedObjectprovides automatic reference counting. Objects accessed exclusively through interface references are freed automatically when the last reference goes out of scope. This eliminates a major category of memory leaks — but requires discipline: do not mix interface and object references to the same instance. -
Abstract classes share code; interfaces share contracts. Use abstract classes when related classes need shared state and shared method implementations (e.g.,
TReportGeneratorwith the Template Method pattern). Use interfaces when unrelated classes need to fulfill the same behavioral contract (e.g.,IExportable). -
The combination of interface + abstract base class is powerful. Define the public contract as an interface, then provide an abstract base class implementing that interface with shared defaults. Client code programs against the interface; implementation classes inherit from the base class.
-
SOLID principles guide professional design: - Single Responsibility: one class, one reason to change. - Open/Closed: open for extension (new classes), closed for modification (existing code unchanged). - Liskov Substitution: subclasses/implementations must honor the contract. - Interface Segregation: prefer small, focused interfaces over large, monolithic ones. - Dependency Inversion: depend on abstractions (interfaces), not concrete classes.
-
Dependency injection makes code testable and flexible. Pass interfaces through constructors instead of creating concrete objects inside a class. This decouples components and allows easy swapping of implementations.
-
The Strategy pattern uses interfaces to swap algorithms at runtime. Define an interface for the algorithm, implement it in multiple classes, and let the client choose which implementation to use.
-
The Observer pattern uses interfaces for event notification. A subject maintains a list of observer interfaces and notifies them when state changes. Neither the subject nor the observers need to know each other's concrete types.