Unexported Struct Pointer Embedding
An exported struct embeds an unexported struct pointer
Prevents arbitrary creation of structs from external packages, enforcing the use of designated constructors or validation logic.
e.g.
1type completedConfig struct {
2 Options options.CompletedOptions
3 Aggregator aggregatorapiserver.CompletedConfig
4 KubeAPIs controlplane.CompletedConfig
5 ApiExtensions apiextensionsapiserver.CompletedConfig
6
7 ExtraConfig
8}
9
10type CompletedConfig struct {
11 // Embed a private pointer that cannot be instantiated outside of this package.
12 *completedConfig
13}
GoalEnforced Constructor
- Prevents arbitrary creation or modification of objects via struct literals from external packages
- External packages cannot create (initialize) structs with unexported fields using literals
- Enforces factory methods
- Ensures that essential validation or defaulting is completed before an object is created
Typical Encapsulation
1type CompletedConfig struct {
2 // Fields are unexported, so they are inaccessible from external packages
3 options options.CompletedOptions
4 aggregator aggregatorapiserver.CompletedConfig
5}
- Values cannot be read from outside
- Requires Getter methods for all fields
Unexported Pointer Embedding
1type completedConfig struct { // unexported struct
2 Options options.CompletedOptions // Exported Field!
3 Aggregator aggregatorapiserver.CompletedConfig // Exported Field!
4}
5
6type CompletedConfig struct {
7 *completedConfig // Embedding
8}
- From outside, it's possible to directly access fields like
cfg.Options, as if they were its own fields. - However, if you try to create
CompletedConfigitself ({}), literal initialization is blocked because the*completedConfigtype is unexported.
Advantages
- Creation Restriction (Safety): Prevents arbitrary creation using
CompletedConfig{...}(struct literal) (enforces constructor functions).- Access Convenience: When using the object, instead of tediously calling methods like
cfg.GetOptions(), you can directly access variables likecfg.Options(no need to write Getter methods).
Disadvantages
- Difficult to use with libraries that internally rely on reflection.
- Difficult to create Mocks or Stubs when writing unit test code.
Recommended for infrastructure objects or core library design where stability must be enhanced by fundamentally blocking incorrect usage.