Unexported Struct Pointer Embedding
Exported 인 구조체가 Unexported 구조체 포인터를 임베딩
외부 패키지에서 구조체를 임의로 생성하는 것을 막고,
반드시 정해진 생성자나 검증 로직을 거치도록 강제
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}
Goal생성자 강제 (Enforced Constructor)
- 외부 패키지에서 구조체 리터럴(Struct Literal)을 통해 객체를 임의로 생성하거나 수정하는 것을 방지
- 외부 패키지에서는 unexported 필드를 가진 구조체를 리터럴로 생성(초기화) 불가
- 팩토리 메서드 강제
- 객체가 생성되기 전에 필수적인 유효성 검사(Validation)나 기본값 설정(Defaulting)이 완료되었음을 보장
일반적인 캡슐화
1type CompletedConfig struct {
2 // 필드가 unexported이므로 외부 패키지에서 접근 불가
3 options options.CompletedOptions
4 aggregator aggregatorapiserver.CompletedConfig
5}
- 외부에서 값을 읽을 수 없음
- 모든 필드에 대해 Getter 메서드 필요
Unexported 포인터 임베딩
1type completedConfig struct { // unexported struct
2 Options options.CompletedOptions // Exported Field!
3 Aggregator aggregatorapiserver.CompletedConfig // Exported Field!
4}
5
6type CompletedConfig struct {
7 *completedConfig // 임베딩
8}
- 외부에서는 마치 자기 필드인 것처럼
cfg.Options로 직접 접근이 가능 - 하지만
CompletedConfig자체를 생성({})하려고 하면,*completedConfig타입이 unexported이므로 리터럴 초기화가 막힘
장점
- 생성 제한 (Safety):
CompletedConfig{...}(구조체 리터럴)로 함부로 만들지 못하게 막음 (생성자 함수 강제)- 접근 편의성 (Convenience): 객체를 사용할 때는
cfg.GetOptions()처럼 귀찮게 메서드를 호출하는 대신,cfg.Options처럼 변수에 바로 접근이 가능 (Getter 메서드 작성 불필요)
단점
- 내부적으로 리플렉션을 사용하는 라이브러리들의 사용이 어려움
- 단위 테스트 코드 작성시 Mock 이나 Stub 을 만들기 어려움
잘못된 사용을 원천 봉쇄하여 라이브러리의 안정성을 높여야 하는
인프라 개체나 핵심 라이브러리 설계시 사용 추천