1 高内聚、低耦合

虽然已经毕业很多年了,但依然总是能记得,《软件工程》这门课的老师总是强调 "高内聚,低耦合"。

这些年,在架构方面的技术发展方向,目标就是不断的拆分代码。

其本质就是 —— 提高内聚性、降低耦合度。

2 我们要拆分什么?

为了降低程序的耦合度,我们总是把代码拆分的更细小。

  • 我们拆分业务,得到微服务。
  • 我们拆分出 表现 - 控制 - 模型,得到了 MVC。
  • 我们拆分出 交互 - 逻辑 - 存储,得到了三层模式。

每一次的拆分,都是对单个职责的细化。

现如今,我们的代码基本上是这样子的:(下面的例子是由用户角度出度,以数据方向顺序来举例的 )

  1. 前台是由 webpack 打包的单页面应用
  2. 利用 ajax 将请求发送到 WebApi 上
  3. WebApi 根据 http 请求调度相应的 Controller
  4. 我们在 Controller 层调用相应的 Service
  5. Service 调用相应的 RepositoryDao 或是其它什么数据持久层

我们根据不同的关注点,拆分成了不同的层次:前台、Controller 层、Service 层、等等。。。。

2.1 前台

我们其实可以把前台看作一个独立的应用,它其实同样需要拆分,这里我就不细谈了。

2.2 Controller

无论是哪种的 Web 服务器、无论哪种开发语言,都会将接收的 Http 请求,转换成为某种固定的数据结构。

笼统的说,接下来程序要做的事情,就是拿着这个数据结构,根据里面数据的不同,去找一个 Controller 去执行。

这个 Controller 就是这个 Http 请求的接受者

流程大致如下:

  1. 将 Http 请求封闭成一个 固定的数据结构
  2. 根据预设的规则,分析这个 固定的数据结构 ,创建一个 Controller

可以看出来,无论是哪种情况,它们在调用 Controller 前,都是同一个数据结构。

2.3 Service 

大家也许并没有留意这个细节,

Service 的调用是一种同时要求知道 数据结构 和 接受者 的过程。

 public class SomeController
{
private readonly ISomeService someService; public SomeController(ISomeService someService)
{
this.someService = someService;
} [HttpPost("Create")]
public SomeDto CreateSomething(SomethingDto parameters)
{
return this.someService.Create(new Something()
{
A = paraeters.A
// and so on
});
}
}

从上面的代码可以看出来,当我们意图创建一个 Something 的时候,我们需要知道三件事

  1. 我们需要使用哪个 Service
  2. 我们需要调用 Service 上的哪个方法
  3. 我们需要以何种数据结构调用 Service 的方法

再对比 Http - Controller 之间的关系,就会发现不同了。

  Controller 调用 Service 调用
数据结构 始终相同 不同的 Service,基本都不一样
数据接受方 不需要知道 需要知道
接受方的方法 不需要知道 需要知道

很明显,Controller 调用方法更好,它的调用方法几乎不需要依赖太多的信息。

我们把这种调用的方式,称为 命令总线。

2 命令总线

命令总线 就像一个黑盒子,你要做的就是把命令放进去,然后结果就弹出来了。

至于一个命令如何去找到它的处理者,那肯定是有一定 规则。

有了 命令总线,有些事情就发生了变化。

不再需要问这个功能在哪个 Service 上了,只要知道这个功能用哪个命令就行了。

实际研发过程中,总有一些功能,放哪个 Service 上都可以,也都不完全合适。

但是把它看过一个命令,就不再会有任何的不合适了。

3 Reface.CommandBus

Reface.CommandBus 这是一个基于 .NetFramework 4.6.1 开发的 命令总线 工具库。

你可以用这个工具库创建命令,并且透明化的执行这些命令并得到结果。

3.1 相关地址

3.2 使用方法

3.2.1 创建命令

所有的命令都必须实现 ICommand 这个无内容的接口。

广义上的命令,是一个开放的,无约束的数据结构。

但是在某些特殊的使用场景下,我们可以使用统一的数据结构作为命令的载体,更可以使用统一的数据结构作为响应的载体。

    public class ACommand : ICommand
{
// 这里可以编写你需要的参数
}

3.2.2 创建命令处理器

命令处理器需要实现 ICommandHandler<T> 接口,其中泛型 T 就是待处理的 Command 的类型。

    public class ACommandHandler : ICommandHandler<ACommand>
{
public object Handler(ACommand command)
{
return "A";
}
}

3.3.3 将处理器注册到总线中

将处理器注册到总线中,是通过向 处理器工厂 实现的,当前版本中,有两个 处理器工厂 类型

  • DefaultCommandHandlerFactory,这是一个基于硬编码注册的工厂
  • ConfigurationCommandHandlerFactory,这是一个基于读取 .config 文件进行注册的工厂,Github 上有关于配置的方法,比较简单。

你也可以定制自己的 处理器工厂 完成更好的从注册到创建过程。

这里还有一个扩展库 [ Reface.CommandBus.IntegrateAutofac ] ,是于 Autofac 集成后,以程序集为单位进行 处理器 注册的实现,可以通过 nuget 安装得到。

3.3.4 创建 ICommandBus 实例

目前库内自带的是 DefaultCommandBus 实例

ICommandBus bus = new DefaultCommandBus(factory); // facotry 就是 ICommandHandlerFactory 的实例
ACommand cmd = new ACommand();
string result = bus.Dispatch<ACommand, string>(cmd);

result 就是执行结果。


通过这种模式对命令的调用,可以让命令发起方只需要关心使用哪个命令和返回的结果类型就可以了。

在比较小的应用领域,可以统一命令结构和响应结构。

[C#] 命令总线模式的更多相关文章

  1. 好压(HaoZip)的命令行模式用法介绍

    好压压缩软件,又叫“2345好压”,是一款国产的优秀压缩软件,目前是免费的,据官网介绍,该软件永久免费.官网地址:http://haozip.2345.com/ 本文主要对该软件的命令行模式用法进行介 ...

  2. WinRAR的命令行模式用法介绍

    因工作中要对数据打包,顺便研究了下WinRAR的命令行模式,自己写了些例子,基本用法如下: 测试压缩文件准备:文件夹test_data,内部包含子文件夹,分别存放了一些*.log和*.txt文件. 测 ...

  3. 命令行模式 svn版本管理

    linux 下svn 在命令行模式下的操作安装完svn服务并配置了环境变量之后,要创建一个存放工厂(项目)的仓库repositories用于版本控制(比如我的repositories的路径为 path ...

  4. Linux 命令行模式 你需要知道的那些事

    注: 安装软件 pip install + 软件名  例如: pip install xadmin 卸载软件 pip uninstall + 软件名 例如 pip uninstall xadmin 安 ...

  5. 如何在命令行模式下查看Python帮助文档---dir、help、__doc__

    如何在命令行模式下查看Python帮助文档---dir.help.__doc__ 1.dir函数式可以查看对象的属性,使用方法很简单,举str类型为例,在Python命令窗口输入 dir(str) 即 ...

  6. Linux 开机启动方式设置 inittab 详解,开机直接进入“命令行”模式

    Linux下的 /etc/inittab 中的英文解释: This file describes how the INIT process should set up  the system in a ...

  7. Ubuntu 开机进入命令行模式

    1.修改配置 sudo vim /etc/default/grub 把 GRUB_CMDLINE_LINUX_DEFAULT="quiet splash" 改为 GRUB_CMDL ...

  8. 命令行模式下 MYSQL导入导出.sql文件的方法

    一.MYSQL的命令行模式的设置:桌面->我的电脑->属性->环境变量->新建->PATH=“:path\mysql\bin;”其中path为MYSQL的安装路径.二.简 ...

  9. [转载] ubuntu开机直接进入命令行模式

    最近安装了ubuntu12.04来使用,每次都进入unity界面再进入命令行很不方便. 不需要界面的话,可以通过设置来开机进入命令行模式. 今天提供两中比较好的方法.经本人测试两中方法都可使用. [1 ...

随机推荐

  1. html和jsp页面中把文本框禁用,只能读不能写的方法

    方法常用有三种: 第一种,使用   onfocus="this.blur()" <input name="deptno" type="text& ...

  2. 题解:BZOJ 1009 HNOI2008 GT考试 KMP + 矩阵

    原题描述: 阿申准备报名参加GT考试,准考证号为N位数 X1X2....Xn(0<=Xi<=9),他不希望准考证号上出现不吉利的数字.他的不吉利数学A1A2...Am(0<=Ai&a ...

  3. MyBatis if test 传入一个数字进行比较报错 There is no getter for property named 'userState' in 'class java.lang.Integer'

    在写MyBatis映射文件中,我要传入一个 int 类型的参数,在映射文件中用 'test' 中进行比较,我花了很长时间百度,发现都是不靠谱的方法,有把数字在比较时转成字符串用 equals 比较的. ...

  4. python 面向对象静态方法、类方法、属性方法、类的特殊成员方法

    静态方法:只是名义上归类管理,实际上在静态方法里访问不了类或实例中的任何属性. 在类中方法定义前添加@staticmethod,该方法就与类中的其他(属性,方法)没有关系,不能通过实例化类调用方法使用 ...

  5. windows的各种扩展名详解

    Windows系统文件按照不同的格式和用途分很多种类,为便于管理和识别,在对文件命名时,是以扩展名加以区分的,即文件名格式为: 主文件名.扩展名.这样就可以根据文件的扩展名,判定文件的种类,从而知道其 ...

  6. 阿里云ECS 实例Centos7系统磁盘扩容

    需求:一台阿里云的数据盘磁盘空间不足,需要扩容,我这里只有一个主分区,ext4文件系统. 因为磁盘扩容场景不同,阿里云的文档比较全面一些,所以先奉上阿里云的文档,下面开始我的操作步骤: 1.登录控制台 ...

  7. Nginx 配置GeoIP2 禁止访问,并允许添加白名单过滤访问设置

    配置环境:Centos 7.6 + Tengine 2.3.2 GeoIP2 下载地址:https://dev.maxmind.com/geoip/geoip2/geolite2/ 1. Nginx  ...

  8. 攻防世界Mobile5 EasyJNI 安卓逆向CTF

    EasyJNI 最近正好在出写JNI,正好看到了一道JNI相关的较为简单明了的CTF,就一时兴起的写了,不得不说逆向工程和正向开发确实是可以互补互相加深的 JNI JNI(Java Native In ...

  9. GZOJ 1361. 国王游戏【NOIP2012提高组DAY1】

    国王游戏[NOIP2012提高组DAY1] Time Limit:1000MS Memory Limit:128000K Description 国王游戏(game.cpp/c/pas) [问题描述] ...

  10. 【面试必备】硬核!30 张图解 HTTP 常见的面试题

    每日一句英语学习,每天进步一点点: 前言 在面试过程中,HTTP 被提问的概率还是比较高的.小林我搜集了 5 大类 HTTP 面试常问的题目,同时这 5 大类题跟 HTTP 的发展和演变关联性是比较大 ...