Go语言推崇的CSP编程模型和设计思想,并没有引起很多Go开发者包括Go标准库作者的重视。标准库的很多设计保留了很浓的OOP的味道。本篇Blog想比较下从设计的角度看,CSP和OOP到底有什么区别。

下面,我们来看一个例子,如果我们有一个项目,需要做一个TCP连接中继器(请原谅我的用词)。我们先按照OOP来设计下:

  1. 系统的结构:需要有一个客户端和一个服务器端。分两个进程分别跑在不同机器上。
  2. 系统对象关系拆分(这里有所简化,E-R图等省略):连接中继器类--系统的主类config类--描述配置的类、connection类--每个连接一个conn类的实例、pipe类--提供一个管道,把上游的连接和下游的连接打通,把数据从A--pipe--B、encrypt工具类,提供各种加解密工具。
  3. 理清楚系统中各种对象(类)的作用关系,设计接口的细节。这里的接口,其实就是对象之间相互发送的同步阻塞的消息。
  4. 设计错误处理,日志等。
  5. 从性能方面审视整个设计,优化。

=====

好,我们再按CSP的思路来设计下,是这么一个过程:

  1. 我们需要一个 main 协程来处理各种命令行参数的配置,收集处理配置文件; 如果是server,那么初始化server的主协程 -- tcpRemote;如果是client,则启动client的主协程 -- tcpLocal;
  2. 分别设计 tcpRemote和tcpLocal。tcpRemote 是server主协程,负责在指定的端口监听,如果有新的连接连入,则启动一个协程处理 -- serverConn ; tcpLocal是client的主协程,负责在客户机的指定端口监听,如果有连入连接,则启动一个协程处理 -- clientConn;
  3. 分别设计 serverConn和clientConn。serverConn 处理新连入连接的请求,把连接交给shadow函数处理得到一个装饰后的连接,析出目的Addr,发起连接到远端,再交给 relay 函数处理。clientConn 处理新连入的连接请求,析出目的地址, 发起一个新的到server的连接,把连接交给shadow函数处理得到一个装饰后的连接,转发请求给server,接下来的工作交给 relay 函数来处理。
  4. shadow函数,接受一个连接返回一个连接,标准的装饰器。由于需要实现多种加密算法,所以shadow函数有很多实现。程序启动时,在初始化阶段根据配置,注册好对应的shadow函数实现。
  5. relay函数,一个桥接器,接受两个连接,异步转发两个连接中 上行 和 下行 的数据。分别用两个协程来实现数据的 上行 和 下行 的转发逻辑。
  6. 设计错误处理,日志等。
  7. 从性能方面审视整个设计,优化。

=====

OOP部分写的比较简略,但是设计思路还是能看出来的,OOP的设计 核心的围绕点是系统中的对象的种类、职责以及相互的关系;OOP在低并发的时代诞生,对于系统中动力分配是不怎么重视的。在遇到具有共性的点的时候,OOP多是用接口的形式表达,多个不同类实现同一个接口。

CSP的设计 核心的围绕点,是系统中的动力源,也就是系统中动力的来源。动力源在Go语言里就是goroutine;由于goroutine往往是通过闭包函数创建出来,所以闭包函数捕获的upvalue等,也就成了父goroutine和子goroutine之间的一种隐藏的协议。更重要的,每个协程,本质上就是在调度发生时,自动把 上下文 保存起来的回调函数。这大大简化了状态的维护工作。在遇到具有共性的点的时候,CSP是多用装饰器和桥接器,把系统中的共性用函数的参数表达出来。

这两种设计中,接口和函数其实可以相互转换,一个接口只有一个方法的等价于一个函数;而几个函数构成了固定的组合,在Go里面等价于实现了某个接口。所以,这种对共性抽象的方法并没有太大的差别,甚至有人就推崇在Java中,一个接口就只有一个方法。

=====

OOP、FP、CSP、Actor等思想,其实都是在做取舍,究竟要隐藏那些细节暴露那些功能。如果什么都不考虑,那就是汇编了(近似的说法)。没有最优的设计思想只有合适的设计思想。

无论OOP/FP/CSP/Actor模型,都是可以相互转换、替换和实现。

FP/CSP/Actor中大量用闭包,其实就是把OOP的结构体交给编译器去自动生成而已,每个闭包函数捕获的upvalues在各种支持闭包的语言中,多是交给编译生成一个特殊命名的结构体,并在闭包传递时一并生成实例并传递引用。这样就使得一些地方用于消息传递的结构体可以省略,很多时候在chan中传递一个func()比传递一个消息更加的方便。Go的chan可以看作是把传统OOP语言以 方法调用形式 表达的同步阻塞消息传递,改成了显式的消息传递,更好的是,多路分发和逆多路分发机制也集成在语言中。

OOP中的方法调用本质上就是一种同步阻塞的消息传递,这点在ObjC中表现的非常清楚,ObjC中每个方法调用本质上就是发送了一条消息给某对象(sendmsg是一个变参数函数)。OOP隐形约定了,所有的进程内的语言运行时级别的消息传递都是同步阻塞的。而Go/Erlang等CSP/Actor模型的语言,打破了这一点,提供了语言级别的异步非阻塞的消息传递。

如果我们把软件设计分成 装配、动力驱动、可变性 三个方面考虑。

OOP的装配工作量比CSP要大的多。每个接口的每个方法都可以看成是一个螺丝,只有你紧固了每一个螺丝,OOP设计的软件才可以运行。而CSP设计的程序,每一个协程的创建,都是一个装配点,仰赖方便的闭包机制,装配所需螺丝是一次性自动紧固的。这就是CSP在设计上的优势之一吧。

在动力驱动方面,OOP由于假设了方法调用是同步阻塞的消息传递,其动力驱动也比较原始,大部分是依赖操作系统提供的线程和进程机制。但是CSP则提供了异步的非阻塞消息机制,以及自动上下文保存的可中断函数(也就是协程)。这些机制使得CSP的动力驱动简单高效。

在可变性方面,OOP的合约是由接口和结构体来约束的,而CSP的合约是由函数签名和闭包的upvalues来约束的。函数的参数和返回值可以都是空,只用upvalues来隐式表达约束。因此CSP在可变性方面也是更优秀的。

P.S.

需要强调的是OOP并没有什么特别的不好的,相反OOP具有巨大的优势,就是容易设计。

CSP虽然会要求从设计上改变即有思路,耗费较多的脑力,但其设计方案简单容易扩展,具有巨大的优势。

https://my.oschina.net/linker/blog/1507315

引用来自“qgymje”的评论
在Go里,可以将CSP的概念融入到OOP中,最为简单的做法是将函数调用的参数与返回值的类型都改为channel,这样可以做到function call在一定程度(channel没满)上为异步,至于闭包context不是重点,这个概念在Actor Model里表示为state; OOP思想可以体现在实现Actor Model的语言里,CSP是一种特殊的Actor,关注点不同

引用来自“LinkerLin”的评论
嗯,这么理解是可以的。
我说的是设计上,可以不用先考虑chan的使用,只关注goroutine,设计好goroutine后,goroutine之间的通讯可以不用chan,而是用闭包捕获的upvalues来沟通。这样就省去了通讯数据结构的设计。或者在chan中传输func,也一样省去了通讯用数据结构。

无论OOP,CSP, Actor, 它们的关注点都是message passing,oop目前大多表现为同步,actor为异步,csp半同步半异步

基于 CSP 的设计思想和 OOP 设计思想的异同的更多相关文章

  1. 基于UML的高校教务管理系统的设计与实现

    一.基本信息 标题:基于UML的高校教务管理系统的设计与实现 时间:2018 出版源:南昌航空大学 领域分类:教育信息化:教务管理系统:UML:SSH:Oracle 二.研究背景 问题定义:高校教务管 ...

  2. 基于ESP32的智能家居管理系统的设计与实现

    基于ESP32的智能家居管理系统的设计与实现 ESP32的智能家居管理系统访问链接: https://www.cnblogs.com/easyidea/p/13101165.html 一.需求分析 1 ...

  3. 基于 Angularjs&Node.js 云编辑器架构设计及开发实践

    基于 Angularjs&Node.js 云编辑器架构设计及开发实践 一.产品背景 二.总体架构 1. 前端架构 a.前端层次 b.核心基础模块设计 c.业务模块设计 2. Node.js端设 ...

  4. 基于特定领域国土GIS应用框架设计及应用

              基于特定领域国土GIS应用框架 设计及应用              何仕国 2012年8月16日   摘要: 本文首先讲述了什么是框架和特定领域框架,以及与国土GIS 这个特定领 ...

  5. 【科研论文】基于文件解析的飞行器模拟系统软件设计(应用W5300)

    摘要: 飞行器模拟系统是复杂飞行器研制和使用过程中的重要设备,它可以用来模拟真实飞行器的输入输出接口,产生与真实系统一致的模拟数据,从而有效避免因使用真实飞行器带来的高风险,极大提高地面测发控系统的研 ...

  6. Python 基于python+mysql浅谈redis缓存设计与数据库关联数据处理

    基于python+mysql浅谈redis缓存设计与数据库关联数据处理 by:授客  QQ:1033553122 测试环境 redis-3.0.7 CentOS 6.5-x86_64 python 3 ...

  7. 基于SSH框架的考勤管理系统的设计与实现

    基于SSH框架的考勤管理系统的设计与实现

  8. 基于Android平台的会议室管理系统具体设计说明书

    会议室管理系统具体设计说明书 第一部分  引言 1.编写目的 本说明对会议室管理系统项目的各模块.页面.脚本分别进行了实现层面上的要求和说明. 软件开发小组的产品实现成员应该阅读和參考本说明进行代码的 ...

  9. 基于Redis的限流系统的设计

    本文讲述基于Redis的限流系统的设计,主要会谈及限流系统中限流策略这个功能的设计:在实现方面,算法使用的是令牌桶算法来,访问Redis使用lua脚本.   1.概念 In computer netw ...

随机推荐

  1. 【solr专题之一】Solr快速入门 分类: H4_SOLR/LUCENCE 2014-07-02 14:59 2403人阅读 评论(0) 收藏

    一.Solr学习相关资料 1.官方材料 (1)快速入门:http://lucene.apache.org/solr/4_9_0/tutorial.html,以自带的example项目快速介绍发Solr ...

  2. JMS是一种应用于异步消息传递的标准API

    JMS是一种应用于异步消息传递的标准API,作为Java平台的一部分,JMS可以允许不同应用.不同模块之间实现可靠.异步数据通信.一些概念 JMS provider    An implementat ...

  3. PatentTips - Method and system for browsing things of internet of things on ip using web platform

    BACKGROUND The following disclosure relates to a method and system for enabling a user to browse phy ...

  4. Django之模板过滤器

    Django 模板过滤器也是我们在以后基于 Django 网站开发过程中会经常遇到的,如显示格式的转换.判断处理等.以下是 Django 过滤器列表,希望对为大家的开发带来一些方便. 一.形式:小写 ...

  5. 安装alien,DEB与RPM互换

    http://blog.csdn.net/sidely/article/details/40181653

  6. 走进windows编程的世界-----窗体的注冊及创建

    1   窗体注冊和创建 1.1WIN32 窗体程序创建步骤 1.WinMain入口函数的定义 2.WindowProc函数的定义 3.注冊窗体类 RegisterClass.RegisterClass ...

  7. 【oracle11g ,19】索引管理

    一.索引的分类: 1.逻辑上分为:  单列索引和复合索引  唯一索引和非唯一索引  函数索引 domain索引 2.物理上分:  分区索引和非分区索引 b-tree  bitmap 注意:表和索引最好 ...

  8. 开发自己的PHP MVC框架(一)

    这个教程能够使大家掌握用mvc模式开发php应用的基本概念.此教程分为三个部分.如今这篇是第一部分. 如今市面上有非常多流行的框架供大家使用.可是我们也能够自己动手开发一个mvc框架.採用mvc模式能 ...

  9. js如何实现动态点击改变单元格颜色?

    js如何实现动态点击改变单元格颜色? 一.总结 1.通过table的rows属性,遍历表格所有行,然后通过cells属性,遍历每一行中的单元格. 2.遍历的过程中,动态的为每一个单元格定义单击事件,改 ...

  10. 【codeforces 754A】Lesha and array splitting

    time limit per test2 seconds memory limit per test256 megabytes inputstandard input outputstandard o ...