Golang interface-oriented programming

1. Interface [Polymorphism]

​Polymorphism is a technique that allows you to set a parent object equal to one or more of its child objects. Once assigned, the parent object can behave differently depending on the properties of the child objects currently assigned to it operate.

In short, it allows assigning pointers of subclass types to pointers of superclass types.

That is to say, which class instance object a reference variable will point to, and which class implements the method call issued by the reference variable must be determined during the running of the program. Without modifying the program code, you can change the specific code bound when the program runs, so that the program can choose multiple running states, which is polymorphism. Polymorphism is divided into compile-time polymorphism (static polymorphism) and run-time polymorphism (dynamic polymorphism). Compile-time polymorphism is generally implemented through method overloading, and run-time polymorphism is generally implemented through method rewriting.

1.1 Interface Concept

An interface type can be regarded as a special type in the type system, and an instance is a specific structure type that implements the interface.

The relationship between an interface type and a struct object that implements the interface is like the relationship between variable types and variables.

​ 接口即一组方法定义的集合,定义了对象的一组行为,由具体的类型实例实现具体的方法。换句话说,一个接口就是定义(规范或约束),而方法就是实现,接口的作用应该是将定义与实现分离,降低耦合度。习惯用“er”结尾来命名,例如“Reader”。接口与对象的关系是多对多,即一个对象可以实现多个接口,一个接口也可以被多个对象实现。

​ 接口是Go语言整个类型系统的基石,其他语言的接口是不同组件之间的契约的存在,对契约的实现是强制性的,必须显式声明实现了该接口,这类接口称之为“侵入式接口”。而Go语言的接口是隐式存在,只要实现了该接口的所有函数则代表已经实现了该接口,并不需要显式的接口声明

  • 接口的比喻

​ 你的电脑上只有一个USB接口。这个USB接口可以接MP3,数码相机,摄像头,鼠标,键盘等。。。所有的上述硬件都可以公用这个接口,有很好的扩展性,该USB接口定义了一种规范,只要实现了该规范,就可以将不同的设备接入电脑,而设备的改变并不会对电脑本身有什么影响(低耦合)

  • 面向接口编程

​ 接口表示调用者和设计者的一种约定,在多人合作开发同一个项目时,事先定义好相互调用的接口可以大大提高开发的效率。接口是用类来实现的,实现接口的类必须严格按照接口的声明来实现接口提供的所有功能。有了接口,就可以在不影响现有接口声明的情况下,修改接口的内部实现,从而使兼容性问题最小化。

​ 面向接口编程可以分为三方面:制定者(或者叫协调者),实现者(或者叫生产者),调用者(或者叫消费者)

​ 当其他设计者调用了接口后,就不能再随意更改接口的定义,否则项目开发者事先的约定就失去了意义。但是可以在类中修改相应的代码,完成需要改动的内容。

1.2 非侵入式接口

非侵入式接口:一个类只需要实现了接口要求的所有函数就表示实现了该接口,并不需要显式声明

type File struct{
  //类的属性
}
//File类的方法
func (f *File) Read(buf []byte) (n int,err error)
func (f *File) Write(buf []byte) (n int,err error)
func (f *File) Seek(off int64,whence int) (pos int64,err error)
func (f *File) Close() error
//接口1:IFile
type IFile interface{
  Read(buf []byte) (n int,err error)
  Write(buf []byte) (n int,err error)
  Seek(off int64,whence int) (pos int64,err error)
  Close() error
}
//接口2:IReader
type IReader interface{
  Read(buf []byte) (n int,err error)
}
//接口赋值,File类实现了IFile和IReader接口,即接口所包含的所有方法
var file1 IFile = new(File)
var file2 IReader = new(File)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

1.3 接口赋值

只要类实现了该接口的所有方法,即可将该类赋值给这个接口,接口主要用于多态化方法。即对接口定义的方法,不同的实现方式。

接口赋值: 
1)将对象实例赋值给接口

type IUSB interface{
    //定义IUSB的接口方法
}
//方法定义在类外,绑定该类,以下为方便,备注写在类中
type MP3 struct{
    //实现IUSB的接口,具体实现方式是MP3的方法
}
type Mouse struct{
    //实现IUSB的接口,具体实现方式是Mouse的方法
}
//接口赋值给具体的对象实例MP3
var usb IUSB =new(MP3)
usb.Connect()
usb.Close()
//接口赋值给具体的对象实例Mouse
var usb IUSB =new(Mouse)
usb.Connect()
usb.Close()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

2)将接口赋值给另一个接口

  1. 只要两个接口拥有相同的方法列表(与次序无关),即是两个相同的接口,可以相互赋值
  2. 接口赋值只需要接口A的方法列表是接口B的子集(即假设接口A中定义的所有方法,都在接口B中有定义),那么B接口的实例可以赋值给A的对象。反之不成立,即子接口B包含了父接口A,因此可以将子接口的实例赋值给父接口。
  3. 即子接口实例实现了子接口的所有方法,而父接口的方法列表是子接口的子集,则子接口实例自然实现了父接口的所有方法,因此可以将子接口实例赋值给父接口。
type Writer interface{    //父接口
    Write(buf []byte) (n int,err error)
}
type ReadWriter interface{    //子接口
    Read(buf []byte) (n int,err error)
    Write(buf []byte) (n int,err error)
}
var file1 ReadWriter=new(File)   //子接口实例
var file2 Writer=file1           //子接口实例赋值给父接口
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

1.4 接口查询

若要在 switch 外判断一个接口类型是否实现了某个接口,可以使用“逗号 ok ”。

value, ok := Interfacevariable.(implementType)

其中 Interfacevariable 是接口变量(接口值),implementType 为实现此接口的类型,value 返回接口变量实际类型变量的值,如果该类型实现了此接口返回 true。

//判断file1接口指向的对象实例是否是File类型
var file1 Writer=...
if file5,ok:=file1.(File);ok{  
  ...
}
  • 1
  • 2
  • 3
  • 4
  • 5

1.5 接口类型查询

在 Go 中,要判断传递给接口值的变量类型,可以在使用 type switch 得到。(type)只能在 switch 中使用。

// 另一个实现了 I 接口的 R 类型
type R struct { i int }
func (p *R) Get() int { return p.i }
func (p *R) Put(v int) { p.i = v }

func f(p I) {
    switch t := p.(type) { // 判断传递给 p 的实际类型
        case *S: // 指向 S 的指针类型
        case *R: // 指向 R 的指针类型
        case S:  // S 类型
        case R:  // R 类型
        default: //实现了 I 接口的其他类型
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

1.6 接口组合

//接口组合类似类型组合,只不过只包含方法,不包含成员变量
type ReadWriter interface{  //接口组合,避免代码重复
  Reader      //接口Reader
  Writer      //接口Writer
}
  • 1
  • 2
  • 3
  • 4
  • 5

1.7 Any类型[空接口]

每种类型都能匹配到空接口:interface{}。空接口类型对方法没有任何约束(因为没有方法),它能包含任意类型,也可以实现到其他接口类型的转换。如果传递给该接口的类型变量实现了转换后的接口则可以正常运行,否则出现运行时错误。

//interface{}即为可以指向任何对象的Any类型,类似Java中的Object类
var v1 interface{}=struct{X int}{1}
var v2 interface{}="abc"

func DoSomething(v interface{}) {   //该函数可以接收任何类型的参数,因为任何类型都实现了空接口
   // ...
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

1.8 接口的代码示例

//接口animal
type Animal interface {
    Speak() string
}
//Dog类实现animal接口
type Dog struct {
}

func (d Dog) Speak() string {
    return "Woof!"
}
//Cat类实现animal接口
type Cat struct {
}

func (c Cat) Speak() string {
    return "Meow!"
}
//Llama实现animal接口
type Llama struct {
}

func (l Llama) Speak() string {
    return "?????"
}
//JavaProgrammer实现animal接口
type JavaProgrammer struct {
}

func (j JavaProgrammer) Speak() string {
    return "Design patterns!"
}
//主函数
func main() {
    animals := []Animal{Dog{}, Cat{}, Llama{}, JavaProgrammer{}}  //利用接口实现多态
    for _, animal := range animals {
        fmt.Println(animal.Speak())  //打印不同实现该接口的类的方法返回值
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39

2. client-go中接口的使用分析

以下以k8s.io/client-go/kubernetes/typed/core/v1/pod.go的pod对象做分析。

2.1 接口设计与定义

2.1.1 接口组合

// PodsGetter has a method to return a PodInterface.
// A group's client should implement this interface.
type PodsGetter interface {
    Pods(namespace string) PodInterface
}
  • 1
  • 2
  • 3
  • 4
  • 5

2.1.2 接口定义

// PodInterface has methods to work with Pod resources.
type PodInterface interface {
    Create(*v1.Pod) (*v1.Pod, error)
    Update(*v1.Pod) (*v1.Pod, error)
    UpdateStatus(*v1.Pod) (*v1.Pod, error)
    Delete(name string, options *meta_v1.DeleteOptions) error
    DeleteCollection(options *meta_v1.DeleteOptions, listOptions meta_v1.ListOptions) error
    Get(name string, options meta_v1.GetOptions) (*v1.Pod, error)
    List(opts meta_v1.ListOptions) (*v1.PodList, error)
    Watch(opts meta_v1.ListOptions) (watch.Interface, error)
    Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1.Pod, err error)
    PodExpansion
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

PodInterface接口定义了pod对象所使用的方法,一般为增删改查等。其他kubernetes资源对象的接口定义类似,区别在于入参和出参与对象相关。例如Create(*v1.Pod) (*v1.Pod, error)方法定义的入参出参为*v1.Pod。如果要实现该接口,即实现该接口的所有方法。

2.2 接口的实现

2.2.1 结构体的定义

// pods implements PodInterface
type pods struct {
    client rest.Interface
    ns     string
}
  • 1
  • 2
  • 3
  • 4
  • 5

2.2.2 new函数[构造函数]

// newPods returns a Pods
func newPods(c *CoreV1Client, namespace string) *pods {
    return &pods{
        client: c.RESTClient(),
        ns:     namespace,
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

2.2.3 方法的实现

Get

// Get takes name of the pod, and returns the corresponding pod object, and an error if there is any.
func (c *pods) Get(name string, options meta_v1.GetOptions) (result *v1.Pod, err error) {
    result = &v1.Pod{}
    err = c.client.Get().
        Namespace(c.ns).
        Resource("pods").
        Name(name).
        VersionedParams(&options, scheme.ParameterCodec).
        Do().
        Into(result)
    return
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

List

// List takes label and field selectors, and returns the list of Pods that match those selectors.
func (c *pods) List(opts meta_v1.ListOptions) (result *v1.PodList, err error) {
    result = &v1.PodList{}
    err = c.client.Get().
        Namespace(c.ns).
        Resource("pods").
        VersionedParams(&opts, scheme.ParameterCodec).
        Do().
        Into(result)
    return
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

Create

// Create takes the representation of a pod and creates it.  Returns the server's representation of the pod, and an error, if there is any.
func (c *pods) Create(pod *v1.Pod) (result *v1.Pod, err error) {
    result = &v1.Pod{}
    err = c.client.Post().
        Namespace(c.ns).
        Resource("pods").
        Body(pod).
        Do().
        Into(result)
    return
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

Update

// Update takes the representation of a pod and updates it. Returns the server's representation of the pod, and an error, if there is any.
func (c *pods) Update(pod *v1.Pod) (result *v1.Pod, err error) {
    result = &v1.Pod{}
    err = c.client.Put().
        Namespace(c.ns).
        Resource("pods").
        Name(pod.Name).
        Body(pod).
        Do().
        Into(result)
    return
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

Delete

// Delete takes name of the pod and deletes it. Returns an error if one occurs.
func (c *pods) Delete(name string, options *meta_v1.DeleteOptions) error {
    return c.client.Delete().
        Namespace(c.ns).
        Resource("pods").
        Name(name).
        Body(options).
        Do().
        Error()
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

2.3 接口的调用

示例:

// 创建clientset实例
clientset, err := kubernetes.NewForConfig(config)
// 具体的调用
pods, err := clientset.CoreV1().Pods("").List(metav1.ListOptions{})
  • 1
  • 2
  • 3
  • 4

clientset实现了接口InterfaceInterface是个接口组合,包含各个client的接口类型。例如CoreV1()方法对应的接口类型是CoreV1Interface

以下是clientset的CoreV1()方法实现:

// CoreV1 retrieves the CoreV1Client
func (c *Clientset) CoreV1() corev1.CoreV1Interface {
    return c.coreV1
}
  • 1
  • 2
  • 3
  • 4

该方法可以理解为是一个构造函数。构造函数的返回值类型是一个接口类型CoreV1Interface,而return的返回值是实现了该接口类型的结构体对象c.coreV1

接口类型是一种特殊的类型,接口类型与结构体对象之间的关系好比变量类型与变量之间的关系。其中的结构体对象必须实现了该接口类型的所有方法。

So the method implementation of clientset CoreV1()is to return a CoreV1Clientstructure object. The struct object implements the CoreV1Interfaceinterface, which is also an interface composition.

type CoreV1Interface interface {
    RESTClient() rest.Interface
    ComponentStatusesGetter
    ConfigMapsGetter
    EndpointsGetter
    EventsGetter
    LimitRangesGetter
    NamespacesGetter
    NodesGetter
    PersistentVolumesGetter
    PersistentVolumeClaimsGetter
    PodsGetter
    PodTemplatesGetter
    ReplicationControllersGetter
    ResourceQuotasGetter
    SecretsGetter
    ServicesGetter
    ServiceAccountsGetter
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

And the implemented Pods()method is the PodsGetterinterface in it.

Pods()The same CoreV1()is a constructor, the return value type of the constructor is an interface, and the return value is a structure object PodInterfacethat implements the PodInterfaceinterface .pods

func (c *CoreV1Client) Pods(namespace string) PodInterface {
    return newPods(c, namespace)
}
  • 1
  • 2
  • 3

The PodInterfaceinterface definition refers to the interface definition , the podsobject implements the PodInterfacemethods of the interface, and the specific reference to the implementation of the interface .

Finally podsthe method of the object is called List().

pods, err := clientset.CoreV1().Pods("").List(metav1.ListOptions{})
  • 1

That is, the above code is to continuously call the constructor of a structure object that implements an interface, generate a specific structure object, and then call a specific method of the structure object.

3. General interface design

3.1 Interface Definition

// ProjectManager manage life cycle of Deployment and Resources
type PodInterface interface {
    Create(*v1.Pod) (*v1.Pod, error)
    Update(*v1.Pod) (*v1.Pod, error)
    UpdateStatus(*v1.Pod) (*v1.Pod, error)
    Delete(name string, options *meta_v1.DeleteOptions) error
    DeleteCollection(options *meta_v1.DeleteOptions, listOptions meta_v1.ListOptions) error
    Get(name string, options meta_v1.GetOptions) (*v1.Pod, error)
    List(opts meta_v1.ListOptions) (*v1.PodList, error)
    Watch(opts meta_v1.ListOptions) (watch.Interface, error)
    Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1.Pod, err error)
    PodExpansion
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

3.2 Structure Definition

// pods implements PodInterface
type pods struct {
    client rest.Interface
    ns     string
}
  • 1
  • 2
  • 3
  • 4
  • 5

3.3 Constructor

// newPods returns a Pods
func newPods(c *CoreV1Client, namespace string) *pods {
    return &pods{
        client: c.RESTClient(),
        ns:     namespace,
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

3.4 Structure Implementation

List()

// List takes label and field selectors, and returns the list of Pods that match those selectors.
func (c *pods) List(opts meta_v1.ListOptions) (result *v1.PodList, err error) {
    result = &v1.PodList{}
    err = c.client.Get().
        Namespace(c.ns).
        Resource("pods").
        VersionedParams(&opts, scheme.ParameterCodec).
        Do().
        Into(result)
    return
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

3.5 Interface call

pods, err := clientset.CoreV1().Pods("").List(metav1.ListOptions{})
  • 1

3.6 Other Interface Design Examples

type XxxManager interface {
    Create(args argsType) (*XxxStruct, error)
    Get(args argsType) (**XxxStruct, error)
    Update(args argsType) (*XxxStruct, error)
    Delete(name string, options *DeleleOptions) error
}

type XxxManagerImpl struct {
    Name string
    Namespace string
    kubeCli *kubernetes.Clientset
}

func NewXxxManagerImpl (namespace, name string, kubeCli *kubernetes.Clientset) XxxManager {
    return &XxxManagerImpl{
        Name name,
        Namespace namespace,
        kubeCli: kubeCli,
    }
}

func (xm *XxxManagerImpl) Create(args argsType) (*XxxStruct, error) {
    //具体的方法实现
}

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324686603&siteId=291194637