利用Go中的Layer-cake设计

我喜欢go的多线程支持已经不是什么秘密了。这是一个新线程的单线程:

 FUNC(){}() 

并且与该线程交谈的三个班轮(最小):

信使 := 使 INT 3  FUNC(){      对于 信息 := < -信使 {         FMT的println< -信使      } }() 信使 < -  

线程和通道在天堂是一个匹配,让你建立一个反应导向的程序,分而治之的递归,以及我最终将在dev.to上讨论的许多更多的事情。

但是让我们来看看第一个。这让我建立了自己的个人小设计理念,我发现自己已经深入到每个项目中。它可能不是描述它的最准确的方式,但这个想法在名称中得到了体现。一切都是一层,而且它们都是结合在一起的。

在我看来,这种方法鼓励我以鼓励可扩展,模块化和可理解的风格的方式构建我的应用程序,服务器等。因此,可以加入新的服务,接口等,而无需集成它们的大量麻烦。话虽这么说,我没有完全按照它调整,但是,嘿,这就是我写这篇文章的原因。我希望将来能够保持这种结构,而不是过去几次我实施它的混乱方式。

此外,由于Go不允许递归导入,这鼓励创建我想称为“基板”的东西。这是您的应用程序中的通信核心。您的基材定义了可以(内部)命令的所有内容,命令格式,参数等。

最初,你需要做几件事情:

  • 您的应用可以对哪些接口做出反应?
  • 您的应用程序提供哪些服务?

通常,我的界面出来了

  • CLI
  • RPC(我的剪贴板管理器中没有使用)
  • 基于TCP的协议

服务会有所不同,但对于我的剪贴板管理器来说却是如此

  • 监控剪贴板
  • (很快)客户端加密货币管道

所以让我们列举一下。在Go中有几个主要的枚举结构(我在第一个版本中默认最多,在使用AzCopy之后我明白为什么这可能有问题):

  • type-const枚举(如果包中只有一个枚举,我通常会使用它)
类型 地点 UINT8  常量      CLI 地点 = 丝毫     TCP     监控   //你为什么要用这个? / *  - 在调试日志中输出为const名称 - 易于参考* /  //你为什么不想用这个? / *  - 一个软件包中的多个枚举令人困惑 - 可能会意外地与函数名称冲突等。* / 

-type-func枚举(AzCopy使用的)

类型 地点 UINT8 常量 ELocation = 地点0  FUNC  地点 命令行() 地点 { 返回 地点1 } FUNC  地点 的TcpClient() 地点 { 返回 地点2 } FUNC  地点 监控() 地点 { 返回 地点3 }  FUNC  地点 ()  { 返回 ... }  //你为什么要用这个? / *  - 由变量类型而不是点评隔离 - 不能与其他函数,枚举等冲突。* /  //你为什么不想用这个? / *  - 需要一个Location变量来实际获取枚举值 - 没有简单的方法来打印它的字符串用于调试目的 - 由函数枚举,而不是由任何具体值* / 

– 双映射枚举

VAR ELocation = 地图UINT8{...} VAR LocationToString = 地图UINT8{...}  //你为什么要用这个? / *  - 由变量类型而不是点评隔离 - 不能与其他函数,枚举等冲突。* /  //你为什么不想用这个? / *  - 不可变 - 需要更多内存 - 不要强制使用单一类型 - 没有简单的方法来打印它的字符串用于调试目的* / 

我不是缺乏一个人的忠实粉丝 枚举 go中的结构,但是,你用你拥有的东西做。所有这些都有各自的问题。所有这些都可以混合和匹配,以创造“完美” 枚举 实现。

此时,是时候构建基板了。您的命令结构几乎总是最重要的。您可以随时添加它,但随着时间的推移,它会越来越难以删除。为了简单起见,我通常包括一个命令ID,一个字符串列表作为参数,以及原点位置。

类型 命令 结构 { //每个图层可以有多个命令结构     CommandID  UINT8 //可以是枚举     参数 ()     起源     地点 //这可以是字符串或枚举。你的选择。 } 

所以你已经掌握了命令结构,现在是时候设置你的频道了。为每个图层设置一个通道。

VAR      CLIChannel = 使 命令 50 //合理的缓冲区大小。     ClientChannel     MonitorChannel  

现在,您的基板准备就绪。我通常把它放在“普通”包(或一个字面上称为“基板”)的盘点下。是时候创建你的图层了。为每个图层创建一个包,并为它们创建一个基线goroutine。也许他们将来会产生自己的。我们为我提供一个示例TCP客户端层。

 客户  类型 extMessage 结构 {     CommandID  UINT8     参数 () }  FUNC 的TcpClient() {     ExternalMessages := 使 extMessage 50       FUNC(){         //这里的TCP侦听器,管道到ExternalMessages     }()      对于 {         选择 {         案件 extMsg := < - ExternalMessages              //处理外部消息         案件 intMsg := < - 共同的TcpChannel              //处理内部消息         }     } } 

在你的骨架开始运转之后,把那些吸盘扔进去 main.go 因为你很高兴。

 主要  进口      “同步”   FUNC 主要() {     VAR WG 同步WaitGroup     WG3       客户的TcpClient()      命令行CLI()      监控监控()      WG等待() } 

当然,你可以用不同的方式杀死程序。也许你的每个图层都有一个自杀命令,并让他们回调等待组 wg.Done() 安全地终止程序。如果当前活动数据无关紧要,a os.Exit(0) 你会没事的。

所有这些工作的结果是你现在有一个很好的蛋糕层,你可以理解:

  • 每个命令都来自哪里
  • 谁与什么以及如何相互作用

在此之上,您可以获得模块化,可维护性和多线程的优势。

资讯来源:由0x资讯编译自DEV,原文:https://dev.to/virepri/leveraging-the-layer-cake-design-in-go-45ci ,版权归作者所有,未经许可,不得转载
你可能还喜欢