package Parent is
type Item is private;
function Get return Item;
private
type Item is
record
Value : Boolean;
end record;
procedure Set
(Object : Item;
Value : Boolean);
end Parent;
Please tell me how in this example to prevent changing the Item record from child packages directly, leaving the ability to call the private method Set?
As Jere has pointed out, this is a consequence of using child pkgs to provide programming by extension. Programming by extension is generally a bad idea, as it emphasizes ease of writing over ease of reading, and violates S/W-engineering principles.
Jere presented the standard way to hide the actual type from child pkgs, using access types. This works, but as it involves manual memory management is error prone.
A way to avoid this problem with programming by extension without using access types is to use ... more programming by extension:
private -- Parent
type Root is abstract tagged null record;
function Equal (Left : in Root'Class; Right : in Root'Class) is
(Left = Right);
package Class_Holders is new Ada.Containers.Indefinite_Holders
(Element_Type => Root'Class, "=" => Equal);
type Item is record
Value : Class_Holders.Holder;
end record;
end Parent;
package body Parent is
type Real_Item is new Root with record
Value : Boolean;
end record;
You can store a Real_Item
in a Holder
. When retrieving the value, you have to convert it to Real_Item
:
R : Real_Item;
V : Item;
...
R.Value := True;
V.Value.Replace_Element (New_Item => R);
...
R := Real_Item (V.Value.Element);
There are ways to use this approach in which Root can be an interface type, and others where it cannot. I always use an abstract tagged type to avoid having to remember which is which.
The function Equal
is needed because class-wide types have no primitive operations (note that GNAT will compile this without Equal
and with no association for "="
, but this is a compiler error).