每日新闻

每日新闻

GoCN每日新闻资讯
有问必答

有问必答

Go相关的问题,技术相关的问题
文章分享

文章分享

技术文章分享,让知识传播给更多的人
招聘应聘

招聘应聘

为Gopher服务的招聘应聘平台

技术实践——教你用100行写一个 go 的协程池 (任务池)!!!

Go开源项目有只黑白猫 发表了文章 • 0 个评论 • 254 次浏览 • 2020-01-09 16:44 • 来自相关话题

点击这里,查看封装 GetCap() 方法等重要内容实现Talk is cheap. Show me the code.任务的定义 ...查看全部

点击这里,查看封装 GetCap() 方法等重要内容

实现
Talk is cheap. Show me the code.

任务的定义
任务要包含需要执行的函数、以及函数要传的参数, 因为参数类型、个数不确定, 这里使用可变参数和空接口的形式

type Task struct {
Handler func(v ...interface{})
Params []interface{}
}

任务池的定义
任务池的定义包括了池的容量 capacity、当前运行的 worker(goroutine)数量 runningWorkers、任务队列(channel)taskC、关闭任务池的 channel closeC 以及任务池的状态 state(运行中或已关闭, 用于安全关闭任务池)

type Pool struct {
capacity uint64
runningWorkers uint64
state int64
taskC chan *Task
closeC chan bool
}

任务池的构造函数:

var ErrInvalidPoolCap = errors.New("invalid pool cap")

const (
RUNNING = 1
STOPED = 0
)

func NewPool(capacity uint64) (*Pool, error) {
if capacity <= 0 {
return nil, ErrInvalidPoolCap
}
return &Pool{
capacity: capacity,
state: RUNNING,
// 初始化任务队列, 队列长度为容量
taskC: make(chan *Task, capacity),
closeC: make(chan bool),
}, nil
}

启动 worker

新建 run() 方法作为启动 worker 的方法:

func (p *Pool) run() {
p.runningWorkers++ // 运行中的任务加一

go func() {
defer func() {
p.runningWorkers-- // worker 结束, 运行中的任务减一
}()

for {
select { // 阻塞等待任务、结束信号到来
case task, ok := <-p.taskC: // 从 channel 中消费任务
if !ok { // 如果 channel 被关闭, 结束 worker 运行
return
}
// 执行任务
task.Handler(task.Params...)
case <-p.closeC: // 如果收到关闭信号, 结束 worker 运行
return
}
}
}()
}

上述代码中, runningWorkers 的加减直接使用了自增运算, 但是考虑到启动多个 worker 时, runningWorkers 就会有数据竞争, 所以我们使用 sync.atomic 包来保证 runningWorkers 的自增操作是原子的。

对 runningWorkers 的操作进行封装:

func (p *Pool) incRunning() { // runningWorkers + 1
atomic.AddUint64(&p.runningWorkers, 1)
}

func (p *Pool) decRunning() { // runningWorkers - 1
atomic.AddUint64(&p.runningWorkers, ^uint64(0))
}

func (p *Pool) GetRunningWorkers() uint64 {
return atomic.LoadUint64(&p.runningWorkers)
}

打铁乘热, 对于 capacity 的操作也考虑数据竞争, 封装 GetCap() 方法:


关键字:网络协议 Java Go

GoCN 每日新闻(2020-01-08)

每日新闻tsingson 回复了问题 • 2 人关注 • 1 个回复 • 560 次浏览 • 2020-01-09 10:42 • 来自相关话题

GoCN每日新闻(2020-01-09)

回复

每日新闻keke001 发起了问题 • 1 人关注 • 0 个回复 • 5591 次浏览 • 2020-01-09 09:49 • 来自相关话题

不安分的 Go 强势入侵前端,后端难道要抢前端饭碗了?

文章分享有只黑白猫 发表了文章 • 0 个评论 • 679 次浏览 • 2020-01-08 16:35 • 来自相关话题

点击这里,查看Go写前端的具体操作步骤 ...查看全部

点击这里,查看Go写前端的具体操作步骤

image.png  

Go 语言写前端 Web 应用借助的是 WebAssembly 。

那什么是 WebAssembly 呢?它也叫 wasm ,是由 Google、Microsoft、Mozilla、Apple 等几家大公司合作发起的 WebAssembly 是一种新的字节码格式,主流浏览器都已经支持 WebAssembly。和 JS 需要解释执行不同,WebAssembly 字节码和底层机器码很相似可快速装载运行,因此性能相对于 JS 解释执行大大提升。也就是说 WebAssembly 并不是一门编程语言,而是一份字节码标准,需要用高级编程语言编译出字节码放到 WebAssembly 虚拟机中才能运行 。所以,理论上讲只要能编译成 WebAssembly 字节码的高级语言都可以写 Web 应用程序。

而 Go 的前端框架叫:Vugo 。它是一个 Go 语言开发库,可以很容易地使用 Go 语言编写 Web 用户界面。

image.png  

Vugu: 是一个用于 Go+WebAssembly 的现代 UI 库,受 Vue 和 React 等工具的启发,Vugu 是一个完全用 Go 编写的小型库,可以在现代浏览器中使用 WebAssembly 运行。

官网示例,go 写前端大概是这样的:

image.png  

关键字:Go语言 前端开发

20 个有用的 Go 语言微服务开发框架吐血总结!!!

Golang有只黑白猫 发表了文章 • 0 个评论 • 369 次浏览 • 2020-01-08 15:29 • 来自相关话题

点击这里,查看剩余1 ...查看全部

点击这里,查看剩余10个高效有用的Go 语言微服务开发框架

Beego https://beego.me/

Beego 框架提供了很多标准附加功能,例如全功能路由器和可用于执行 CRUD 操作的对象到数据库映射工具。Bee 是 Beego 爱好者的最爱,它是一个快速而强大的命令行工具,用于构建、更新、打包和部署应用程序。Bee 可以从模板生成源代码,并保持数据库的最新状态。

Buffalo https://gobuffalo.io/en

Buffalo 团队需要能够将 Web 应用程序的所有部分组装在一起的东西,包括应用程序本身的一些设计。他们把能够安装在一起的很多部件叫作“生态系统”。如果你想要路由——很少有人不需要——Buffalo 就包含了 Gorilla/Mux。如果你需要模板,Buffalo 倾向于使用 Plush,而不是使用内置的 Go 语言模板机制。数据库连接模块集合 Pop 可以帮你将数据库信息转换为 Go 对象。你还可以找到连接数据库、处理 cookie 以及完成其他任务的标准方法。

Cobra https://github.com/spf13/cobra

有时候,你只需要一个命令行界面。Cobra 提供了 CLI 的所有标准功能,因此你不必浪费时间实现代码来查找 -h 或 -help 标志。如果你的微服务需要对具有大量标志和其他功能的命令行调用做出响应,那么可以考虑集成 Cobra。

Docker你当然可以在办公室服务器小黑屋里的裸机上运行微服务,但越来越多的人将他们的代码捆绑在 Docker 容器中,并将容器发到云端。小型的包更容易处理大量不同的代码块,当你对微服务架构的愿景要求你创建很多小的独立代码块时,这将是一项有价值的服务。

值得一提的是,Docker 是用 Go 语言开发的,尽管在部署 Docker 容器时你可能永远不会想到这一点。Docker 社区版是开源的,所以如果有必要,你可以参与其中,但很可能你只是将 Docker 作为部署微服务的工具。Go 语言爱好者之所以想要记住 Docker 是用 Go 语言开发的这一事实,是因为无处不在的 Docker 有力地证明了他们对这门语言的支持。

Echo https://echo.labstack.com/

Echo 是一个极简框架,但它提供了很多最重要的组件。路由器可以将 URL 拆解,然后将拆解的各个部份转换为参数,因此你无需自行解析它们。然后,你可以混合使用身份验证、表单解析、压缩和合理性限制。你可以专注于从函数中返回正确的信息。

Errors https://github.com/juju/errors

有时候,API 的用户会传递错误的参数。你可以自己处理这些参数,也可以把它们创给 Errors,这是一个可以自动执行大部分跟踪的库,方便进行调试。当发生错误时,Errors 会使用注释来详细说明出错的地方和位置。

Gin https://github.com/gin-gonic/gin

Gin 是 Martini( https://github.com/go-martini... )的下一代框架。可以说,Gin 抛弃了那些额外的东西,专注于提供最有用的部分。花费大量时间构建 Node.js 微服务的开发人员会感到宾至如归。你可以实例化一个对象,然后附加函数来处理特定的调用,这样就可以创建一个微服务。Gin 负责处理路由,而你的函数处理业务逻辑。如果不去考虑标点符号,它的代码甚至看起来有点像 Node.js 代码。

Ginkgo https://onsi.github.io/ginkgo/

测试可能是微服务开发当中最具挑战性的事情。Ginkgo 通过行为驱动测试扩展了标准 Go 发行版的内置测试机制。Ginkgo 提供了一种高级机制,用于指定函数或服务应该产生哪些结果。结果通常使用 Ginkgo 提供的 Gomega 匹配器( http://onsi.github.io/gomega/ )进行评估,但如果你愿意,也可以使用不同的匹配器库。

Ginkgo 是一个全面的框架,提供了各种选项,用于设置测试数据、运行测试以及在事后释放测试数据。你只需要描述结果,然后让 Ginkgo 处理其他的事情。

Goa https://github.com/goadesign/goa

如果你是一个曾经使用 Ruby 和 Praxis 框架的开发人员,或者是一个欣赏设计语言的强大力量人,那么你会在 Goa 中找到很多你喜欢的东西。你本身不需要编写 Go 代码。你使用 Goa DSL 为 API 编写设计规范,然后 Goa 将其转换为可执行的 Go 代码。DSL 针对微服务 API 进行了优化,并强制你的设计遵循标准的架构。

Gorilla http://www.gorillatoolkit.org/

Gorilla 项目提供了一系列你需要的模块。Gorilla 的 Mux( http://www.gorillatoolkit.org... )路由器被很多其他框架使用,因为它太好用了。很多用户之所以使用 Gorilla,是因为 websocket( http://www.gorillatoolkit.org... )。

Gotify https://github.com/gotify/server

同步一组微服务所面临的一个挑战是建立有效的消息传递节点。Gotify 是一个简单的服务器,用于发送和接收消息,将你的微服务集合与持续存储的消息组合在一起。最有用的部分可能是它的 Web 接口,可帮助开发者应对最令人头疼的调试问题。

关键字:开发 框架 前端开发 Go API 微服务

BookStack v2.4 发布,类似 GitBook 的文档管理系统

回复

开源程序皇虫 发起了问题 • 1 人关注 • 0 个回复 • 196 次浏览 • 2020-01-08 12:11 • 来自相关话题

如何高效获取一个时间差

有问必答lrita 回复了问题 • 1 人关注 • 1 个回复 • 156 次浏览 • 2020-01-08 11:01 • 来自相关话题

GoCN 每日新闻(2020-01-07)

回复

每日新闻傅小黑 发起了问题 • 2 人关注 • 0 个回复 • 522 次浏览 • 2020-01-07 17:02 • 来自相关话题

Go 会接替 Java,成为下一个企业级编程语言吗?

文章分享有只黑白猫 发表了文章 • 2 个评论 • 568 次浏览 • 2020-01-07 14:33 • 来自相关话题

点击这里,查看 ...查看全部

点击这里,查看GO的优势、劣势等剩余重要内容

Go 的优势在于能够将简单的和经过验证的想法结合起来,同时避免了其他语言中出现的许多问题。本文概述了 Go 背后的一些设计原则和工程智慧,作者认为,Go 语言具备的所有这些优点,将共同推动其成为接替 Java 并主导下一代大型软件开发平台的最有力的编程语言候选。很多优秀的编程语言只是在个别领域比较强大,如果将所有因素都纳入考虑,没有其他语言能够像 Go 语言一样“全面开花”,在大型软件工程方面,尤为如此。

基于现实经验 Go 是由经验丰富的软件行业老手一手创建的,长期以来,他们对现有语言的各种缺点有过切身体会的痛苦经历。几十年前,Rob Pike 和 Ken Thompson 在 Unix、C 和 Unicode 的发明中起到了重要作用。Robert Griensemer 在为 JavaScript 和 Java 开发 V8 和 HotSpot 虚拟机之后,在编译器和垃圾收集方面拥有数十年的经验。有太多次,他们不得不等待 Google 规模的 C++/Java 代码库进行编译。于是,他们开始着手创建新的编程语言,将他们半个世纪以来的编写代码所学到的一切经验包含进去。

专注于大型工程 小型工程项目几乎可以用任何编程语言来成功构建。当成千上万的开发人员在数十年的持续时间压力下,在包含数千万行代码的大型代码库上进行协作时,就会发生真正令人痛苦的问题。这样会导致一些问题,如下:

  • 较长的编译时间导致中断开发。
  • 代码库由几个人 / 团队 / 部门 / 公司所拥有,混合了不同的编程风格。
  • 公司雇佣了数千名工程师、架构师、测试人员、运营专家、审计员、实习生等,他们需要了解代码库,但也具备广泛的编码经验。
  • 依赖于许多外部库或运行时,其中一些不再以原始形式存在。
  • 在代码库的生命周期中,每行代码平均被重写 10 次,被弄得千疮百痍,而且还会发生技术偏差。
  • 文档不完整。
    Go 注重减轻这些大型工程的难题,有时会以使小型工程变得更麻烦为代价,例如,代码中到处都需要几行额外的代码行。

注重可维护性 Go 强调尽可能多地将工作转给自动化的代码维护工具中。Go 工具链提供了最常用的功能,如格式化代码和导入、查找符号的定义和用法、简单的重构以及代码异味的识别。由于标准化的代码格式和单一的惯用方式,机器生成的代码更改看起来非常接近 Go 中人为生成的更改并使用类似的模式,从而允许人机之间更加无缝地协作。

保持简单明了

初级程序员为简单的问题创建简单的解决方案。高级程序员为复杂的问题创建复杂的解决方案。伟大的程序员找到复杂问题的简单解决方案。
——Charles Connell

让很多人惊讶的一点是,Go 居然不包含他们喜欢的其他语言的概念。Go 确实是一种非常小巧而简单的语言,只包含正交和经过验证的概念的最小选择。这鼓励开发人员用最少的认知开销来编写尽可能简单的代码,以便许多其他人可以理解并使用它。

使事情清晰明了 良好的代码总是显而易见的,避免了那些小聪明、难以理解的语言特性、诡异的控制流和兜圈子。

许多语言都致力提高编写代码的效率。然而,在其生命周期中,人们阅读代码的时间却远远超过最初编写代码所需的时间(100 倍)。例如,审查、理解、调试、更改、重构或重用代码。在查看代码时,往往只能看到并理解其中的一小部分,通常不会有完整的代码库概述。为了解释这一点,Go 将所有内容都明确出来。

错误处理就是一个例子。让异常在各个点中断代码并在调用链上冒泡会更容易。Go 需要手动处理和返回每个错误。这使得它可以准确地显示代码可以被中断的位置以及如何处理或包装错误。总的来说,这使得错误处理编写起来更加繁琐,但是也更容易理解。

简单易学 Go 是如此的小巧而简单,以至于人们可以在短短几天内就能研究通整个语言及其基本概念。根据我们的经验,培训用不了一个星期(相比于掌握其他语言需要几个月),初学者就能够理解 Go 专家编写的代码,并为之做出贡献。为了方便吸引更多的用户,Go 网站提供了所有必要的教程和深入研究的文章。这些教程在浏览器中运行,允许人们在将 Go 安装到本地计算机上之前就能够学习和使用 Go。

解决之道 Go 强调的是团队之间的合作,而不是个人的自我表达。

在 Go(和 Python)中,所有的语言特性都是相互正交和互补的,通常有一种方法可以做一些事情。如果你想让 10 个 Python 或 Go 程序员来解决同一个问题,你将会得到 10 个相对类似的解决方案。不同的程序员在彼此的代码库中感觉更自在。在查看其他人的代码时,国骂会更少,而且人们的工作可以更好地融合在一起,从而形成了一致的整体,人人都为之感到自豪,并乐于工作。这还避免了大型工程的问题,如:

关键字:GO  JAVA  编程语言

Go 开发关键技术指南 | 带着服务器编程金刚经走进 2020 年(内含超全知识大图)

技术讨论有只黑白猫 发表了文章 • 0 个评论 • 268 次浏览 • 2020-01-07 14:24 • 来自相关话题

点击这里,查看 ...查看全部

点击这里,查看Orthogonal,Modules,GOPATH & Vendor等剩余重要内容

Go 开发指南

1.png

Interfaces

Go 在类型和接口上的思考是:

  • Go 类型系统并不是一般意义的 OO,并不支持虚函数;
  • Go 的接口是隐含实现,更灵活,更便于适配和替换;
  • Go 支持的是组合、小接口、组合+小接口;
  • 接口设计应该考虑正交性,组合更利于正交性。

Type System

Go 的类型系统是比较容易和 C++/Java 混淆的,特别是习惯于类体系和虚函数的思路后,很容易想在 Go 走这个路子,可惜是走不通的。而 interface 因为太过于简单,而且和 C++/Java 中的概念差异不是特别明显,所以本章节专门分析 Go 的类型系统。

先看一个典型的问题 Is it possible to call overridden method from parent struct in golang? 代码如下所示:

package main

import (
"fmt"
)

type A struct {
}

func (a *A) Foo() {
fmt.Println("A.Foo()")
}

func (a *A) Bar() {
a.Foo()
}

type B struct {
A
}

func (b *B) Foo() {
fmt.Println("B.Foo()")
}

func main() {
b := B{A: A{}}
b.Bar()
}

本质上它是一个模板方法模式 (TemplateMethodPattern),A 的 Bar 调用了虚函数 Foo,期待子类重写虚函数 Foo,这是典型的 C++/Java 解决问题的思路。

我们借用模板方法模式 (TemplateMethodPattern) 中的例子,考虑实现一个跨平台编译器,提供给用户使用的函数是 crossCompile,而这个函数调用了两个模板方法 collectSource 和 compileToTarget

public abstract class CrossCompiler {
public final void crossCompile() {
collectSource();
compileToTarget();
}
//Template methods
protected abstract void collectSource();
protected abstract void compileToTarget();
}

C 版,不用 OOAD 思维参考 C: CrossCompiler use StateMachine,代码如下所示:

// g++ compiler.cpp -o compiler && ./compiler
#include <stdio.h>

void beforeCompile() {
printf("Before compile\n");
}

void afterCompile() {
printf("After compile\n");
}

void collectSource(bool isIPhone) {
if (isIPhone) {
printf("IPhone: Collect source\n");
} else {
printf("Android: Collect source\n");
}
}

void compileToTarget(bool isIPhone) {
if (isIPhone) {
printf("IPhone: Compile to target\n");
} else {
printf("Android: Compile to target\n");
}
}

void IDEBuild(bool isIPhone) {
beforeCompile();

collectSource(isIPhone);
compileToTarget(isIPhone);

afterCompile();
}

int main(int argc, char** argv) {
IDEBuild(true);
//IDEBuild(false);
return 0;
}

C 版本使用 OOAD 思维,可以参考 C: CrossCompiler,代码如下所示:

// g++ compiler.cpp -o compiler && ./compiler
#include <stdio.h>

class CrossCompiler {
public:
void crossCompile() {
beforeCompile();

collectSource();
compileToTarget();

afterCompile();
}
private:
void beforeCompile() {
printf("Before compile\n");
}
void afterCompile() {
printf("After compile\n");
}
// Template methods.
public:
virtual void collectSource() = 0;
virtual void compileToTarget() = 0;
};

class IPhoneCompiler : public CrossCompiler {
public:
void collectSource() {
printf("IPhone: Collect source\n");
}
void compileToTarget() {
printf("IPhone: Compile to target\n");
}
};

class AndroidCompiler : public CrossCompiler {
public:
void collectSource() {
printf("Android: Collect source\n");
}
void compileToTarget() {
printf("Android: Compile to target\n");
}
};

void IDEBuild(CrossCompiler* compiler) {
compiler->crossCompile();
}

int main(int argc, char** argv) {
IDEBuild(new IPhoneCompiler());
//IDEBuild(new AndroidCompiler());
return 0;
}

我们可以针对不同的平台实现这个编译器,比如 Android 和 iPhone:

public class IPhoneCompiler extends CrossCompiler {
protected void collectSource() {
//anything specific to this class
}
protected void compileToTarget() {
//iphone specific compilation
}
}

public class AndroidCompiler extends CrossCompiler {
protected void collectSource() {
//anything specific to this class
}
protected void compileToTarget() {
//android specific compilation
}
}

在 C++/Java 中能够完美的工作,但是在 Go 中,使用结构体嵌套只能这么实现,让 IPhoneCompiler 和 AndroidCompiler 内嵌 CrossCompiler,参考 Go: TemplateMethod,代码如下所示:

package main

import (
"fmt"
)

type CrossCompiler struct {
}

func (v CrossCompiler) crossCompile() {
v.collectSource()
v.compileToTarget()
}

func (v CrossCompiler) collectSource() {
fmt.Println("CrossCompiler.collectSource")
}

func (v CrossCompiler) compileToTarget() {
fmt.Println("CrossCompiler.compileToTarget")
}

type IPhoneCompiler struct {
CrossCompiler
}

func (v IPhoneCompiler) collectSource() {
fmt.Println("IPhoneCompiler.collectSource")
}

func (v IPhoneCompiler) compileToTarget() {
fmt.Println("IPhoneCompiler.compileToTarget")
}

type AndroidCompiler struct {
CrossCompiler
}

func (v AndroidCompiler) collectSource() {
fmt.Println("AndroidCompiler.collectSource")
}

func (v AndroidCompiler) compileToTarget() {
fmt.Println("AndroidCompiler.compileToTarget")
}

func main() {
iPhone := IPhoneCompiler{}
iPhone.crossCompile()
}

执行结果却让人手足无措:

# Expect
IPhoneCompiler.collectSource
IPhoneCompiler.compileToTarget

# Output
CrossCompiler.collectSource
CrossCompiler.compileToTarget

Go 并没有支持类继承体系和多态,Go 是面向对象却不是一般所理解的那种面向对象,用老子的话说“道可道,非常道”。

实际上在 OOAD 中,除了类继承之外,还有另外一个解决问题的思路就是组合 Composition,面向对象设计原则中有个很重要的就是 The Composite Reuse Principle (CRP)Favor delegation over inheritance as a reuse mechanism,重用机制应该优先使用组合(代理)而不是类继承。类继承会丧失灵活性,而且访问的范围比组合要大;组合有很高的灵活性,另外组合使用另外对象的接口,所以能获得最小的信息。

C++ 如何使用组合代替继承实现模板方法?可以考虑让 CrossCompiler 使用其他的类提供的服务,或者说使用接口,比如 CrossCompiler 依赖于 ICompiler

public interface ICompiler {
//Template methods
protected abstract void collectSource();
protected abstract void compileToTarget();
}

public abstract class CrossCompiler {
public ICompiler compiler;
public final void crossCompile() {
compiler.collectSource();
compiler.compileToTarget();
}
}

C 版本可以参考 C: CrossCompiler use Composition,代码如下所示:

// g++ compiler.cpp -o compiler && ./compiler
#include <stdio.h>

class ICompiler {
// Template methods.
public:
virtual void collectSource() = 0;
virtual void compileToTarget() = 0;
};

class CrossCompiler {
public:
CrossCompiler(ICompiler* compiler) : c(compiler) {
}
void crossCompile() {
beforeCompile();

c->collectSource();
c->compileToTarget();

afterCompile();
}
private:
void beforeCompile() {
printf("Before compile\n");
}
void afterCompile() {
printf("After compile\n");
}
ICompiler* c;
};

class IPhoneCompiler : public ICompiler {
public:
void collectSource() {
printf("IPhone: Collect source\n");
}
void compileToTarget() {
printf("IPhone: Compile to target\n");
}
};

class AndroidCompiler : public ICompiler {
public:
void collectSource() {
printf("Android: Collect source\n");
}
void compileToTarget() {
printf("Android: Compile to target\n");
}
};

void IDEBuild(CrossCompiler* compiler) {
compiler->crossCompile();
}

int main(int argc, char** argv) {
IDEBuild(new CrossCompiler(new IPhoneCompiler()));
//IDEBuild(new CrossCompiler(new AndroidCompiler()));
return 0;
}

我们可以针对不同的平台实现这个 ICompiler,比如 Android 和 iPhone。这样从继承的类体系,变成了更灵活的接口的组合,以及对象直接服务的调用:

public class IPhoneCompiler implements ICompiler {
protected void collectSource() {
//anything specific to this class
}
protected void compileToTarget() {
//iphone specific compilation
}
}

public class AndroidCompiler implements ICompiler {
protected void collectSource() {
//anything specific to this class
}
protected void compileToTarget() {
//android specific compilation
}
}

在 Go 中,推荐用组合和接口,小的接口,大的对象。这样有利于只获得自己应该获取的信息,或者不会获得太多自己不需要的信息和函数,参考 Clients should not be forced to depend on methods they do not use. –Robert C. Martin,以及 The bigger the interface, the weaker the abstraction, Rob Pike。关于面向对象的原则在 Go 中的体现,参考 Go: SOLID 或中文版 Go: SOLID

先看如何使用 Go 的思路实现前面的例子,跨平台编译器,Go Composition: Compiler,代码如下所示:

package main

import (
"fmt"
)

type SourceCollector interface {
collectSource()
}

type TargetCompiler interface {
compileToTarget()
}

type CrossCompiler struct {
collector SourceCollector
compiler TargetCompiler
}

func (v CrossCompiler) crossCompile() {
v.collector.collectSource()
v.compiler.compileToTarget()
}

type IPhoneCompiler struct {
}

func (v IPhoneCompiler) collectSource() {
fmt.Println("IPhoneCompiler.collectSource")
}

func (v IPhoneCompiler) compileToTarget() {
fmt.Println("IPhoneCompiler.compileToTarget")
}

type AndroidCompiler struct {
}

func (v AndroidCompiler) collectSource() {
fmt.Println("AndroidCompiler.collectSource")
}

func (v AndroidCompiler) compileToTarget() {
fmt.Println("AndroidCompiler.compileToTarget")
}

func main() {
iPhone := IPhoneCompiler{}
compiler := CrossCompiler{iPhone, iPhone}
compiler.crossCompile()
}

这个方案中,将两个模板方法定义成了两个接口,CrossCompiler 使用了这两个接口,因为本质上 C++/Java 将它的函数定义为抽象函数,意思也是不知道这个函数如何实现。而 IPhoneCompiler 和 AndroidCompiler 并没有继承关系,而它们两个实现了这两个接口,供 CrossCompiler 使用;也就是它们之间的关系,从之前的强制绑定,变成了组合。

type SourceCollector interface {
collectSource()
}

type TargetCompiler interface {
compileToTarget()
}

type CrossCompiler struct {
collector SourceCollector
compiler TargetCompiler
}

func (v CrossCompiler) crossCompile() {
v.collector.collectSource()
v.compiler.compileToTarget()
}

Rob Pike 在 Go Language: Small and implicit 中描述 Go 的类型和接口,第 29 页说:

  • Objects implicitly satisfy interfaces. A type satisfies an interface simply by implementing its methods. There is no "implements" declaration; interfaces are satisfied implicitly. 这种隐式的实现接口,实际中还是很灵活的,我们在 Refector 时可以将对象改成接口,缩小所依赖的接口时,能够不改变其他地方的代码。比如如果一个函数 foo(f *os.File),最初依赖于 os.File,但实际上可能只是依赖于 io.Reader 就可以方便做 UTest,那么可以直接修改成 foo(r io.Reader) 所有地方都不用修改,特别是这个接口是新增的自定义接口时就更明显;
  • In Go, interfaces are usually small: one or two or even zero methods. 在 Go 中接口都比较小,非常小,只有一两个函数;但是对象却会比较大,会使用很多的接口。这种方式能够以最灵活的方式重用代码,而且保持接口的有效性和最小化,也就是接口隔离。

隐式实现接口有个很好的作用,就是两个类似的模块实现同样的服务时,可以无缝的提供服务,甚至可以同时提供服务。比如改进现有模块时,比如两个不同的算法。更厉害的时,两个模块创建的私有接口,如果它们签名一样,也是可以互通的,其实签名一样就是一样的接口,无所谓是不是私有的了。这个非常强大,可以允许不同的模块在不同的时刻升级,这对于提供服务的服务器太重要了。

比较被严重误认为是继承的,莫过于是 Go 的内嵌 Embeding,因为 Embeding 本质上还是组合不是继承,参考 Embeding is still composition

Embeding 在 UTest 的 Mocking 中可以显著减少需要 Mock 的函数,比如 Mocking net.Conn,如果只需要 mock Read 和 Write 两个函数,就可以通过内嵌 net.Conn 来实现,这样 loopBack 也实现了整个 net.Conn 接口,不必每个接口全部写一遍:

type loopBack struct {
net.Conn
buf bytes.Buffer
}

func (c *loopBack) Read(b []byte) (int, error) {
return c.buf.Read(b)
}

func (c *loopBack) Write(b []byte) (int, error) {
return c.buf.Write(b)
}

Embeding 只是将内嵌的数据和函数自动全部代理了一遍而已,本质上还是使用这个内嵌对象的服务。Outer 内嵌了Inner,和 Outer 继承 Inner 的区别在于:内嵌 Inner 是不知道自己被内嵌,调用 Inner 的函数,并不会对 Outer 有任何影响,Outer 内嵌 Inner 只是自动将 Inner 的数据和方法代理了一遍,但是本质上 Inner 的东西还不是 Outer 的东西;对于继承,调用 Inner 的函数有可能会改变 Outer 的数据,因为 Outer 继承 Inner,那么 Outer 就是 Inner,二者的依赖是更紧密的。

如果很难理解为何 Embeding 不是继承,本质上是没有区分继承和组合的区别,可以参考 Composition not inheritance,Go 选择组合不选择继承是深思熟虑的决定,面向对象的继承、虚函数、多态和类树被过度使用了。类继承树需要前期就设计好,而往往系统在演化时发现类继承树需要变更,我们无法在前期就精确设计出完美的类继承树;Go 的接口和组合,在接口变更时,只需要变更最直接的调用层,而没有类子树需要变更。

The designs are nothing like hierarchical, subtype-inherited methods. They are looser (even ad hoc), organic, decoupled, independent, and therefore scalable.

组合比继承有个很关键的优势是正交性 orthogonal,详细参考正交性

关键字:XML  存储  Shell  Go  开发工具  Android开发  iOS开发  数据格式  git  C++

袋鼠存储 v1.4.1 来了:docker镜像,linux命令行安装,客户端...

文章分享fabsnail 发表了文章 • 1 个评论 • 486 次浏览 • 2020-01-07 12:20 • 来自相关话题

袋鼠存储是一款跨平台,跨网络管理数据,就近为用户提供服务的分布式服务。可通过官网文档 roostore.com/docs# 详细了解袋鼠存储自从发布以来,已 ...查看全部

袋鼠存储是一款跨平台,跨网络管理数据,就近为用户提供服务的分布式服务。可通过官网文档 roostore.com/docs# 详细了解

袋鼠存储自从发布以来,已得到大量用户的下载与使用反馈,在此感谢大家的支持与认可,我们继续努力改进和完善

现在 v1.4.1 版本来了,该版本提供docker镜像,linux命令行部署方式,并提供客户端等

kstore

1. 提供 docker 镜像
2. 提供 linux 一键安装 kstore 服务

客户端首发

1. 支持 windows,linux
2. 支持用户设置需要连接的 kstore 服务,默认使用本地服务
3. 提供下载管理列表:支持暂停,恢复和取消下载任务
4. 提供下载完成项管理列表:支持在系统资源管理器中打开和删除下载项
5. 支持直接下载目录
6. 支持设置默认下载存放目录

其他更新

1. 提供树莓派 4B 从开箱到部署运行袋鼠存储服务的实践教程
2. 重写边同步边下载逻辑,解决使用一些第三方工具下载得到的文件无效问题
3. 调整后台网络管理中不可显示子网管理员密码
4. 后台网络管理提供修改子网密码的入口
5. 后台管理页面样式微调,更轻快的视觉体验
6. 后台设备管理中调整添加同步帐号布局,显示更醒目
7. 用户头像菜单提供后台管理入口
8. 快速访问入口项提供图标
9. 登录界面显示正在登录动画
10. 刷新功能优化,本地不存在元数据时尝试等待网络同步返回并显示等待动画
11. 元数据同步逻辑优化,避免上传大批量小文件时出现频繁发起同步
12. 数据中心节点周期性主动发起同步
13. 上传对话框显示统计已上传的文件数,用户可自行重置该统计项
14. 调整上传过程中出错时的状态显示,点击可查看详细错误信息
15. 优化上传逻辑,解决当大批量上传文件时界面卡死问题
16. 修复通过拖放上传文件夹时每一层最多仅扫描 100 项问题
17. 修复上传过程中后端出现的:Access is denied 问题
18. 修复上传过程中若切换浏览目录导致上传目标位置改变问题
19. 修复上传时可能报错:无法找到文件问题
20. 修复上传时可能报错:invalid argument
21. 修复取消上传选中项时误取消所有上传任务问题
22. 修复通过拖放上传一次后,切换浏览目录再上传时,上传存放目录错误问题
23. 修复通过拖放上传时,若选择了多个文件仅上传选中的第一个文件问题
24. 修复新创建的子网,全新节点加入失败问题
25. 修复加载页在加载完毕后可能出现的不跳转问题

感兴趣的朋友,欢迎通过官网 roostore.com 下载使用

golang技术文章精选(2019)

文章分享hanyajun 发表了文章 • 0 个评论 • 552 次浏览 • 2020-01-07 10:06 • 来自相关话题

https://hanyajun.com/golang/go_article_2019/ ...查看全部

GoCN每日新闻(2020-01-06)

回复

每日新闻kevin 发起了问题 • 1 人关注 • 0 个回复 • 6571 次浏览 • 2020-01-06 16:12 • 来自相关话题

Go语言编程有哪些利与弊?编程时如何判断是否应该用Go?

文章分享有只黑白猫 发表了文章 • 2 个评论 • 520 次浏览 • 2020-01-06 13:38 • 来自相关话题

点击这里,查看 ...查看全部

点击这里,查看使用Go语言编程的弊端及何时使用Go语言编程等重要内容


我们喜欢 Go 语言的地方

近年来,Go 语言的使用量呈爆炸式增长。似乎每个初创公司都将它用于后端系统。开发人员认为它如此广受欢迎,背后的原因有很多。

Go 语言速度非常快 Go 语言是一门非常快速的编程语言。因为 Go 语言是编译成机器码的,因此,它的表现自然会优于那些解释性或具有虚拟运行时的编程语言。Go 程序的编译速度也非常快,并且生成的二进制文件非常小。我们的 API 在短短几秒钟内就编译完毕,生成的可执行文件区区只有 11.5MB 这么小。

易于掌握 与其他语言相比,Go 语言的语法很简单,很容易掌握。你完全可以把 Go 语言的大部分语法记在脑子里,这意味着你并不需要花很多时间来查找东西。Go 语言也非常干净易读。非 Go 语言的程序员,尤其是那些习惯于 C 风格语法的程序员,就可以阅读 Go 程序代码,并且能够理解发生什么事。

静态类型定义语言 Go 语言是一种强大的静态类型定义语言。有基本类型,如 int、byte 和 string。也有结构类型。与任何强类型语言一样,类型系统允许编译器帮助捕获整个类的错误。Go 语言还具有内置的列表和映射类型,而且它们也易于使用。

接口类型 Go 语言有接口类型,任何结构都可以简单地通过实现接口的方法来满足接口。这允许你解耦代码中的依赖项。然后,你可以在测试中模拟你的依赖项。通过使用接口,你可以编写更加模块化的可测试代码。Go 语言还具有头等函数,这使得开发人员以更实用的方式编写代码成为可能。

标准库 Go 语言有一个相当不错的标准库。它提供了方便的内置函数,用于处理基本类型。有些包可以让你轻松构建一个 Web 服务器、处理 I/O、使用加密技术以及操作原始字节。标准库提供的 JSON 序列化和反序列化非常简单。通过使用“tags”,你可以在 struct 字段旁边指定 JSON 字段名。

测试支持 测试支持内置在标准库中,不需要额外的依赖。如果你有个名为 thing.go 的文件,请在另一个名为 thing_test.go 的文件中编写测试,并运行“go test”。Go 就将快速执行这些测试。

静态分析工具 Go 语言的静态分析工具众多且强大。一种特别的工具是 gofmt,它根据 Go 的建议风格对代码进行格式化。这可以规范项目的许多意见,让团队奖经理集中在代码所做的工作上。我们对每个构建运行 gofmt、golint 和 vet,如果发现任何警告的话,则构建将会失败。

垃圾收集 在设计 Go 语言时,有意将内存管理设计得比 C 和 C++ 更容易。动态分配的对象是垃圾收集。Go 语言使指针的使用更加安全,因为它不允许指针运算。还提供了使用值类型的选项。

更容易的并发模型 虽然并发编程从来就不是一件易事,但 Go 语言在并发编程要比其他语言更容易。创建一个名为“goroutine”的轻量级线程,并通过“channel”与它进行通信几乎是非常简单的事情,至于更为复杂的模型,也是有可能能够实现的。

我们不喜欢 Go 语言的地方

正如我们前面讨论过的,Go 语言确实是一门优秀的语言。它有一个干净的语法,执行速度也很快速。它还有很多优点。但是,编程语言的全部并不仅仅是指它的语法。下面是我们遇到的一些问题。


关键字:GO语言   编程语言 

2020年Go语言那些不得不看的最新面试题

文章分享有只黑白猫 发表了文章 • 0 个评论 • 595 次浏览 • 2020-01-06 13:31 • 来自相关话题

点击这里,查看 ...查看全部

点击这里,查看剩余5道2020年最新面试题及其解析

1、编译执行下面代码会出现什么?

package main
var(
size :=1024
max_size = size*2
)
func main() {
println(size,max_size)
}

解析 考点:变量简短模式 变量简短模式限制: - 定义变量同时显式初始化 - 不能提供数据类型 - 只能在函数内部使用

结果:

syntax error: unexpected :=
2、下面函数有什么问题?
package main
const cl = 100

var bl = 123

func main() {
println(&bl,bl)
println(&cl,cl)
}

解析 考点:常量 常量不同于变量的在运行期分配内存,常量通常会被编译器在预处理阶段直接展开,作为指令数据使用,

cannot take the address of cl

3、编译执行下面代码会出现什么?

package main

func main() {

for i:=0;i<10 ;i++ {
loop:
println(i)
}
goto loop
}

解析 考点:goto goto不能跳转到其他函数或者内层代码

goto loop jumps into block starting at

4、编译执行下面代码会出现什么?

package main
import "fmt"

func main() {
type MyInt1 int
type MyInt2 = int
var i int =9
var i1 MyInt1 = i
var i2 MyInt2 = i
fmt.Println(i1,i2)
}

解析 考点:Go 1.9 新特性 Type Alias 基于一个类型创建一个新类型,称之为defintion;基于一个类型创建一个别名,称之为alias。 MyInt1为称之为defintion,虽然底层类型为int类型,但是不能直接赋值,需要强转; MyInt2称之为alias,可以直接赋值。

结果:

cannot use i (type int) as type MyInt1 in assignment

5、编译执行下面代码会出现什么?

package main
import "fmt"

type User struct {
}
type MyUser1 User
type MyUser2 = User
func (i MyUser1) m1(){
fmt.Println("MyUser1.m1")
}
func (i User) m2(){
fmt.Println("User.m2")
}

func main() {
var i1 MyUser1
var i2 MyUser2
i1.m1()
i2.m2()
}

解析 考点:Go 1.9 新特性 Type Alias 因为MyUser2完全等价于User,所以具有其所有的方法,并且其中一个新增了方法,另外一个也会有。 但是

i1.m2()

是不能执行的,因为MyUser1没有定义该方法。 结果:

MyUser1.m1
User.m2

关键字:Go语言  面试