Golang系列(四)之面向接口编程
1. 接口[多态]
多态性(polymorphisn)是允许你将父对象设置成为和一个或更多的他的子对象相等的技术,赋值之后,父对象就可以根据当前赋值给它的子对象的特性以不同的方式运作。
简而言之,就是允许将子类类型的指针赋值给父类类型的指针。
即一个引用变量倒底会指向哪个类的实例对象,该引用变量发出的方法调用到底是哪个类中实现的方法,必须在由程序运行期间才能决定。不修改程序代码就可以改变程序运行时所绑定的具体代码,让程序可以选择多个运行状态,这就是多态性。多态分为编译时多态(静态多态)和运行时多态(动态多态),编译时多态一般通过方法重载实现,运行时多态一般通过方法重写实现。
1.1 接口概念
接口类型可以看作是类型系统中一种特殊的类型,而实例就是实现了该接口的具体结构体类型。
接口类型与实现了该接口的结构体对象之间的关系好比变量类型与变量之间的关系。
接口即一组方法定义的集合,定义了对象的一组行为,由具体的类型实例实现具体的方法。换句话说,一个接口就是定义(规范或约束),而方法就是实现,接口的作用应该是将定义与实现分离,降低耦合度。习惯用“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.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()
2)将接口赋值给另一个接口
- 只要两个接口拥有相同的方法列表(与次序无关),即是两个相同的接口,可以相互赋值
- 接口赋值只需要接口A的方法列表是接口B的子集(即假设接口A中定义的所有方法,都在接口B中有定义),那么B接口的实例可以赋值给A的对象。反之不成立,即子接口B包含了父接口A,因此可以将子接口的实例赋值给父接口。
- 即子接口实例实现了子接口的所有方法,而父接口的方法列表是子接口的子集,则子接口实例自然实现了父接口的所有方法,因此可以将子接口实例赋值给父接口。
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.4 接口查询
若要在 switch 外判断一个接口类型是否实现了某个接口,可以使用“逗号 ok ”。
value, ok := Interfacevariable.(implementType)
其中 Interfacevariable 是接口变量(接口值),implementType 为实现此接口的类型,value 返回接口变量实际类型变量的值,如果该类型实现了此接口返回 true。
//判断file1接口指向的对象实例是否是File类型
var file1 Writer=...
if file5,ok:=file1.(File);ok{ ...
}
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.6 接口组合
//接口组合类似类型组合,只不过只包含方法,不包含成员变量
type ReadWriter interface{ //接口组合,避免代码重复Reader //接口ReaderWriter //接口Writer
}
1.7 Any类型[空接口]
每种类型都能匹配到空接口:interface{}。空接口类型对方法没有任何约束(因为没有方法),它能包含任意类型,也可以实现到其他接口类型的转换。如果传递给该接口的类型变量实现了转换后的接口则可以正常运行,否则出现运行时错误。
//interface{}即为可以指向任何对象的Any类型,类似Java中的Object类
var v1 interface{}=struct{X int}{1}
var v2 interface{}="abc"func DoSomething(v interface{}) { //该函数可以接收任何类型的参数,因为任何类型都实现了空接口// ...
}
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()) //打印不同实现该接口的类的方法返回值}
}
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
}
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) errorDeleteCollection(options *meta_v1.DeleteOptions, listOptions meta_v1.ListOptions) errorGet(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
}
PodInterface
接口定义了pod对象所使用的方法,一般为增删改查等。其他kubernetes资源对象的接口定义类似,区别在于入参和出参与对象相关。例如Create(*v1.Pod) (*v1.Pod, error)
方法定义的入参出参为*v1.Pod
。如果要实现该接口,即实现该接口的所有方法。
2.2 接口的实现
2.2.1 结构体的定义
// pods implements PodInterface
type pods struct {client rest.Interfacens string
}
2.2.2 new函数[构造函数]
// newPods returns a Pods
func newPods(c *CoreV1Client, namespace string) *pods {return &pods{client: c.RESTClient(),ns: namespace,}
}
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
}
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
}
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
}
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
}
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()
}
2.3 接口的调用
示例:
// 创建clientset实例
clientset, err := kubernetes.NewForConfig(config)
// 具体的调用
pods, err := clientset.CoreV1().Pods("").List(metav1.ListOptions{})
clientset实现了接口Interface
,Interface
是个接口组合,包含各个client的接口类型。例如CoreV1()
方法对应的接口类型是CoreV1Interface
。
以下是clientset的CoreV1()
方法实现:
// CoreV1 retrieves the CoreV1Client
func (c *Clientset) CoreV1() corev1.CoreV1Interface {return c.coreV1
}
该方法可以理解为是一个构造函数
。构造函数的返回值类型是一个接口类型CoreV1Interface
,而return的返回值是实现了该接口类型的结构体对象c.coreV1
。
接口类型是一种特殊的类型,接口类型与结构体对象之间的关系好比变量类型与变量之间的关系。其中的结构体对象必须实现了该接口类型的所有方法。
所以clientset的CoreV1()
方法实现是返回一个CoreV1Client
结构体对象。该结构体对象实现了CoreV1Interface
接口,该接口也是一个接口组合。
type CoreV1Interface interface {RESTClient() rest.InterfaceComponentStatusesGetterConfigMapsGetterEndpointsGetterEventsGetterLimitRangesGetterNamespacesGetterNodesGetterPersistentVolumesGetterPersistentVolumeClaimsGetterPodsGetterPodTemplatesGetterReplicationControllersGetterResourceQuotasGetterSecretsGetterServicesGetterServiceAccountsGetter
}
而实现的Pods()
方法是其中的PodsGetter
接口。
Pods()
同CoreV1()
一样是个构造函数,构造函数的返回值类型是PodInterface
接口,返回值是实现了PodInterface
接口的pods
结构体对象。
func (c *CoreV1Client) Pods(namespace string) PodInterface {return newPods(c, namespace)
}
而PodInterface
接口定义参考接口定义,pods
对象实现了PodInterface
接口的方法,具体参考接口的实现。
最终调用了pods
对象的List()
方法。
pods, err := clientset.CoreV1().Pods("").List(metav1.ListOptions{})
即以上代码就是不断调用实现了某接口的结构体对象的构造函数,生成具体的结构体对象,再调用结构体对象的某个具体方法。
3. 通用接口设计
3.1 接口定义
// 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) errorDeleteCollection(options *meta_v1.DeleteOptions, listOptions meta_v1.ListOptions) errorGet(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
}
3.2 结构体定义
// pods implements PodInterface
type pods struct {client rest.Interfacens string
}
3.3 构造函数
// newPods returns a Pods
func newPods(c *CoreV1Client, namespace string) *pods {return &pods{client: c.RESTClient(),ns: namespace,}
}
3.4 结构体实现
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
}
3.5 接口调用
pods, err := clientset.CoreV1().Pods("").List(metav1.ListOptions{})
3.6 其他接口设计示例
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 stringNamespace stringkubeCli *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) {//具体的方法实现
}
Golang系列(四)之面向接口编程相关推荐
- C语言面向对象编程(四):面向接口编程
Java 中有 interface 关键字,C++ 中有抽象类或纯虚类可以与 interface 比拟,C 语言中也可以实现类似的特性. 在面试 Java 程序员时我经常问的一个问题是:接口和抽象类有 ...
- golang与面向接口编程
这节不阐述OOP的理念,从接口直接讲,需要一定的抽象思想,新手可以绕道 go里面的接口定义 Go语言不同于其他语言.在Go语言中,接口是自定义类型,用于指定一组一个或多个方法签名,并且该接口是抽象的, ...
- 【转】工厂模式面向接口编程
为了实现更好的灵活性 应改面向接口编程.因此,应该面向接口提供工场. 比如,Cat, Dog, Mouse,都是4条腿会跑的动物. 因此,我们建立一个接口叫做F ...
- 为什么有人说面向对象编程就是面向接口编程?
"面向对象编程就是面向接口编程" 这句话相信, 很多人都在网上见过, 装b利器. 我一开始也是这么想的, 那些装b者丢下这一句, 就没下文了. 首先, 我认为这句话是1个假命题. ...
- 软件设计模式—面向接口编程
原文作者:laoer2009 原文地址:设计模式之面向接口编程 01第一次需求 玩家有很多属性,例如:身高,性别 blalalala ,玩家可以攻击其他玩家.产品狗YY妹子写程序也是很利索,一天就把程 ...
- java 接口,接口的特性,接口实现多态,面向接口编程
package cn.zy.cellphone; /**接口是一种引用数据类型.使用interface声明接口,形式* 形式:public interface 接口名称{}* 接口不能拥有构造方法,不 ...
- 黑马java教程是什么_Java教程:揭秘什么是面向接口编程
先用一个案例来给大家说明一下面向接口编程. 案例:有一个电脑类(Computer),电脑除了有基本的开机关机功能外,还有连接任何外接设备的功能,比如能电脑能连接外置键盘(Keyboard),鼠标(Mo ...
- Java面向接口编程,低耦合高内聚的设计哲学
接口体现的是一种规范和实现分离的设计哲学,充分利用接口可以极大的降低程序中各个模块之间的耦合,提高系统的可维护性以及可扩展性. 因此,很多的软件架构设计理念都倡导"面向接口编程"而 ...
- java的知识点13——多态、对象的转型(casting)、final关键字、抽象方法和抽象类、接口的作用、如何定义和使用接口?、接口的多继承、面向接口编程
多态 多态指的是同一个方法调用,由于对象不同可能会有不同的行为.现实生活中,同一个方法,具体实现会完全不同. 多态的要点: 1. 多态是方法的多态,不是属性的多态(多态与属性无关). 2. 多态的存在 ...
- Java 面向抽象编程和面向接口编程
以下内容来自<Java 2实用教程>,主编:耿祥义.张跃平 鉴于面向抽象编程和面向接口编程思维培养的重要性,写此博客巩固. 面向抽象编程: 在设计程序时,经常会使用到abstract类,其 ...
最新文章
- App设计灵感之十二组精美的AR现实导航App设计案例
- NoSQLUnit 0.3.0发布
- 硬件工程师的基本技能
- SCOM 2012知识分享-17:创建并管理组
- 每天一算法(一)——用链表实现加减乘运算
- CB推荐的AP统计学课程在线学习资源网站汇总
- 计算机启动过程中按DEL,电脑开机按del之后如何u盘装系统教程
- Android SDK是什么意思?
- coredns异常问题
- Java虚拟机:垃圾收集
- Linux开发环境部署
- 十进制、八进制和十六进制,各种进制存在的意义,为什么要设计出来?
- MATLAB绘制小胖墩
- MATLAB 各类二维渐变图
- 从 HTTP 瞎逼逼到 HTTP/2
- pocketsphinx 模型库_pocketSphinx 嵌入式关键词唤醒
- 升级JDK8的坎坷之路
- LaTex 之 数学运算符号
- Golang字符串拼接的方法
- HTML 粗体与斜体
热门文章
- 设备管理器---usb大容量储存设备感叹号,USB大容量存储设备无法启动
- HTML5正确嵌入flash,网页上嵌入Flash播放器(1)
- 百科知识:呼叫转移与呼叫前转
- spss基本总结——聚类分析
- 编程分钟转化小时怎么编_CNC数控加工中心编程好学吗?多年数控编程工程师指点迷津...
- ftl模板导出excel_ftl方式导出excel
- Redhat8 配置使用阿里源(关闭官方订阅)
- 开发流程与管理--《人月神话》精简
- 【AE】缺少Color属性或方法解决办法
- 2022-03-24 windows pc和Android 手机同屏软件vysor,download网址: https://www.vysor.io/#