golang 디자인 모델 - kubernetes 소스 코드 를 예 로 들 면

20648 단어
개술
design pattern 소개 디자인 모델 engineer pattern 정리 선진 적 인 공정 모델
design pattern
레 퍼 런 스
  • github.com/senghoo/gol…
  • github.com/yksz/go-des…
  • github.com/svett/golan…

  • simplefactory
    golang 에 게 는 Newxx 함수 입 니 다. interface 로 돌아 갑 니 다. kubernetes interface 는 어디서나 볼 수 있 습 니 다. interface 로 추상 적 인 것 은 interface 라 고 할 수 있 습 니 다. 예 를 들 어 보 세 요.
    // k8s.io/kubernetes/vendor/k8s.io/client-go/tools/cache/store.go
    func NewStore(keyFunc KeyFunc) Store {
        return &cache{
            cacheStorage: NewThreadSafeStore(Indexers{}, Indices{}),
            keyFunc:      keyFunc,
        }
    }
    
    type cache struct {
        // cacheStorage bears the burden of thread safety for the cache
        cacheStorage ThreadSafeStore
        // keyFunc is used to make the key for objects stored in and retrieved from items, and
        // should be deterministic.
        keyFunc KeyFunc
    }
    
    type Store interface {
        Add(obj interface{}) error
        Update(obj interface{}) error
        Delete(obj interface{}) error
        List() []interface{}
        ListKeys() []string
        Get(obj interface{}) (item interface{}, exists bool, err error)
        GetByKey(key string) (item interface{}, exists bool, err error)
    
        // Replace will delete the contents of the store, using instead the
        // given list. Store takes ownership of the list, you should not reference
        // it after calling this function.
        Replace([]interface{}, string) error
        Resync() error
    }

    facade / adapter / decorator / delegate / bridge / mediator / composite
    조합 모델 의 서로 다른 형식 은 곳곳에서 볼 수 있 고 그 중의 차 이 를 깊이 연구 할 필요 도 없다.
    singleton
    kubernetes / golang 은 거의 사용 하지 않 습 니 다. 일반적으로 전역 변수 (예 를 들 어 설정) (예 를 들 어 net / http package 의 http. DefaultClient 와 http. Default ServeMux) 를 사용 하거나 context 로 전 달 됩 니 다.실현 방식 은 marcio. io / 2015 / 07 / sin 을 참고 할 수 있 습 니 다.
    흔히 볼 수 있 는 방법 중 하 나 는 double check 입 니 다.
    func GetInstance() *singleton {
        if instance == nil {     // 's not fully atomic
            mu.Lock()
            defer mu.Unlock()
    
            if instance == nil {
                instance = &singleton{}
            }
        }
        return instance
    }

    하지만 골 랑 에 서 는 더 좋 은 방법 이 있 습 니 다. "Once" 를 사용 하 세 요.
    type singleton struct {
    }
    
    var instance *singleton
    var once sync.Once
    
    func GetInstance() *singleton {
        once.Do(func() {
            instance = &singleton{}
        })
        return instance
    }

    factory/ abstract factory / builder
    이 몇 가지 creational patterns 에 대한 차이 점:
  • Builder focuses on constructing a complex object step by step. Abstract Factory emphasizes a family of product objects (either simple or complex). Builder returns the product as a final step, but as far as the Abstract Factory is concerned, the product gets returned immediately.
  • Builder often builds a Composite.
  • Often, designs start out using Factory Method (less complicated, more customizable, subclasses proliferate) and evolve toward Abstract Factory, Prototype, or Builder (more flexible, more complex) as the designer discovers where more flexibility is needed.
  • Sometimes creational patterns are complementary: Builder can use one of the other patterns to implement which components get built. Abstract Factory, Builder, and Prototype can use Singleton in their implementations.

  • factory
    // k8s.io/kubernetes/vendor/k8s.io/apimachinery/pkg/runtime/serializer/codec_factory.go
    func NewCodecFactory(scheme *runtime.Scheme) CodecFactory {
        serializers := newSerializersForScheme(scheme, json.DefaultMetaFactory)
        return newCodecFactory(scheme, serializers)
    }
    
    func (f CodecFactory) LegacyCodec(version ...schema.GroupVersion) runtime.Codec {
        return versioning.NewDefaultingCodecForScheme(f.scheme, f.legacySerializer, f.universal, schema.GroupVersions(version), runtime.InternalGroupVersioner)
    }

    abstract factory 는 Shared InformerFactory 를 예 로 들 면 이 factory 는 app / core / batch... 등 각종 인 터 페 이 스 를 만 들 수 있 습 니 다.
    //k8s.io/kubernetes/pkg/client/informers/informers_generated/internalversion/factory.go
    func NewSharedInformerFactory(client internalclientset.Interface, defaultResync time.Duration) SharedInformerFactory {
        return &sharedInformerFactory{
            client:           client,
            defaultResync:    defaultResync,
            informers:        make(map[reflect.Type]cache.SharedIndexInformer),
            startedInformers: make(map[reflect.Type]bool),
        }
    }
    
    // SharedInformerFactory provides shared informers for resources in all known
    // API group versions.
    type SharedInformerFactory interface {
        internalinterfaces.SharedInformerFactory
        ForResource(resource schema.GroupVersionResource) (GenericInformer, error)
        WaitForCacheSync(stopCh return apps.New(f)
    }

    builder
    // k8s.io/kubernetes/pkg/controller/client_builder.go
    
    func NewForConfigOrDie(c *rest.Config) *Clientset {
        var cs Clientset
        cs.admissionregistrationV1alpha1 = admissionregistrationv1alpha1.NewForConfigOrDie(c)
        cs.appsV1beta1 = appsv1beta1.NewForConfigOrDie(c)
        cs.appsV1beta2 = appsv1beta2.NewForConfigOrDie(c)
        cs.appsV1 = appsv1.NewForConfigOrDie(c)
        cs.authenticationV1 = authenticationv1.NewForConfigOrDie(c)
        cs.authenticationV1beta1 = authenticationv1beta1.NewForConfigOrDie(c)
        cs.authorizationV1 = authorizationv1.NewForConfigOrDie(c)
        cs.authorizationV1beta1 = authorizationv1beta1.NewForConfigOrDie(c)
        cs.autoscalingV1 = autoscalingv1.NewForConfigOrDie(c)
        cs.autoscalingV2beta1 = autoscalingv2beta1.NewForConfigOrDie(c)
        cs.batchV1 = batchv1.NewForConfigOrDie(c)
        cs.batchV1beta1 = batchv1beta1.NewForConfigOrDie(c)
        cs.batchV2alpha1 = batchv2alpha1.NewForConfigOrDie(c)
        cs.certificatesV1beta1 = certificatesv1beta1.NewForConfigOrDie(c)
        cs.coreV1 = corev1.NewForConfigOrDie(c)
        cs.extensionsV1beta1 = extensionsv1beta1.NewForConfigOrDie(c)
        cs.networkingV1 = networkingv1.NewForConfigOrDie(c)
        cs.policyV1beta1 = policyv1beta1.NewForConfigOrDie(c)
        cs.rbacV1 = rbacv1.NewForConfigOrDie(c)
        cs.rbacV1beta1 = rbacv1beta1.NewForConfigOrDie(c)
        cs.rbacV1alpha1 = rbacv1alpha1.NewForConfigOrDie(c)
        cs.schedulingV1alpha1 = schedulingv1alpha1.NewForConfigOrDie(c)
        cs.settingsV1alpha1 = settingsv1alpha1.NewForConfigOrDie(c)
        cs.storageV1beta1 = storagev1beta1.NewForConfigOrDie(c)
        cs.storageV1 = storagev1.NewForConfigOrDie(c)
    
        cs.DiscoveryClient = discovery.NewDiscoveryClientForConfigOrDie(c)
        return &cs
    }

    prototype
    프로 토 타 입 모드 는 생 성 모드 의 하나 로 '복사' 를 통 해 이미 존재 하 는 인 스 턴 스 를 통 해 새로운 인 스 턴 스 를 되 돌려 주 는 것 이 특징 입 니 다.복 제 된 인 스 턴 스 는 바로 우리 가 말 하 는 '원형' 이 고 이 원형 은 맞 춤 형 이다.The Prototype Pattern creates duplicate objects while keeping performance in mind. It's a part of the creational patterns and provides one of the best ways to create an object.
    blog. ralch. com / tutorial / de 참조...
    kubernetes 는 deepcopy - gen 자동 생 성 대상 의 deepcopy 등 방법 을 사 용 했 습 니 다. 예 를 들 어 아래 생 성 된 예 입 니 다.
    // k8s.io/kubernetes/vendor/k8s.io/apiserver/pkg/apis/audit/zz_generated.deepcopy.go
    // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
    func (in *GroupResources) DeepCopyInto(out *GroupResources) {
        *out = *in
        if in.Resources != nil {
            in, out := &in.Resources, &out.Resources
            *out = make([]string, len(*in))
            copy(*out, *in)
        }
        if in.ResourceNames != nil {
            in, out := &in.ResourceNames, &out.ResourceNames
            *out = make([]string, len(*in))
            copy(*out, *in)
        }
        return
    }
    
    // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GroupResources.
    func (in *GroupResources) DeepCopy() *GroupResources {
        if in == nil {
            return nil
        }
        out := new(GroupResources)
        in.DeepCopyInto(out)
        return out
    }

    생 성 된 도 구 는 여기 k8s. io / kubernetes / vendor / k8s. io / code - generator / cmd / deepcopy - gen / main. go 에서 github. com / kubernetes /...
    observer
    이 모델 은 kubernetes 에서 도 흔히 볼 수 있다. 예 를 들 어 shared Informer 는 관찰자 모델 의 실현 이다.
    // k8s.io/client-go/tools/cache/shared_informer.go
    func (s *sharedIndexInformer) AddEventHandlerWithResyncPeriod(handler ResourceEventHandler, resyncPeriod time.Duration) {
        ...
        s.processor.addListener(listener)
        ...
    }
    
    
    //   
    func (p *sharedProcessor) distribute(obj interface{}, sync bool) {
        p.listenersLock.RLock()
        defer p.listenersLock.RUnlock()
    
        if sync {
            for _, listener := range p.syncingListeners {
                listener.add(obj)
            }
        } else {
            for _, listener := range p.listeners {
                listener.add(obj)
            }
        }
    }

    command
    정의: 명령 모드 (Command Pattern): 하나의 요청 을 대상 으로 밀봉 하여 서로 다른 요청 으로 고객 을 매개 변수 화 할 수 있 도록 합 니 다.요청 대기 열 이나 요청 로 그 를 기록 하고 취소 가능 한 동작 을 지원 합 니 다.명령 모드 는 동작 (Action) 모드 나 트 랜 잭 션 (Transaction) 모드 라 는 별명 을 가 진 대상 행동 형 모드 입 니 다.kubernetes 의 command 는 github. com / spf 13 / cobra 를 바탕 으로 명령 을 대상 으로 만 들 었 습 니 다. 예 를 들 어 cmdRollOut 은 undo 도 실현 하 였 습 니 다.
    interator
    데이터 구조 와 관련 된 package 에 많이 사 용 됩 니 다. 예 를 들 어 인 용 된 라 이브 러 리 jsoniter, btree, govaidator.
    // golang    "go/token"    Iterate  ,        vistor  ...
    func (s *FileSet) Iterate(f func(*File) bool)

    strategy
    일련의 알고리즘 을 정의 하여 이 알고리즘 들 이 실 행 될 때 교환 할 수 있 도록 분리 알고리즘 을 개폐 원칙 에 부합 하도록 합 니 다.대상 은 어떤 행위 가 있 지만 서로 다른 장면 에서 이 행 위 는 서로 다른 실현 알고리즘 이 있다.실제로 interface 를 사용 하 는 것 은 모두 strategy 모델 과 같다. 이런 측면 에서 볼 때 strategy 모델 은 말 그대로 의 강조 의미 일 뿐 실현 에 있어 interface 가 실현 하 는 factory 모델 은 강조 하 는 점 만 다 를 뿐이다. 하 나 는 창설 형 을 강조 하 는 것 이 고 하 나 는 행위 형 예 를 강조 하 는 것 이다.
    // k8s          strategy,   update,delete,get    
    //    /k8s.io/kubernetes/pkg/registry/core/configmap/strategy.go
    
    // strategy implements behavior for ConfigMap objects
    type strategy struct {
        runtime.ObjectTyper
        names.NameGenerator
    }
    
    // Strategy is the default logic that applies when creating and updating ConfigMap
    // objects via the REST API.
    var Strategy = strategy{api.Scheme, names.SimpleNameGenerator}
    
    // Strategy should implement rest.RESTCreateStrategy
    var _ rest.RESTCreateStrategy = Strategy
    
    // Strategy should implement rest.RESTUpdateStrategy
    var _ rest.RESTUpdateStrategy = Strategy
    
    func (strategy) NamespaceScoped() bool {
        return true
    }
    
    func (strategy) PrepareForCreate(ctx genericapirequest.Context, obj runtime.Object) {
        _ = obj.(*api.ConfigMap)
    }
    
    func (strategy) Validate(ctx genericapirequest.Context, obj runtime.Object) field.ErrorList {
        cfg := obj.(*api.ConfigMap)
    
        return validation.ValidateConfigMap(cfg)
    }
    
    // Canonicalize normalizes the object after validation.
    func (strategy) Canonicalize(obj runtime.Object) {
    }
    
    func (strategy) AllowCreateOnUpdate() bool {
        return false
    }
    
    func (strategy) PrepareForUpdate(ctx genericapirequest.Context, newObj, oldObj runtime.Object) {
        _ = oldObj.(*api.ConfigMap)
        _ = newObj.(*api.ConfigMap)
    }
    
    func (strategy) AllowUnconditionalUpdate() bool {
        return true
    }
    
    func (strategy) ValidateUpdate(ctx genericapirequest.Context, newObj, oldObj runtime.Object) field.ErrorList {
        oldCfg, newCfg := oldObj.(*api.ConfigMap), newObj.(*api.ConfigMap)
    
        return validation.ValidateConfigMapUpdate(newCfg, oldCfg)
    }
    
    
    
    // k8s.io/kubernetes/pkg/registry/core/configmap/storage/storage.go
    // NewREST returns a RESTStorage object that will work with ConfigMap objects.
    func NewREST(optsGetter generic.RESTOptionsGetter) *REST {
        store := &genericregistry.Store{
            NewFunc:                  func() runtime.Object { return &api.ConfigMap{} },
            NewListFunc:              func() runtime.Object { return &api.ConfigMapList{} },
            DefaultQualifiedResource: api.Resource("configmaps"),
    
            CreateStrategy: configmap.Strategy,
            UpdateStrategy: configmap.Strategy,
            DeleteStrategy: configmap.Strategy,
        }
        options := &generic.StoreOptions{RESTOptions: optsGetter}
        if err := store.CompleteWithOptions(options); err != nil {
            panic(err) // TODO: Propagate error up
        }
        return &REST{store}
    }

    state
    상태 모드 (State Pattern): 한 대상 이 내부 상태 가 바 뀔 때 행동 을 바 꿀 수 있 도록 합 니 다. 대상 이 종 류 를 수정 한 것 같 습 니 다.그 별명 은 상태 대상 (Objects for States) 이 고 상태 모델 은 대상 행위 형 모델 이다.상태 와 행 위 를 분리 하 는 것, 예 를 들 어 하나의 상태 기기 의 실현 은 바로 표준 state 모델 이다.
    // k8s.io/kubernetes/vendor/github.com/coreos/etcd/raft/node.go
    // Node represents a node in a raft cluster.
    type Node interface {
        ....
        Step(ctx context.Context, msg pb.Message) error
    
        ....
    
        // Status returns the current status of the raft state machine.
        Status() Status
        ....
    }

    memento
    패 키 징 성 을 파괴 하지 않 는 전제 에서 대상 의 내부 상 태 를 포착 하고 이 대상 외 에 이 상 태 를 저장 합 니 다.이렇게 하면 이 대상 을 원래 저 장 된 상태 로 복원 할 수 있다.
    // k8s.io/kubernetes/pkg/registry/core/service/portallocator/allocator.go
    // NewFromSnapshot allocates a PortAllocator and initializes it from a snapshot.
    func NewFromSnapshot(snap *api.RangeAllocation) (*PortAllocator, error) {
        pr, err := net.ParsePortRange(snap.Range)
        if err != nil {
            return nil, err
        }
        r := NewPortAllocator(*pr)
        if err := r.Restore(*pr, snap.Data); err != nil {
            return nil, err
        }
        return r, nil
    }
    
    func (r *PortAllocator) Snapshot(dst *api.RangeAllocation) error {
        snapshottable, ok := r.alloc.(allocator.Snapshottable)
        if !ok {
            return fmt.Errorf("not a snapshottable allocator")
        }
        rangeString, data := snapshottable.Snapshot()
        dst.Range = rangeString
        dst.Data = data
        return nil
    }

    넓 은 의미 에서 볼 때 deployment, statefulset 등 대상 은 rollback 방법 을 실 현 했 고 memeto 와 유사 하 다. 예 를 들 어 deployment 는 각종 version 의 replicasset 의 history 백업 을 가지 고 역사 버 전 을 복원 하 는 데 사용 된다.k8s. io / kubernetes / pkg / controller / deployment /
    flyweight / object pool
    flyweight 는 대상 재 활용 을 강조 하 며 object pool 의 목적 과 같다.One difference in that flyweights are comonly immutable instances, while resources acquired from the pool usually are mutable. object pool 은 golang 과 kubernetes 에서 많이 사용 합 니 다. 예 를 들 어 공식 적 으로 sync. pool 이 있 습 니 다.
    // k8s.io/kubernetes/staging/src/k8s.io/apiserver/pkg/storage/cacher.go
    
    var timerPool sync.Pool
    
    func (c *cacheWatcher) add(event *watchCacheEvent, budget *timeBudget) {
        ...
    
        t, ok := timerPool.Get().(*time.Timer)
        if ok {
            t.Reset(timeout)
        } else {
            t = time.NewTimer(timeout)
        }
        defer timerPool.Put(t)
    
        ....
    }

    iterpreter
    해석 기 모드 는 언어 문법 을 정의 하고 이 언어 해석 기 를 설계 하여 사용자 가 특정한 문법 으로 해석 기 행 위 를 제어 할 수 있 도록 한다.이것 은 kubernetes 의 각종 코드 / 문서 생 성 도구 에 많이 사 용 됩 니 다.
    chain_of_responsibility
    일종 의 조합 모드 입 니 다. 사용 하 는 곳 이 많 습 니 다.직책 체인 모델 은 서로 다른 직책 을 분리 하고 동태 적 으로 관련 직책 을 조합 하 는 데 사용 된다.체인 대상 은 현재 직책 대상 과 다음 직책 체인 을 포함한다.
    //   wrapper        childrens
    // k8s.io/test-infra/velodrome/transform/plugins/multiplexer_wrapper.go
    func NewMultiplexerPluginWrapper(plugins ...Plugin) *MultiplexerPluginWrapper {
        return &MultiplexerPluginWrapper{
            plugins: plugins,
        }
    }
    
    //   warpper            clildrens
    // k8s.io/test-infra/velodrome/transform/plugins/author_logger_wrapper.go
    func NewAuthorLoggerPluginWrapper(plugin Plugin) *AuthorLoggerPluginWrapper {
        return &AuthorLoggerPluginWrapper{
            plugin: plugin,
        }
    }
    
    //      ,  clildrens
    ...

    visit
    대상 이 방문 자 인터페이스 (예 를 들 어 Accept) 를 미리 남 겨 두 면 후기 에 대상 에 기능 을 추가 할 때 대상 을 바 꿀 필요 가 없다.본질은 외부 에서 자신의 대상 을 방문 하 는 방법 을 정의 할 수 있 도록 하 는 것 이다. 이런 측면 에서 볼 때 visit 가 하나의 함수 로 전달 되면 visit 모델 이다.즉시
    func(a *A)Visit(Vistor func(*A) error){
        Vistor(a)
    }

    예시
    // k8s.io/kubernetes/pkg/kubectl/cmd/util/openapi/openapi.go
    type SchemaVisitor interface {
        VisitArray(*Array)
        VisitMap(*Map)
        VisitPrimitive(*Primitive)
        VisitKind(*Kind)
        VisitReference(Reference)
    }
    
    // Schema is the base definition of an openapi type.
    type Schema interface {
        // Giving a visitor here will let you visit the actual type.
        Accept(SchemaVisitor)
    
        // Pretty print the name of the type.
        GetName() string
        // Describes how to access this field.
        GetPath() *Path
        // Describes the field.
        GetDescription() string
        // Returns type extensions.
        GetExtensions() map[string]interface{}
    }
    // /k8s.io/kubernetes/pkg/kubectl/resource/builder.go
    func (b *Builder) visitByName() *Result {
        ...
    
        visitors := []Visitor{}
        for _, name := range b.names {
            info := NewInfo(client, mapping, selectorNamespace, name, b.export)
            visitors = append(visitors, info)
        }
        result.visitor = VisitorList(visitors)
        result.sources = visitors
        return result
    }

    engineering patterns
    code generator
    다음으로 전송:https://juejin.im/post/5a113e686fb9a0452936596c

    좋은 웹페이지 즐겨찾기