duck typing的概念
描述事物的外部行为而非内部结构。
在动态语言设计中,可以解释为无论一个对象是什么类型的,只要它具有某类型的行为(方法),则它就是这一类型,而不在于它是否显示的实现或者继承。
动态类型语言:是在运行时确定数据类型的语言,变量在使用之前不需要类型声明,通常变量的类型是被赋值的那个值的类型。如Python
静态类型语言:在编译时变量的数据类型就可以确定的语言。如C++
像C++一类的静态类型语言,传入的参数必须要是所要求的类型才行,要想表现出不同的行为,就得通过继承、多态等机制。
//python中的duck typing
//运行时才知道传入的retriver有没有get;需要注释来说明接口
def download(retriever):
return retriever.get("www.imooc.com")
//C++中的duck typing
//编译时才知道传入的retriver有没有get;需要注释来说明接口
template <class R>
string download(const R& retriver){
return retriever.get("www.imooc.com");
}
//java中的类似代码
//传入的参数必须实现Retriver接口;不是duck typing
//若一个类有get方法,但是没有实现Retriver接口,也不能给download用,因此不是duck typing
<R extends Retriver>
String download(R r){
return r.get("www.imooc.com");
}
//go语言的duck typing
//什么类型都是穿,只要实现了get方法就可以;可以同时实现好几个接口;具有类型检查
接口
在面向对象编程中,可以这么说:“接口定义了对象的行为”, 那么具体的实现行为就取决于对象了。
在go语言中,接口就是一系列行为的集合。
接口的作用:1.如果你写了一个类, 一年后你对这个类不满意, 你想重新实现一个, 那为了保证兼容性, 你肯定不能完全写个新的对吧。则可以在刚开始写这个类的时候就想好这个类可以提供哪些方法, 然后定义一个接口, 然后一年后你写个新的, 你直接实现这个接口就好了。可以方便的进行修改和重构。2.var t Printer = &User{1, “Tom”} Printer是接口,User实现了Printer,因此这句话就可以实现了多态性。
接口由使用者定义。download(使用者)------>retriever(实现者)
实现者不需要声明实现了哪个接口,只要实现了其方法就行。由使用者规定这个Retriever必须有Get方法。
type Retriever interface {
Get(url string) string
}
func download(r Retriever) string {
return r.Get("http://www.imooc.com")
}
//接口是实现是隐式的,只要实现里面的方法。
// 一种实现
type Retrievers struct {
Contents string
}
func (r Retrievers) Get(url string) string {
return r.Contents
}
//另一种实现
type Retrievers struct {
}
func (r Retrievers) Get(url string) string {
resp, err := http.Get(url)
if err != nil {
panic(err)
}
res, err := httputil.DumpResponse(resp, true)
resp.Body.Close()
if err != nil {
panic(err)
}
return string(res)
}
func main() {
var r Retriever
//实现了多态
r = mock.Retrievers{"this is a fake Retriever"}
r = real.Retrievers{}
fmt.Println(download(r))
}
接口的值类型
接口变量里面有什么?一个是实现者的类型,一个是实现者的值/实现者的指针。
r = &real.Retrievers{
"Mozilla/5.0",
time.Minute,
}
fmt.Printf("%T, %v\n", r, r)
//*real.Retrievers &{Mozilla/5.0 1m0s}
r = mock.Retrievers{"this is a fake Retriever"}
fmt.Printf("%T, %v\n", r, r)
//mock.Retrievers, {this is a fake Retriever}
interface{}表示任何类型
package queue
type Queue []interface{}
func (q *Queue) Push(v interface{}) {
*q = append(*q, v.(int))
}
func (q *Queue) Pop() int {
head := (*q)[0]
*q = (*q)[1:]
return head.(int) //interface{}类型具体化为int类型
}
func (q *Queue) IsEmpty() bool {
return len(*q) == 0
}
go语言的方法集。
从值的角度来看规则
Values Methods Receivers
T (t T)
*T (t T) and (t *T)
T类型的值的方法集只包含值接收者声明的方法。而指向T类型的指针的方法集既包含值接收者声明的方法,也包含指针接收者声明的方法。
从接收者的角度来看规则
Methods Receivers Values
(t T) T and *T
(t *T) *T
指针接收者实现只能以指针方式使用;值接收者都可以。
使用指针接收者来实现一个接口,那么只有指向那个类型的指针才能够实现对应的接口。如果使用值接收者来实现一个接口,那么那个类型的值和指针都能够实现对应的接口。
type assertion和type switch用法。
type Retriever interface {
Get(url string) string
}
func download(r Retriever) string {
return r.Get("http://www.imooc.com")
}
func main() {
var r Retriever
r = mock.Retrievers{"this is a fake Retriever"}
//r = &mock.Retrievers{"this is a fake Retriever"}
inspect(r)
//type assertion
mockRetriever := r.(mock.Retrievers)
fmt.Println(mockRetriever.Contents)
/*
type Retrievers struct {
UserAgent string
TimeOut time.Duration
}
//使用指针接收者
func (r *Retrievers) Get(url string) string {}*/
r = &real.Retrievers{
"Mozilla/5.0",
time.Minute,
}
inspect(r)
//type assertion
realRetriever := r.(*real.Retrievers)
fmt.Println(realRetriever.TimeOut)
}
func inspect(r Retriever) {
fmt.Printf("%T %v\n", r, r) //%T表示类型,%v表示结构体的值
//type switch
switch v := r.(type) {
case mock.Retrievers:
fmt.Println("Contents: ", v.Contents)
case *real.Retrievers:
fmt.Println("UserAgent: ", v.UserAgent)
}
}
/*
mock.Retrievers {this is a fake Retriever}
Contents: this is a fake Retriever
this is a fake Retriever
*real.Retrievers &{Mozilla/5.0 1m0s}
UserAgent: Mozilla/5.0
1m0s
*/
接口的组合
只要实现了Retriever和Poster接口的所有函数,就相当于实现了这个接口。
使用者可以实现几个接口。
//接口的组合
type RetrieverPoster interface {
Retriever
Poster
}
func session(s RetrieverPoster) string {
s.Post("http://www.imooc.com", map[string]string{"contents": "This is another fake imooc.com"})
return s.Get("http://www.imooc.com")
}
//Retrievers即实现了Retriever接口也实现了Poster接口
type Retrievers struct {
Contents string
}
func (r *Retrievers) Post(url string, form map[string]string) string {
r.Contents = form["contents"]
return "ok"
}
func (r *Retrievers) Get(url string) string {
return r.Contents
}
常用的系统接口
//stringer接口 格式化我们的结构
type Retrievers struct {
Contents string
}
func (r *Retrievers) String() string {
return fmt.Sprintf("Retriever: {Contents: %s}", r.Contents)
}
fmt.Printf("%T %v\n", r, r)
//*mock.Retrievers Retriever: {Contents: this is a fake Retriever}
Reader和Writer接口。
func printFile(filename string) {
file, err := os.Open(filename)
if err != nil {
panic(err)
}
printFileContents(file)
}
//只要实现了Reader接口的类型,都可以使用这个方法,不一定是文件,如下面代码中的字符串
func printFileContents(reader io.Reader) {
scanner := bufio.NewScanner(reader)
for scanner.Scan() {
fmt.Println(scanner.Text())
}
}
func main() {
//fmt.Println(convertToBin(13))
printFile("abc.txt")
s := `abc"a"
das
张晋嘉
`
printFileContents(strings.NewReader(s))
}