你Go代码写的像“鸭子”吗???

2024-01-10 19:20
文章标签 代码 go 鸭子

本文主要是介绍你Go代码写的像“鸭子”吗???,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

概 述

Go 语言也提供了接口类型,使得我们可以面向接口编程,将实现和接口分离。在我看来,软件的抽象之美也应该以此来表达,和 Java 语言不同的是 Go 并不是那么 “强制”,它使用了一种 鸭子类型 的方式让动态类型成为可能。

Duck Typing

在 Go 中没有 implements 和 extends 这种关键字,这对我们而言反倒轻松了一些,它认为 Go 的接口就像鸭子测试里的描述:

当看到一只鸟走起来像鸭子、游泳起来像鸭子、叫起来也像鸭子,那么这只鸟就可以被称为鸭子。

在鸭子类型中,关注点在于对象的行为,能做什么;而不是关注对象所属的类型。例如,在不使用鸭子类型的语言中,我们可以编写一个函数,它接受一个类型为 “鸭子” 的对象,并调用它的 “走” 和 “叫” 方法。

在使用鸭子类型的语言中,这样的一个函数可以接受一个任意类型的对象,并调用它的 “走” 和 “叫” 方法。如果这些需要被调用的方法不存在,那么将引发一个运行时错误。

任何拥有这样的正确的 “走” 和 “叫” 方法的对象都可被函数接受的这种行为引出了以上表述,这种决定类型的方式因此得名。

啥是鸭子🦆???

我们用 Go 语言来实现一个鸭子类型:

type Duck interface {Swim()    // 游泳Feathers() // 羽毛
}

这里使用 Go 提供的 interface 关键字定义了一个鸭子接口类型,这个接口中提供了鸭子的两种行为:游泳和羽毛是什么样的,但是没有提供实现。

我们见过的鸭子类型可多了去了,下面是你可能见过的:

组合接口

现在我们给鸭子再添加一种嘎嘎叫的能力,一种方式是在原有的接口上添加 “嘎嘎叫” 方法,这样做的话就表示所有的鸭子都应该拥有此能力,假设我们的玩具鸭并不能开口嘎嘎叫,所以它没有这种能力。这时候我们可以将会嘎嘎叫的鸭子单独定义一种类型,在 Go 可以使用组合的方式来实现:

type QuackDuck interface {Quack()  // 嘎嘎叫Duck     // 嵌入接口
}

这样 QuackDuck 类型就拥有了之前 Duck 提供的两种抽象能力,同时还应该拥有嘎嘎叫的能力。

接口实现

前面我们只给出了鸭子的能力定义,还没有任何实现,由于 Go 中没有继承和实现的关键字,想成为上述接口的实现非常简单,只要实现它们定义的方法就可以了。

// RealDuck - 真正的鸭子
type RealDuck struct { }func (RealDuck) Swim() {fmt.Println("用鸭璞向后划水")
}func (RealDuck) Feathers() {fmt.Println("遇到水也不会湿的羽毛")
}func (RealDuck) Quack() {fmt.Println("嘎~ 嘎~ 嘎~")
}// ToyDuck - 玩具鸭
type ToyDuck struct { }func (ToyDuck) Swim() {fmt.Println("以固定的速度向前移动")
}func (ToyDuck) Feathers() {fmt.Println("白色的固定的塑料羽毛")
}

可以看到我们定义了两种鸭子类型,一种是真正的鸭子,它还多实现了一种嘎嘎叫方法,另一个玩具鸭子只有游泳和羽毛这两种行为。

这个编程方式和我们写普通的结构体方法没什么区别,只是对应的方法签名相同,其实这种方式在 Go 语言的标准库中有特别多的应用,比如:io.Reader、io.Writer 和 io.Closer

接口使用

接下来我们可以使用一下这个类型了:

var duck Duck
duck = ToyDuck{}
duck.Swim()
duck.Feathers()

输出

以固定的速度向前移动 白色的固定的塑料羽毛 由于玩具鸭没有嘎嘎叫的能力,所以如果你这么写编译无法通过

错误实现

我们也可以用一种工厂的方式来进行调用:

func Factory(name string) Duck {switch name {case "toy":return &ToyDuck{}case "real":return &RealDuck{}default:panic("No such duck")}
}func main() {duck := Factory("toy")duck.Swim()duck.Feathers()
}

小 结

其实这就是 Go 中的多态体现,仔细体会其中的味道,在阅读其他源码的时候你会更加熟练。但是也有一个问题当代码工程庞大的时候你很难知道一个接口体到底实现了哪些接口,不过有幸的是我们生在最智能的 IDE 时代,在 GoLand 中可以帮你提示。

这篇关于你Go代码写的像“鸭子”吗???的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/591817

相关文章

Go语言开发实现查询IP信息的MCP服务器

《Go语言开发实现查询IP信息的MCP服务器》随着MCP的快速普及和广泛应用,MCP服务器也层出不穷,本文将详细介绍如何在Go语言中使用go-mcp库来开发一个查询IP信息的MCP... 目录前言mcp-ip-geo 服务器目录结构说明查询 IP 信息功能实现工具实现工具管理查询单个 IP 信息工具的实现服

利用Python调试串口的示例代码

《利用Python调试串口的示例代码》在嵌入式开发、物联网设备调试过程中,串口通信是最基础的调试手段本文将带你用Python+ttkbootstrap打造一款高颜值、多功能的串口调试助手,需要的可以了... 目录概述:为什么需要专业的串口调试工具项目架构设计1.1 技术栈选型1.2 关键类说明1.3 线程模

Python Transformers库(NLP处理库)案例代码讲解

《PythonTransformers库(NLP处理库)案例代码讲解》本文介绍transformers库的全面讲解,包含基础知识、高级用法、案例代码及学习路径,内容经过组织,适合不同阶段的学习者,对... 目录一、基础知识1. Transformers 库简介2. 安装与环境配置3. 快速上手示例二、核心模

Java的栈与队列实现代码解析

《Java的栈与队列实现代码解析》栈是常见的线性数据结构,栈的特点是以先进后出的形式,后进先出,先进后出,分为栈底和栈顶,栈应用于内存的分配,表达式求值,存储临时的数据和方法的调用等,本文给大家介绍J... 目录栈的概念(Stack)栈的实现代码队列(Queue)模拟实现队列(双链表实现)循环队列(循环数组

使用Java将DOCX文档解析为Markdown文档的代码实现

《使用Java将DOCX文档解析为Markdown文档的代码实现》在现代文档处理中,Markdown(MD)因其简洁的语法和良好的可读性,逐渐成为开发者、技术写作者和内容创作者的首选格式,然而,许多文... 目录引言1. 工具和库介绍2. 安装依赖库3. 使用Apache POI解析DOCX文档4. 将解析

C++使用printf语句实现进制转换的示例代码

《C++使用printf语句实现进制转换的示例代码》在C语言中,printf函数可以直接实现部分进制转换功能,通过格式说明符(formatspecifier)快速输出不同进制的数值,下面给大家分享C+... 目录一、printf 原生支持的进制转换1. 十进制、八进制、十六进制转换2. 显示进制前缀3. 指

go 指针接收者和值接收者的区别小结

《go指针接收者和值接收者的区别小结》在Go语言中,值接收者和指针接收者是方法定义中的两种接收者类型,本文主要介绍了go指针接收者和值接收者的区别小结,文中通过示例代码介绍的非常详细,需要的朋友们下... 目录go 指针接收者和值接收者的区别易错点辨析go 指针接收者和值接收者的区别指针接收者和值接收者的

使用Python实现全能手机虚拟键盘的示例代码

《使用Python实现全能手机虚拟键盘的示例代码》在数字化办公时代,你是否遇到过这样的场景:会议室投影电脑突然键盘失灵、躺在沙发上想远程控制书房电脑、或者需要给长辈远程协助操作?今天我要分享的Pyth... 目录一、项目概述:不止于键盘的远程控制方案1.1 创新价值1.2 技术栈全景二、需求实现步骤一、需求

Java中Date、LocalDate、LocalDateTime、LocalTime、时间戳之间的相互转换代码

《Java中Date、LocalDate、LocalDateTime、LocalTime、时间戳之间的相互转换代码》:本文主要介绍Java中日期时间转换的多种方法,包括将Date转换为LocalD... 目录一、Date转LocalDateTime二、Date转LocalDate三、LocalDateTim

Go 语言中的select语句详解及工作原理

《Go语言中的select语句详解及工作原理》在Go语言中,select语句是用于处理多个通道(channel)操作的一种控制结构,它类似于switch语句,本文给大家介绍Go语言中的select语... 目录Go 语言中的 select 是做什么的基本功能语法工作原理示例示例 1:监听多个通道示例 2:带