• 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")

2. C++中的 duck typing

template <class R>
string download(const R& retriever) {return retriever.get("http://xxx")
}

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 mainimport ("fmt"
)type Retriever interface { // 定义接口RetrieverGet(url string) stringisDelete() bool
}// 定义结构体
type RandomName struct {context stringisEmpty bool
}// 实现接口Retriever的所有方法,让结构体Random实现接口Retriever
func (rn RandomName) Get(url string) string{return rn.context + url
}func (rn RandomName) isDelete() bool{rn.context = ""rn.isEmpty = falsereturn 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实现接口Retrieverfmt.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 mainimport ("fmt"
)// 接口
type Retriever interface { // 定义接口RetrieverGet(url string) stringisDelete() bool
}// 结构体1
type RandomName1 struct {context stringisEmpty bool
}func (rn *RandomName1) Get(url string) string{return rn.context + url
}func (rn *RandomName1) isDelete() bool{rn.context = ""rn.isEmpty = falsereturn rn.isEmpty
}// 结构体二
type RandomName2 struct {context stringisEmpty bool
}func (rn RandomName2) Get(url string) string{return rn.context + url
}func (rn RandomName2) isDelete() bool{rn.context = ""rn.isEmpty = falsereturn 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 assertionname1 := 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 assertionif 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 mainimport "fmt"// 接口一
type Retriever interface{Get(url string) string
}
// 接口二
type Poster interface {Post(url string,form map[string]string) string
}// 接口组合
type Association interface { // 将多个接口组合一起RetrieverPosterOtherMethod(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结构体实现接口Posterfunc (a *AssociationStruct) Get(url string) string{return a.Contents
} // 以上AssociationStruct结构体实现接口Retrieverfunc (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

常用系统接口

type Stringer interface {String() string
}
type  Reader interface {Read(p []byte) (n int, err error) // 可以从文件中读取数据
}
type  Writer interface {Write(p []byte) (n int, err error) // 可以写数据到文件中
}
  • 例子
package mainimport ("fmt"
)type Retriever interface {Get(url string) string
}type Association interface {Retrieverfmt.Stringer
}// 结构体RetrieverStruct实现接口Retriever和接口Stringer
type RetrieverStruct struct {Contents string
}func (r *RetrieverStruct) Get(url string) string {  // 实现接口Retrieverreturn r.Contents
}func (r *RetrieverStruct) String() string{ // 实现接口Stringerreturn fmt.Sprintf("Retriever : {Contents = %s}", r.Contents)
}func main() {var a Associationa = &RetrieverStruct{"this is a fake imooc.com"}fmt.Println(a.String())
}
  • 输出结果
Retriever : {Contents = this is a fake imooc.com}

参考链接

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