首页 > 其他分享 >为什么我们需要一门新语言——Go语言

为什么我们需要一门新语言——Go语言

时间:2023-06-21 12:00:56浏览次数:39  
标签:... Java 语言 一门 编程 接口 Go


编程语言已经非常多,偏性能敏感的编译型语言有 C、C++、Java、C#、Delphi和Objective-C等,偏快速业务开发的动态解析型语言有 PHP、Python、Perl、Ruby、JavaScript和Lua等,面向特定领域的语言有 Erlang、R和MATLAB等,那么我们为什么需要 Go这样一门新语言呢? 在2000年前的单机时代, C语言是编程之王。随着机器性能的提升、软件规模与复杂度的提高,Java逐步取代了C的位置。尽管看起来 Java已经深获人心,但 Java编程的体验并未尽如人意。历年来的编程语言排行榜(如图 0-1所示)显示, Java语言的市场份额在逐步下跌,并趋近于 C语言的水平,显示了这门语言后劲不足。


图0-1编程语言排行榜

Go语言官方自称,之所以开发 Go语言,是因为“近 10年来开发程序之难让我们有点沮丧”。这一定位暗示了 Go语言希望取代 C和Java的地位,成为最流行的通用开发语言。 Go希望成为互联网时代的 C语言。多数系统级语言(包括 Java和C#)的根本编程哲学来源于 C++,将C++的面向对象进一步发扬光大。但是Go语言的设计者却有不同的看法,他们认为C++ 真的没啥好学的,值得学习的是 C语言。C语言经久不衰的根源是它足够简单。因此, Go语言也要足够简单!

那么,互联网时代的 C语言需要考虑哪些关键问题呢?首先,并行与分布式支持。多核化和集群化是互联网时代的典型特征。作为一个互联网时代的C语言,必须要让这门语言操作多核计算机与计算机集群如同操作单机一样容易。其次,软件工程支持。工程规模不断扩大是产业发展的必然趋势。单机时代语言可以只关心问题本身的解决,而互联网时代的 C语言还需要考虑软件品质保障和团队协作相关的话题。最后,编程哲学的重塑。计算机软件经历了数十年的发展,形成了面向对象等多种学术流派。什么才是最佳的编程实践?作为互联网时代的 C语言,需要回答这个问题。接下来我们来聊聊 Go语言在这些话题上是如何应对的。

并发与分布式

多核化和集群化是互联网时代的典型特征,那语言需要哪些特性来应对这些特征呢?第一个话题是并发执行的“执行体”。执行体是个抽象的概念,在操作系统层面有多个概念与之对应,比如操作系统自己掌管的进程( process)、进程内的线程( thread)以及进程内的协程(coroutine,也叫轻量级线程)。多数语言在语法层面并不直接支持协程,而通过库的方式支持的协程的功能也并不完整,比如仅仅提供协程的创建、销毁与切换等能力。如果在这样的协程中调用一个同步 IO操作,比如网络通信、本地文件读写,都会阻塞其他的并发执行协程,从而无法真正达到协程本身期望达到的目标。

Go语言在语言级别支持协程,叫 goroutine。Go语言标准库提供的所有系统调用( syscall)操作,当然也包括所有同步 IO操作,都会出让 CPU给其他goroutine,这让事情变得非常简单。我们对比一下Java和Go,近距离观摩下两者对“执行体”的支持。

为了简化,我们在样例中使用的是 Java标准库中的线程,而不是协程,具体代码如下:

public class MyThread implements Runnable { 
String arg; 
public MyThread(String a) { arg = a; } 
public void run() { // ... } 
public static void main(String[] args) { new Thread(new MyThread("test")).start(); // ... 
} 
}

相同功能的代码,在 Go语言中是这样的:

func run(arg string) { 
// ... 
} 
func main() { 
go run("test") 
... 
}

对比非常鲜明。我相信你已经明白为什么 Go语言会叫 Go语言了:Go语言献给这个时代最好的礼物,就是加了 go这个关键字。当然也有人会说,叫 Go语言是因为它是 Google出的。好吧,这也是个不错的闲聊主题。 第二个话题是“执行体间的通信”。执行体间的通信包含几个方式:

  • 执行体之间的互斥与同步
  • 执行体之间的消息传递


Go语言推荐采用“ Erlang风格的并发模型”的编程范式,尽管传统的“共享内存模型”仍然被保留,允许适度地使用。在 Go语言中内置了消息队列的支持,只不过它叫通道( channel)。两个goroutine之间可以通过通道来进行交互。

软件工程 单机时代的语言可以只关心问题本身的解决,但是随着工程规模的不断扩大,软件复杂度的不断增加,软件工程也成为语言设计层面要考虑的重要课题。多数软件需要一个团队共同去完成,在团队协作的过程中,人们需要建立统一的交互语言来降低沟通的成本。规范化体现在多个层面,如:

  • 代码风格规范
  • 错误处理规范
  • 包管理
  • 契约规范(接口)
  • 单元测试规范
  • 功能开发的流程规范

Go语言很可能是第一个将代码风格强制统一的语言,例如 Go语言要求 public的变量必须以大写字母开头,private变量则以小写字母开头,这种做法不仅免除了public、private关键字,更重要的是统一了命名风格。 另外,Go语言对 { }应该怎么写进行了强制,比如以下风格是正确的:

if expression { ... }

但下面这个写法就是错误的:

if expression { ... }

而C和Java语言中则对花括号的位置没有任何要求。哪种更有利,这个见仁见智。但很显然的是,所有的 Go代码的花括号位置肯定是非常统一的。最有意思的其实还是 Go语言首创的错误处理规范:

f, err := os.Open(filename) 
if err != nil { log.Println("Open file failed:", err) return 
} defer f.Close() ... // 操作已经打开的 f文件

这里有两个关键点。其一是 defer关键字。 defer语句的含义是不管程序是否出现异常,均在函数退出时自动执行相关代码。在上面的例子中,正是因为有了 defer,才使得无论后续是否会出现异常,都可以确保文件被正确关闭。其二是 Go语言的函数允许返回多个值。大多数函数的最后一个返回值会为 error类型,以在错误情况下返回详细信息。 error类型只是一个系统内置的interface,如下:

type error interface { Error() string }

有了error类型,程序出现错误的逻辑看起来就相当统一。在Java中,你可能这样写代码来保证资源正确释放:

Connection conn = ...; 
try { Statement stmt = ...; try { 
ResultSet rset = ...; try { 
... // 正常代码 } finally { 
rset.close(); 
} } finally { 
stmt.close(); 
} } finally { 
conn.close(); }

完成同样的功能,相应的 Go代码只需要写成这样:

conn := ... defer conn.Close() 
stmt := ... defer stmt.Close() 
rset := ... defer rset.Close() ... // 正常代码

对比两段代码, Go语言处理错误的优势显而易见。当然,其实 Go语言带给我们的惊喜还有很多,后续有机会我们可以就某个更具体的话题详细展开来谈一谈。

编程哲学

计算机软件经历了数十年的发展,形成了多种学术流派,有面向过程编程、面向对象编程、函数式编程、面向消息编程等,这些思想究竟孰优孰劣,众说纷纭。

C语言是纯过程式的,这和它产生的历史背景有关。 Java语言则是激进的面向对象主义推崇者,典型表现是它不能容忍体系里存在孤立的函数。而 Go语言没有去否认任何一方,而是用批判吸收的眼光,将所有编程思想做了一次梳理,融合众家之长,但时刻警惕特性复杂化,极力维持语言特性的简洁,力求小而精。

从编程范式的角度来说, Go语言是变革派,而不是改良派。对于C++、Java和C#等语言为代表的面向对象( OO)思想体系,Go语言总体来说持保守态度,有限吸收。首先,Go语言反对函数和操作符重载(overload),而C++、Java和C#都允许出现同名函数或操作符,只要它们的参数列表不同。虽然重载解决了一小部分面向对象编程( OOP)的问题,但同样给这些语言带来了极大的负担。而 Go语言有着完全不同的设计哲学,既然函数重载带来了负担,并且这个特性并不对解决任何问题有显著的价值,那么 Go就不提供它。其次,Go语言支持类、类成员方法、类的组合,但反对继承,反对虚函数( virtual function)和虚函数重载。确切地说, Go也提供了继承,只不过是采用了组合的文法来提供:

type Foo struct { Base ... 
    } 

func (foo *Foo) Bar() { ... }

再次,Go语言也放弃了构造函数( constructor)和析构函数(destructor)。由于Go语言中没有虚函数,也就没有 vptr,支持构造函数和析构函数就没有太大的价值。本着“如果一个特性并不对解决任何问题有显著的价值,那么 Go就不提供它”的原则,构造函数和析构函数就这样被Go语言的作者们干掉了。

在放弃了大量的 OOP特性后,Go语言送上了一份非常棒的礼物:接口( interface)。你可能会说,除了 C这么原始的语言外,还有什么语言没有接口呢?是的,多数语言都提供接口,但它们的接口都不同于 Go语言的接口。

Go语言中的接口与其他语言最大的一点区别是它的非侵入性。在 C++、Java和C#中,为了实现一个接口,你需要从该接口继承,具体代码如下:

class Foo implements IFoo { // Java文法 ... } 
class Foo : public IFoo { // C++文法 ... } 
IFoo* foo = new Foo;

在Go语言中,实现类的时候无需从接口派生,具体代码如下:

type Foo struct { // Go 文法 ... } 
var foo IFoo = new(Foo)

只要Foo实现了接口IFoo要求的所有方法,就实现了该接口,可以进行赋值。 Go语言的非侵入式接口,看似只是做了很小的文法调整,实则影响深远。其一,Go语言的标准库再也不需要绘制类库的继承树图。你只需要知道这个类实现了哪些方法,每个方法是啥含义就足够了。其二,不用再纠结接口需要拆得多细才合理,比如我们实现了 File类,它有下面这些方法:

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

那么,到底是应该定义一个 IFile接口,还是应该定义一系列的 IReader、IWriter、 ISeeker和ICloser接口,然后让File从它们派生好呢?事实上,脱离了实际的用户场景,讨论这两个设计哪个更好并无意义。问题在于,实现 File类的时候,我怎么知道外部会如何用它呢?

其三,不用为了实现一个接口而专门导入一个包,而目的仅仅是引用其中的某个接口的定义。在Go语言中,只要两个接口拥有相同的方法列表,那么它们就是等同的,可以相互赋值,如对于以下两个接口,第一个接口:


为了引用另一个包中的接口而导入这个包的做法是不被推荐的。因为多引用一个外部的包,就意味着更多的耦合。 除了OOP外,近年出现了一些小众的编程哲学, Go语言对这些思想亦有所吸收。例如, Go语言接受了函数式编程的一些想法,支持匿名函数与闭包。再如, Go语言接受了以 Erlang语言为代表的面向消息编程思想,支持 goroutine和通道,并推荐使用消息而不是共享内存来进行并发编程。总体来说, Go语言是一个非常现代化的语言,精小但非常强大。

小结 在十余年的技术生涯中,我接触过、使用过、喜爱过不同的编程语言,但总体而言, Go语言的出现是最让我兴奋的事情。我个人对未来 10年编程语言排行榜的趋势判断如下:

  • Java语言的份额继续下滑,并最终被 C和Go语言超越;
  • C语言将长居编程榜第二的位置,并有望在 Go取代Java前重获语言榜第一的宝座;
  • Go语言最终会取代 Java,居于编程榜之首。

由七牛云存储团队编著的这本书将尽可能展现出 Go语言的迷人魅力。希望本书能够让更多人理解这门语言,热爱这门语言,让这门优秀的语言能够落到实处,把程序员从以往繁杂的语言细节中解放出来,集中精力开发更加优秀的系统软件。 

标签:...,Java,语言,一门,编程,接口,Go
From: https://blog.51cto.com/u_15767091/6528093

相关文章

  • Go应用性能优化的8个最佳实践,快速提升资源利用效率!
    作者|IfedayoAdesiyan翻译|Seal软件链接|https://earthly.dev/blog/optimize-golang-for-kubernetes/ 优化服务器负载对于确保运行在Kubernetes上的Golang应用程序的高性能和可扩展性至关重要。随着企业越来越多地采用容器化的方式和Kubernetes来部署和管理应用程序,采......
  • 8086汇编语言精讲1 :基础知识
    基础知识   也就是说伪指令和其他符号在编译器上将汇编指令翻译成的机械码改动,形成新的机械码      8086可以寻址2^16大小的数据量,因为它有16根数据总线,同时是在2进制下计数         ......
  • 精通c语言中的指针(精通c指针1)
    之前学c语言的时候,指针这一章学的半吊子,似是而非,最近经过学习,对指针有个更加深刻的理解。如果之前学过c指针,并且可以使用指针随心所欲操作内存中的任何数据,那么说明指针学好了,否则就是半吊子。如果之前学过指针,那最好忘记之前学过的所有概念,把指针当做一种新的类型来学习。这种......
  • 全面解读Objective-C语言及Cocoa特性——《Objective-C基础教程》
    媒体评论“这是我读过的最好的一本编程书。我从头到尾逐字逐句地读完了它,可读性真强啊!试问,现在有几本技术书能达到这种程度?”——Amazon读者评论“这本书结构清晰,逻辑性强,风格幽默……借助本书,你可以毫不费力地从一个初学者摇身一变升级为优秀的Objective-C编程人员。”——Ama......
  • Rust语言 - 接口设计的建议之受约束(Constrained)
    Rust语言-接口设计的建议之受约束(Constrained)RustAPI指南GitHub:https://github.com/rust-lang/api-guidelinesRustAPI指南中文:https://rust-chinese-translation.github.io/api-guidelines/RustAPI指南:https://rust-lang.github.io/api-guidelines/受约束(Constrai......
  • 权威解答495个最常遇到的C语言问题
     该书上市后好评如潮,第一次印刷不到1个月就全部售罄。更多C语言经典图书推荐:《编程精粹:编写高质量C语言代码》     媒体评论:“本书是Summit以及CFAQ在线列表的许多参与者多年心血的结晶,是C语言界最为珍贵的财富之一。我向所有C语言程序员推荐本书。”          ......
  • Mongodb 在工业场景下的数字解决方案
    开头还是介绍一下群,如果感兴趣polardb,mongodb,mysql,postgresql ,redis等有问题,有需求都可以加群群内有各大数据库行业大咖,CTO,可以解决你的问题。译:非常高兴今天和大家在一起,来讨论一些抽象的感念,像工业4.0 ,数字制造业等概念。通过今天演讲我们来说说mongodb altas在行业......
  • Gobinet和QMI-WWAN
    高通Gobi无线宽带芯片技术,只需一个模块即可支持多种移动宽带网络QMI是QualcomMessageInterface的简称,Gobinet和QMI-WWAN的作用是一样的都是Linux上高通模块的驱动程序,分界线就是linux3.4内核,之前高通的QMI-WWAN并没有添加到linux内核中,所以高通就开发了Gobinet软件包,包括4G......
  • 自然语言处理 Paddle NLP - 情感分析技术及应用-理论
    自然语言处理PaddleNLP-信息抽取技术及应用定义:对带有感情色彩的主观性文本进行分析、处理、归纳和推理的过程主观性文本分析:技术难点背景知识电视机的声音小(消极)电冰箱的声音小(积极)反讽/隐晦情感表达:我觉得你的香水不错,你应该关起窗户省着点闻(消极)网......
  • Golang 中文转拼音
    翻遍整个GitHub,Golang中文转拼音类库,怎么就这么难找呢?于是我造了一个轮子:中文转拼音类库.目前来说应该是最好用的了.GitHub传送门:https://github.com/Lofanmi/pinyin-golang如果说基于汉字拼音字典,逐个汉字替换,也是可以转换的,但是碰到多音字就很麻烦了.而......