﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>C++博客-溪流漫话-随笔分类-Golang</title><link>http://www.cppblog.com/Streamlet/category/21409.html</link><description>荒废中，求警醒~</description><language>zh-cn</language><lastBuildDate>Sun, 20 Sep 2020 06:57:28 GMT</lastBuildDate><pubDate>Sun, 20 Sep 2020 06:57:28 GMT</pubDate><ttl>60</ttl><item><title>Go语言的赞和喷</title><link>http://www.cppblog.com/Streamlet/archive/2020/09/20/217465.html</link><dc:creator>溪流</dc:creator><author>溪流</author><pubDate>Sun, 20 Sep 2020 06:16:00 GMT</pubDate><guid>http://www.cppblog.com/Streamlet/archive/2020/09/20/217465.html</guid><wfw:comment>http://www.cppblog.com/Streamlet/comments/217465.html</wfw:comment><comments>http://www.cppblog.com/Streamlet/archive/2020/09/20/217465.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/Streamlet/comments/commentRss/217465.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/Streamlet/services/trackbacks/217465.html</trackback:ping><description><![CDATA[<p>（原发于 GitHub Pages，2019-01-01 23:22:43）</p>
<p>2019 年，我回来了。</p>
<p>不知不觉中，我入 PHP 的坑已经 3 年有余，入 Go 的坑也大半年了。作为不评论不舒服斯基星人，自然要对 Go 品头论足一番的。</p>
<p>总的一句话，Go 的一些特性确实恰到好处，然而更多的地方却是平庸、繁琐、束缚，以至于我想不到它是适合哪些场景的。</p>
<p>静态语言里，C、C++ 有着明显的适用领域：你要想老老实实写程序，不玩任何花招，就用 C 吧，至少你能掌控一切，实在想玩你还有宏这个大杀器；你要想玩点花招，那就用 C++，代价就是需要自身水平更高，能掌控到多大层次就写多大层次，不懂的不要不懂装懂去用，总体来说还是安全的。</p>
<p>脚本语言里，如果要随便写点什么工具，python 啥的挺方便的；写点网络的，就用世界上最好的语言 PHP 吧。什么？你说 Java？实在没办法，体量太大，公司要你用你就用吧。不过就其本质而言，其实 Java 和今天的主角——Go 是同一类的。不是说他们语法像，是指应用场景（不过这个领域拿 PHP 写显然会更爽）。</p>
<p>嗯——为了表达出真实的意思，我想用词稍微犀利点，请先做一下心理建设。</p>
<p>我不怎么懂 Java，就我浅薄的了解而言，如果你的公司、团队有很多傻逼，甚至你自己也是，业务上又正好可以用 Java 界的一些现成的框架、组件，那么用 Java 肯定是没错的啦。它确实有一种魔力，让你无论多傻逼，也绝对写不出错得多么隐蔽、精妙无比的代码；同时让你无论多牛逼，也写不出多么精彩绝伦、言简意赅的代码。Go 也有这种特质，甚至有些地方比起 Java 更有过之而不及。不信请看：</p>
<p>别人家的写法：</p>
<pre><code class="language-C++">r = f(p1, p2 != null ? p2 : p3)
</code></pre>
<pre><code class="language-PHP">$r = f($p1, $p2 ?? $p3) 
</code></pre>
<p>Go 家的写法：</p>
<pre><code class="language-GO">var r someType
if p2 != nil {
	r = f(p1, p2)
} else {
	r = f(p1, p3)
}
</code></pre>
<p>为什么要设计成这样？Go 爸爸说：你们有些人啊，会嵌套很多层 <code>?:</code>，导致代码可读性太差啦，于是禁止你们使用 <code>?:</code>，这是家法。在这里，作为熊孩子的代表，我来告诉大家怎样写出让 Go 爸爸无语的代码：</p>
<pre><code class="language-GO">	foo := 1
	bar := 2
	var foobar int
	if foo &gt; bar { if bar &gt; 1 { foobar = 1 } else if bar &lt; 0 { foobar = 2 } else { foobar = 3 } } else { if foo &gt;= 3 { foobar = 4 } else { foobar = 5 } }
</code></pre>
<p>怎么样？可读性差不差？</p>
<p>看到了吧，这种傻逼是防不住的，他愿意把 ?: 嵌套好多层，它同样可能会把 <code>if else</code> 嵌套好多层。有素质的人会怎么做？遇到 <code>?:</code> 嵌套太多立马拆成 <code>if else</code>。所以结论是，即使去掉 <code>?:</code>，傻逼还是傻逼，但是正常的人写代码就会很啰嗦；支持 <code>?:</code>，傻逼还是傻逼，正常人用起来爽。要知道一层 ?: 的场景占所有 <code>?:</code> 场景的比例还是很高的吧。我觉得可以这样，Go 爸爸可以统计一些工业级代码库的 <code>?:</code> 嵌套层数，作为数据支撑（比如自家 Chrome 里一层 <code>?:</code> 占 95%，两层占 4.9），然后再在语言层面只支持一层 <code>?:</code>，编译参数可选打开两层，不支持两层以上。这就功在千秋了。</p>
<p>除了 不支持 <code>?:</code>，Go 爸爸还有很多这样的设计，随便举几个例子：</p>
<ol>
<li>不支持默认参数</li>
<li>不支持运算符重载</li>
<li>不提供 goroutine id（以及 gls、可重入锁）</li>
</ol>
<p>特别是第三点，也是这种思路，因为你们可能会滥用，所以我不提供。类似这种“爸爸思路”，是我今天要喷的最大喷点。前两点也许是抄 Java 的，不怪 Go 爸爸。</p>
<p>综上，Go 爸爸通过扼杀一些基本语法或者一些基础信息，来防止傻逼干坏事，同时让正常人用起来很啰嗦，同时还可能防止不了傻逼干坏事。这跟 Java 通过不提供高级语法来防止傻逼干坏事是师出同门啊，而且他们正好都能写网络服务程序，你说它们像不像？</p>
<p>然而，Go 爸爸也有精分的时候，它居然发明语法糖了耶！比如 <code>if</code> 可以执行一个句句。单就这个语法而言，我的态度是中立偏赞。赞是赞 Go 爸爸确实用过心了，某些时候挺方便，还能缩小变量作用域；不过这个总归是可有可无的，毕竟换一行写也不会死，要变量作用域加个大括号就行。</p>
<p>小语法方面倒是有个亮点，那就是 switch 的隐式 break、显式 fallthrough 处理。不多展开了。</p>
<p>除了防傻逼，Go 爸爸还有一个思路，就是只许州官放火，不许百姓点灯。有两个语法点——</p>
<ol>
<li>泛型</li>
<li>逗号ok断言</li>
</ol>
<p>先说泛型吧。不支持泛型其实我挺能理解的，因为他确实比 <code>?:</code> 复杂多了，傻逼用不起，<code>?:</code> 都没有，怎么可能会有泛型呢。然而 Go 爸爸有特权呀，它的 map、chan 可都是泛型的哦。但是你要写一个泛型的语法结构的话，对不起没有。</p>
<p>再说逗号ok断言，同样 Go 爸爸要得起，我们要不起。其实我更想要一个这样的语法：当返回值是 xxx, ok 或者 xxx, err 的时候，我如果用一个返回值接，那么就返回第一个，以便链式调用，同时 !ok 或者 err != nil 的时候 panic。</p>
<p>以上两小点虽然是在喷，不过还好啦。无所谓的。下面讲几个大的方面。</p>
<h5>代码组织</h5>
<p>我特别赞赏 Go 对于 package 级严格的循环依赖检查。然而如果加上其他规则：</p>
<ul>
<li>一个目录一个 package</li>
<li>不同路径下的同名目录也是不同 package</li>
<li>go 代码无法拆成 .h、.cpp</li>
</ul>
<p>实际可操作性就会非常差。要拆 package，就要把依赖关系梳理得特别严格，半点不得马虎。这对工一般的程代码来说是个极大的挑战。</p>
<p>我更倾向于做成函数级循环依赖检查，或者不限制（毕竟递归函数也是要支持的嘛）。</p>
<p>这部分，中立偏喷，偏喷是因为 Go 爸爸用了我的小名 internal。</p>
<h5>OOP</h5>
<p>我特别赞赏 Go 对于 OOP 泛滥成灾的思考与探索，以及对于终结这阵 Java 带来的不正之风的决心。怎么可能万物都是 class 呢。但同时，Go 还是有点矫枉过正的，如果我需要利用传统的 OOP 来搞事情，就非常麻烦，你甚至都无法写出一个框架来。你只能写库让别人用。虽然我也不喜欢框架，但有的时候是需要框架的。</p>
<p>defer 非常切中痛点，特别解决一堆错误 return 外加资源释放的问题。相比之下C 里只能用 goto，C++ 本身不支持但可以玩出 LOKI_ON_BLOCK_EXIT 或者 BOOST_SCOPE_EXIT。defer 一举解决问题。（要是能再增加命名 defer 以及撤销 defer 的功能就更好了。没错，你也许看出来了，我觉得 LOKI_ON_BLOCK_EXIT 是最完美的方案。）</p>
<p>虽然 defer 很好，但不意味着析构就没用武之地了。理想的情况是，析构、多态、defer 都要有……</p>
<p>这部分我的态度是中立。</p>
<h5>错误处理</h5>
<p>终于要点赞了。错误处理是在我看来 go 完胜的地方，恰到好处地处理问题，又防止滥用。也矫正 Java 带来的歪风邪气。</p>
<p>Java 的设计，让人不得不用异常来处理业务。甚至 Java 自己还帮我们分好类了：一种是不是异常的异常，用来处理业务；另一种是真的异常。一些用惯 Java 的傻逼跑到 C++、PHP、Python 里乱拉屎，到处是 try catch。</p>
<p>Go 爸爸一声令下，万籁俱寂。</p>
<h5>goroutine</h5>
<p>最后不得不说说 gorouthine，毕竟是卖点嘛。我的态度中立偏赞。赞是因为这是一种太有创意的方案，居然想在在语言层面解决多线程、并发问题；不过我还是觉得这更多的是应用层面的问题，做到官方库里会更好，而不是做成语法。</p>
<h1>总结</h1>
<p>刚开始用 Go 的时候，特别亮眼，简直处处是亮点，然后越接触越讨厌，一点也不耐看……看得出来，设计者糅合了 C、python、Java 的一些特性，并融入了自己的独特的理解。Go的设计者真的特别特立独行且坚持己见，一些我喜欢的特性因为他们的坚持而存在下来，一些我讨厌的特性也因为他们的坚（Gu）持（Zhi）而不能有所改观。就这样的 Go，想代替 C 作为系统语言，是没戏的；想在网络服务有一番作为，抢 Java 的份额，或许是有机会的，不过最多只能抢 Java 的，连 C# 的都抢不了，C++、PHP 更抢不了。</p>
<p>嗯，除了特定的不得不用的场合，反正我是不会特意用 Go 的。</p>
<p>2019，新年快乐！</p>
<img src ="http://www.cppblog.com/Streamlet/aggbug/217465.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/Streamlet/" target="_blank">溪流</a> 2020-09-20 14:16 <a href="http://www.cppblog.com/Streamlet/archive/2020/09/20/217465.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>