编程语言已经非常多,偏性能敏感的编译型语言有 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语言的迷人魅力。希望本书能够让更多人理解这门语言,热爱这门语言,让这门优秀的语言能够落到实处,把程序员从以往繁杂的语言细节中解放出来,集中精力开发更加优秀的系统软件。
分享到:
相关推荐
ASP论坛网站实例开发源码——Punbb v1.2.20 多国语言版是一个基于ASP(Active Server Pages)技术的开源论坛系统。Punbb是一个轻量级、高效且易于使用的论坛软件,其设计目标是提供一个简洁、快速的讨论平台,尤其...
易语言是一种专为中国人设计的编程语言,它的目标是让编程变得简单易学。在“易语言窗口置顶”这个主题中,我们主要探讨的是如何使用易语言来实现一个功能,使得程序窗口能够始终显示在最前端,即窗口置顶功能。这个...
在Windows操作系统中,有时我们需要同时处理多个应用程序,这时将某个窗口始终显示在最上方(即置顶)可以提高工作效率。本文将详细介绍如何使用名为"WINDOW ON TOP"的小巧工具来实现这一功能,并深入探讨相关技术。...
为百度文库用户定制
窗口置顶是指将某个窗口设置为始终显示在其他窗口之上,即使切换到其他应用程序或打开新的窗口,这个被置顶的窗口也会保持在最前面。这对于需要频繁参考或操作某个特定窗口的用户来说非常实用,例如编写代码时查看...
对于置顶功能,我们需要设置`SWP_NOZORDER`和`WS_EX_TOPMOST`标志。`SWP_NOZORDER`防止窗口移动到其他窗口之前,而`WS_EX_TOPMOST`则将窗口设置为顶级窗口,使其始终在其他非顶级窗口之上。 快捷键的设置则涉及到...
将自己想要的窗口置顶:以Google浏览器为例,将自己想要的窗口置顶:以Google浏览器为例,将自己想要的窗口置顶:以Google浏览器为例,将自己想要的窗口置顶:以Google浏览器为例,将自己想要的窗口置顶:以Google...
标题中的“程序置顶”指的是在计算机操作中,让某个应用程序的窗口始终显示在其他所有窗口的上方,即使用户切换到其他程序或者打开新的窗口,这个特定程序的窗口仍然保持在最前端。这是一种非常实用的功能,尤其对于...
XOOPS模组——BBS是一款专为XOOPS内容管理系统设计的论坛模块,它能够无缝集成到XOOPS系统中,提供一个功能完善的在线讨论平台。XOOPS(eXtensible Object-Oriented Portal System)是一个开源的、基于Web的PHP内容...
用户只需要将工具提供的图标(通常是一个小的浮动窗口或系统托盘图标)从任务栏或者桌面拖动到目标窗口上,目标窗口就会被设置为置顶状态。这种交互方式简单易用,不需要用户进行复杂的设置或者记忆快捷键。 在实际...
在本例中,压缩包内包含的文件名为“带置顶功能的记事本.exe”,这表明它是一个可执行文件,用于在Windows操作系统上运行。 描述中提到的“运行后,点击格式选项里面的置顶”,这说明该记事本程序具备了一个不寻常...
浮舟置顶工具 V1.0.0 是一个由VB(Visual Basic)编程语言开发的应用程序,主要用于实现窗口置顶功能。在日常使用电脑时,我们可能会遇到需要将某个窗口始终显示在其他窗口之上,以便在进行多任务操作时保持其可见性...
1、绿色免安装,运行需要.net3.5,首次执行联网自动安装; 2、启动后默认最小化在任务栏,开机自启动; 3、默认ctrl+alt+a快捷键截图,截图后自动置顶显示,双击可以收缩; 4、图标右键“选项”可以按习惯设置软件:...
当用户选择置顶某聊天时,我们需要将该聊天的模型对象移动到`LinkedList`的头部,然后通知`Adapter`数据集发生了变化,`Adapter`会自动更新视图,将置顶的聊天显示在列表顶部。 为了实现列表项点击后置顶的功能,...
这个小工具名为"WinTop.exe",很可能是一个便携式的应用程序,不需要安装即可运行。这种类型的工具通常体积小巧,不占用太多系统资源,易于携带和分享。只需双击"WinTop.exe",用户就可以快速启动并利用窗口置顶功能...
在日常工作中,我们常常需要同时处理多个应用程序,而窗口置顶功能则可以帮助我们将某个重要的窗口始终显示在最前端,避免被其他窗口遮挡,提高工作效率。 窗口置顶技术的核心原理在于利用操作系统提供的API函数来...
在这样的场景下,用户可能需要快速访问特定的窗体,这时实现窗体的置顶显示就显得尤为重要。本文将详细讲解如何在Winform中通过按钮操作实现窗体的置顶显示,以及如何避免窗体重复打开,提升软件的易用性。 首先,...
在开发中,有时我们需要实现一个功能,当用户上滑ListView时,某些特定的条目能够“漂浮”在顶部,即使列表继续滚动,这些条目依然保持可见,这就是所谓的"滑动置顶漂浮"效果。这个功能在很多应用中都有所应用,例如...
首先,我们需要理解“置顶”功能的基本原理。通常,这种效果是通过监听窗口的滚动事件来实现的。当用户滚动到特定位置时,我们将目标元素的CSS样式设置为`position: fixed`,使其脱离正常文档流,固定在屏幕的某个...
"桌面置顶小工具"是一种实用的软件工具,它的主要功能是允许用户将任何窗口设置为始终显示在其他窗口之上,即置顶显示。这种工具对于需要同时处理多个任务或者频繁参考某个特定窗口内容的用户来说非常有用。下面将...