Delphi Private Visibility: Ensuring Class‑Level Encapsulation in Shared Units
0 reputation · 18 Jun 2021, 03:52 UTC
0 reputation · 18 Jun 2021, 03:52 UTC
The goal is to guarantee that a class’s internal fields remain inaccessible to other classes that reside in the same source unit, while keeping those classes together for compilation convenience.
In Delphi, the private specifier grants access to any code within the unit, not only to the enclosing class. This unit‑wide visibility can allow accidental reads or writes of another class’s private members, which may go unnoticed during code review.
Developers must decide whether to rely on strict private, split the unit into separate files, or adopt other conventions, weighing factors such as build time, maintenance overhead, and backward compatibility.
What practices best prevent unintended private‑member access when multiple classes share a unit?
Is there a reliable static‑analysis or compiler warning that can flag cross‑class private access without requiring a unit split?
34775 reputation · 18 Jun 2021, 08:15 UTC
To guarantee that a class's internal fields remain inaccessible to other classes within the same source unit, the most effective practice is to replace the private visibility specifier with strict private.
In Delphi, the private keyword does not provide true class-level encapsulation; it provides unit-level visibility. Any procedure, function, or class defined within the same unit can access the private members of any other class in that unit. This behavior is a legacy design intended to simplify the creation of helper functions and tightly coupled internal logic.
The strict private specifier restricts access exclusively to the class itself and its descendants. This prevents other classes in the same unit from accessing those members, effectively enforcing a hard boundary at the class level.
private to strict private.type
TInternalManager = class
strict private
FSecretKey: string; // Inaccessible to TExternalController
public
procedure Process;
end;
TExternalController = class
procedure UpdateManager(AMgr: TInternalManager);
end;
There is currently no native compiler warning or standard static-analysis flag in the Delphi compiler that can detect or alert you to cross-class private member access. Because the compiler treats unit-level access as legal behavior, it cannot flag it as a violation. To prevent these leaks, you must either use strict private or split the classes into separate units.
This solution assumes you are using a modern version of Delphi that supports the strict private modifier. Note that while strict private solves the encapsulation problem, it may break existing unit tests or internal utility functions that rely on unit-wide access. If you find yourself needing to grant access to a specific "friend" class, you may need to reconsider the unit architecture or use a protected interface.
Use comments to ask for clarification. Post a solution as an answer.
34,775 reputation · 18 Jun 2021, 11:31 UTC
While strict private is the standard solution for modern Delphi versions, it is important to note that this specifier is unavailable in legacy environments (Delphi 7 and earlier). For those maintaining legacy codebases, splitting classes into separate units is the only way to enforce encapsulation at the compiler level.
For teams using modern versions, a pragmatic middle ground is to treat the standard private specifier as a deliberate "friend class" pattern. Since the compiler treats unit-level access as legal, you can reserve private for members that should be accessible to tightly coupled helper classes in the same unit, while using strict private for truly internal state. This creates a clear architectural signal: if a member is private, it is an internal API for the unit; if it is strict private, it is a private implementation detail of the class.