golang) unexported struct pointer embedding

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 을 만들기 어려움

잘못된 사용을 원천 봉쇄하여 라이브러리의 안정성을 높여야 하는
인프라 개체나 핵심 라이브러리 설계시 사용 추천


포스트
카테고리
시리즈