Go的面向接口——Golang学习笔记5

文章目录

      • duck typing的概念
        • 静态语言和动态语言
        • duck typing解释
        • 不同语言中实现duck typing
      • 接口的定义和实现
      • 接口的值类型
      • 接口的组合
      • 常用系统接口
      • 参考链接

  • Golang是面向接口的编程语言,相比其他面向对象的编程语言,其并没有继承和多态;
  • Golang仅仅支持封装;
  • Golang实现面向对象(继承和多态,封装)需要利用接口完成;

duck typing的概念

静态语言和动态语言

1. 静态语言(强类型语言)

  • 静态语言是在编译时变量的数据类型即可确定的语言,多数静态类型语言要求在使用变量之前必须声明数据类型。

    例如:C++、Java、Delphi、C#等。

2. 动态语言(弱类型语言)

  • 动态语言是在运行时确定数据类型的语言。变量使用之前不需要类型声明,通常变量的类型是是由值的类型决定的。

    例如PHP、ASP、Ruby、Python、Perl、ABAP、SQL、JavaScript、Unix Shell等等。

3. 强类型定义语言

  • 强制数据类型定义的语言。也就是说,一旦一个变量被指定了某个数据类型,如果不经过强制转换,那么它就永远是这个数据类型了。

  • 举个例子:如果你定义了一个整型变量a,那么程序根本不可能将a当作字符串类型处理。强类型定义语言是类型安全的语言。

4. 弱类型定义语言

  • 数据类型可以被忽略的语言。它与强类型定义语言相反, 一个变量可以赋不同数据类型的值。强类型定义语言在速度上可能略逊色于弱类型定义语言,但是强类型定义语言带来的严谨性能够有效的避免许多错误。

5. 两者区别

  • 特性

    强类型语言是一旦变量的类型被确定,就不能转化的语言。

    弱类型语言则反之,一个变量的类型是由其应用上下文确定的。

  • 静态语言的优势

    由于类型的强制声明,使得IDE有很强的代码感知能力,故,在实现复杂的业务逻辑、开发大型商业系统、以及那些生命周期
    很长的应用中,依托IDE对系统的开发很有保障;

    由于静态语言相对比较封闭,使得第三方开发包对代码的侵害性可以降到最低;

  • 动态语言的优势

    思维不受束缚,可以任意发挥,把更多的精力放在产品本身上;

    集中思考业务逻辑实现,思考过程即实现过程;

duck typing解释
  • duck typing(鸭子类型)字面解释是 如果一只动物走起来像鸭子、游泳起来像鸭子、叫起来也像鸭子,那么这只动物就可以被称为鸭子

  • 所谓的 Duck Typing,它只关心事物的外部行为而非内部结构

    就如只关系一个动物走路、游泳、叫声是否像gv,如果像则认为是鸭子。它并不关心你这只鸭子内部构造是长肉的还是充气的,仅仅通过外部行为。

  • 许多编程语言都支持 Duck Typing ,通常 Duck Typing 是动态编程语言用来实现多态的一种方式。

不同语言中实现duck typing

1. Python中的duck typing

  • 例如

    def download(retriever):
    	return retriever.get("http://xxx")
    
  • 有一个 download 函数,传过来一个 retriever参数,retriever是可以获取一个 url 链接的资源的。这个 fetcher 就是一个 Duck Typing 的对象,使用者约定好这个 retriever 会有一个 get 函数就可以了;

  • Python是动态语言,retriever随便什么类型都可以,只要实现一个 get 方法,就能通过编译;

  • 但是会出现以下情况:
    运行时才知道传入的 retriever 有没有 get 函数

    需要注释来说明接口。从 download 函数的使用者的角度上看,我怎么知道需要给 retriever 实现 get 方法呢?我不可能去阅读 download 函数的代码,实际情况中,可能 download 函数的代码很长,可能 retriever不只要实现 get 方法,还有其它方法需要实现。通常这种情况需要通过加注释来说明。

2. C++中的 duck typing

  • 例如

    template <class R>
    string download(const R& retriever) {
    	return retriever.get("http://xxx")
    }
    
  • C++ 是静态语言,但是它也能支持 Duck Typing,它是通过模板来支持的;

  • C++通过模板达到 retriever 随便什么类型都可以,只要实现一个 get 方法,就能通过编译的目的;

    • 但是会出现以下情况:
      编译时,才知道传入的 fetcher 有没有 get 这个方法。相比Python 运行时 才知道有没有get方法,好了一些。

      同样,还是需要注释来说明

3. Java中的类似代码

  • 代码
<R extends Retriever>
String download(R r) {
	return r.get("http://xxx")
}
  • 同样也用了模板类型。模板 F 必须 extends Retriever ,有了这个限定,就能逼着 download 函数的使用者对 r 实现 get 方法,它解决了需要注释来说明的缺点

  • 传入的参数必须实现 Retriever 接口,Retriever中存在get()方法,就没有运行时发现错误,编译时发现错误的问题;

  • 但是,它严格上来说不是 Duck Typing,因为传的参数必须实现 Retriever 接口,即便定义一个实现get方法的其他接口,他也不能作为参数传入 ;

  • 如果 download 函数只依赖 r 的 get 方法,而 Retrieve 接口必须要实现除 get 方法以外,还有其它方法,那么也要一一实现,非常不灵活。

4. Golang中的duck typing

  • 在 Java 的 Duck Typing 类似代码中,如果 r 参数需要同时实现两个或以上的接口方法时,Java 是没有办法做到的。但 Go 语言可以做到;

  • Go具有Python、C++的duck typing的灵活性,什么类型都能作为参数;

  • 有具有java的类型检查,不需要通过注释说明;

接口的定义和实现

  • Go中的接口由使用者定义;
  • 接口的实现是隐式的,不需要声明实现接口,只需要实现接口中的方法就表示实现了接口;
  • Go中没有implemet关键词,是利用结构体实现接口中所用的方法来表示,该结构体继承接口;
  • 在Go语言中,一个struct实现了某个接口里的所有方法,叫做这个struct实现了该接口。如下例中
package main

import (
	"fmt"
)


type Retriever interface { // 定义接口Retriever
	Get(url string) string
	isDelete() bool
}



// 定义结构体
type RandomName struct {
	context string
	isEmpty bool
}

// 实现接口Retriever的所有方法,让结构体Random实现接口Retriever
func (rn RandomName) Get(url string) string{
	return rn.context + url
}

func (rn RandomName) isDelete() bool{
	rn.context = ""
	rn.isEmpty = false
	return rn.isEmpty
}




// 定义两个普通函数,体验Go中的duck typing
func download(r Retriever) string{ // Retriever是一个接口
	return r.Get("www.baidu.com") //使用者 使用Get()方法
}

func clean(r Retriever) bool{
	return r.isDelete()
}

func main() {

	var r Retriever // 定义变量rn为接口Retriever类型,这里r是接口指针类型
	r = RandomName{"this is a web about ", true} // 结构体RandomName实现接口Retriever
	
	fmt.Println(download(r)) 
	// r是结构体RnadomName,download接受的参数是Retriever接口。
	// 因为结构体RandomName实现接口Retriever故传参为RandomName接口也行
	// duck typing的体现。

	fmt.Println(clean(r))
}
  • 输出结果
this is a web about www.baidu.com
false

接口的值类型

  • 接口变量包括实现者的类型实现者的指针,实现者常常是结构图struct;
  • 接口变量自带指针,如下变量r
  • 接口变量同样采用值传递;因接口自带指针,在定义时没有定义接口指针,故不需要使用接口的指针;
  • 指针接收者实现只能以指针方式使用;值接收者都可以;
  • 表示任何类型: interface{},使用interface{}定义变量,表示该变量类型可以是任意的;
  • 数据类型为interface{}变量可以通过 变量名.(数据类型) 转化为对应的数据类型;
package main

import (
	"fmt"
)

// 接口
type Retriever interface { // 定义接口Retriever
	Get(url string) string
	isDelete() bool
}

// 结构体1
type RandomName1 struct {
	context string
	isEmpty bool
}

func (rn *RandomName1) Get(url string) string{
	return rn.context + url
}

func (rn *RandomName1) isDelete() bool{
	rn.context = ""
	rn.isEmpty = false
	return rn.isEmpty
}


// 结构体二
type RandomName2 struct {
	context string
	isEmpty bool
}

func (rn RandomName2) Get(url string) string{
	return rn.context + url
}

func (rn RandomName2) isDelete() bool{
	rn.context = ""
	rn.isEmpty = false
	return rn.isEmpty
}



func inspect(r Retriever) {
	fmt.Printf("%T %v\n", r, r)

	switch v := r.(type) {
	case *RandomName1:
		fmt.Println("Contents:", v.context)
		fmt.Println("isEmpty", v.isEmpty)
	case RandomName2:
		fmt.Println("Contents:", v.context)
		fmt.Println("isEmpty", v.isEmpty)
	}
}


func main() {

	var r Retriever // 接口变量

	// RandomName1中方法Get、isDelete是指针接收者,故指针变量r只能以指针方式使用
	r = &RandomName1{
		context: "this is a web about ",
		isEmpty: true}
	inspect(r)

	// Type assertion
	name1 := r.(*RandomName1)
	fmt.Println(name1.isEmpty)

	fmt.Println("---------------------")

	// RandomName2中方法Get、isDelete是值接收者,故指针变量r可以使用指针方式或者不使用
	r = RandomName2{
		context: "",
		isEmpty: false,
	}
	//r = &RandomName2{ // 指针方式也没报错
	//	context: "",
	//	isEmpty: false,
	//}
	inspect(r)

	// Type assertion
	if name2, ok := r.(RandomName2); ok {
		fmt.Println(name2.isEmpty)
	} else {
		fmt.Println("this is not RnadomName2 Retriever!")
	}

}
  • 输出结果
*main.RandomName1 &{this is a web about  true}
Contents: this is a web about 
isEmpty true
true
---------------------
main.RandomName2 { false}
Contents: 
isEmpty false
false

接口的组合

  • 将多个接口组合在一起;
package main

import "fmt"

// 接口一
type Retriever interface{
	Get(url string) string
}
// 接口二
type Poster interface {
	Post(url string,
		form map[string]string) string
}

// 接口组合
type Association interface { // 将多个接口组合一起
	Retriever
	Poster
	OtherMethod(other string)
}


// Association接口的实现,结构体
type AssociationStruct struct {
	Contents string
}

func (a *AssociationStruct) Post(url string,
	form map[string]string) string {
	a.Contents = form["contents"]

	return "ok"
} // 以上AssociationStruct结构体实现接口Poster

func (a *AssociationStruct) Get(url string) string{
	return a.Contents
} // 以上AssociationStruct结构体实现接口Retriever

func (a *AssociationStruct) OtherMethod(other string) {
	fmt.Println("other method!")
} // 以上AssociationStruct结构体实现接口Association


// 其他函数
const url string = "http://www.imooc.com"

// 修改参数contents的值
func assiociation(a Association) string{

	a.Post(url, map[string]string {
		"contents" : "another faked imooc.com",
	}) //  调用已实现接口的方法,修改contents值

	return a.Get(url)
}


func main() {
	var a Association // 接口变量
	a = &AssociationStruct{"this is a fake imooc.com"}

	fmt.Println(assiociation(a))
}
  • 输出结果
another faked imooc.com

常用系统接口

  1. 接口Stringer

    系统中接口定义

    type Stringer interface {
    	String() string
    }
    
  2. 接口Reader

    系统中接口定义

    type  Reader interface {
    	Read(p []byte) (n int, err error) // 可以从文件中读取数据
    }
    
  3. 接口Writer
    系统中接口定义

    type  Writer interface {
    	Write(p []byte) (n int, err error) // 可以写数据到文件中
    }
    
  • 例子
package main

import (
	"fmt"
)


type Retriever interface {
	Get(url string) string
}

type Association interface {
	Retriever
	fmt.Stringer
}



// 结构体RetrieverStruct实现接口Retriever和接口Stringer
type RetrieverStruct struct {
	Contents string
}

func (r *RetrieverStruct) Get(url string) string {  // 实现接口Retriever
	return r.Contents
}

func (r *RetrieverStruct) String() string{ // 实现接口Stringer
	return fmt.Sprintf(
		"Retriever : {Contents = %s}", r.Contents)
}

func main() {

	var a Association
	a = &RetrieverStruct{"this is a fake imooc.com"}

	fmt.Println(a.String())
}
  • 输出结果
Retriever : {Contents = this is a fake imooc.com}

参考链接

静态语言和动态语言的区别
编程语言中的 DUCK TYPING

你可能感兴趣的:(Golang,编程语言)