WEBVTT 00:00:00.000 --> 00:04:43.738 (演讲从 4:45 开始) 00:04:46.478 --> 00:04:50.376 好吧,你们都能听到我吗? 00:04:50.785 --> 00:04:56.304 这种备用的方式能听到我吗?好的,不错不错不错。 00:04:56.401 --> 00:05:00.828 嗯...什么是电子? 00:05:04.013 --> 00:05:07.984 那么我想每个人都知道,它是原子的一部分 00:05:08.290 --> 00:05:17.718 嗯...(从其他房间的尖叫声)是啊,我是这么认为的。我认为这是严肃的(不会翻译)?? 00:05:19.734 --> 00:05:22.415 电子是原子的一部分 00:05:22.604 --> 00:05:32.113 它携带单位负电荷,对立于带有正电荷的质子。 00:05:32.230 --> 00:05:42.408 它显然也有质量,但是却非常小,成千上万的电子或类似的东西。质量非常非常小 00:05:42.922 --> 00:05:55.041 我们认为它是一个粒子,但是这是误导。电子的实际行为更像是一个比波就像一个粒子 00:05:55.041 --> 00:05:58.778 那为什么我们认为它是粒子? 00:05:58.780 --> 00:06:08.906 ,我们这样做的原因是,当一个电子相互作用,在它就像一个粒子那样在一个位置相互作用 00:06:09.151 --> 00:06:16.302 但当电子通过空间移动时它不会像粒子那样移动 00:06:16.418 --> 00:06:21.498 它像波浪一样移动,并且波浪移动式没有具体位置的 00:06:21.498 --> 00:06:27.334 比如大海的波浪,它可以变得巨大 00:06:27.334 --> 00:06:33.819 它有明确的能量,但没有明显的位置 00:06:33.819 --> 00:06:36.450 这就是电子如何移动的 00:06:36.450 --> 00:06:42.191 电子遵守一个原则,就是mistruster原则 00:06:42.191 --> 00:06:45.566 它被称为泡利不相容原理 00:06:45.566 --> 00:06:53.202 两个电子绑定到同一个的系统不会有相同的状态 00:06:53.202 --> 00:06:57.328 无论出于何种原因,他们不能有相同的状态 00:06:57.328 --> 00:07:00.677 因此,如果有两个电子中的原子 00:07:00.677 --> 00:07:04.066 那两只电子一定是不同的 00:07:04.066 --> 00:07:07.709 现在,他们可能以不同的方向旋转 00:07:07.709 --> 00:07:10.899 电子可以向左旋转,也可以向右旋转 00:07:10.899 --> 00:07:18.355 两个电子具有完全相同的能量可以结合在一起,一个向左转,一个向右转 00:07:18.355 --> 00:07:21.442 有没有办法让第三个电子也在那里? 00:07:21.450 --> 00:07:28.412 所以下一电子必须具有更高的能量,那么你可以另外有两个 00:07:28.412 --> 00:07:30.806 一个向左边旋转,一个向右边旋转 00:07:30.806 --> 00:07:34.769 但是如果你想增加另一个,那将是不同的东西 00:07:34.769 --> 00:07:37.917 而你可以在不同的空间安排他们 00:07:37.985 --> 00:07:48.589 所以,你可以有一组的两个这样的,另一组两个这样的,另一组两个这样,一组的两个球体的这汇总方式增加了八个。 00:07:49.633 --> 00:07:57.677 我们所知道的大部分原子都有8个电子在它们的壳中 00:07:57.690 --> 00:08:07.827 原因是眼前这个可爱的小几何形状。球体中三行每行可以包含两个 00:08:07.827 --> 00:08:12.868 你有没有想过水? 00:08:12.868 --> 00:08:16.880 我这里就有一些。迷人的物质 00:08:16.880 --> 00:08:19.898 它是什么做的? 00:08:20.484 --> 00:08:25.082 两个氢原子一个氧原子构成一个水分子 00:08:25.472 --> 00:08:28.076 它是什么形状? 00:08:28.076 --> 00:08:33.482 米老鼠!是的,中间一个大的氧原子和旁边两个像耳朵一样的氢原子 00:08:33.482 --> 00:08:42.437 它们的角度是外壳安排的结果 00:08:42.437 --> 00:08:46.772 是什么使氢气和氧气粘在一起? 00:08:46.772 --> 00:08:51.162 想想两个原子,上面覆盖着电子,两个电子互相平行 00:08:51.162 --> 00:08:56.287 是什么让两个氢原子和一个氧粘在一起? 00:08:56.287 --> 00:09:03.380 而事实证明,如果你把这些两个氢原子和氧放在一起 00:09:03.380 --> 00:09:12.156 那么你想想电子的波浪,而不是粒子,那些波浪会想去哪里呢? 00:09:12.156 --> 00:09:20.400 那些波浪都想在氢原子的质子和氧原子的质子之间 00:09:20.400 --> 00:09:25.099 因此,他们聚集电子的波浪会越集越多 00:09:25.099 --> 00:09:30.861 两个原子之间,实际上是三个,然后再是其他地方 00:09:30.861 --> 00:09:34.964 它们还是会去其他地方,只是这更可能是质子之间 00:09:34.964 --> 00:09:47.718 这意味着原子之间有额外的负电荷,并且负电荷还要跟踪质子,这就是共价键 00:09:47.718 --> 00:09:54.029 现在,如果你把这个米老鼠原子并且一分为二 00:09:54.136 --> 00:09:57.675 你发现有更多的负电荷以上的一个和下面的一个 00:09:57.675 --> 00:10:02.000 原因是全部负电荷都喜欢坐在两个氢原子和氧原子之间 00:10:02.000 --> 00:10:08.075 因此,有比另一侧更多的负电荷,这意味着这是水分子的偶极 00:10:08.075 --> 00:10:10.676 它一面是负极,一面是正极 00:10:10.676 --> 00:10:12.702 这就是为什么水是湿的。 00:10:12.702 --> 00:10:20.463 水粘到你的手,因为所有的水分子旋转,粘到你的皮肤上带电的表面 00:10:20.463 --> 00:10:22.614 即使它不是很带电 00:10:22.614 --> 00:10:33.736 这就是为什么水会是良好的溶剂,因为小的水分子会旋转吸引他们想要的粘上其它分子的部分 00:10:33.772 --> 00:10:37.394 这就是为什么水使它成为一个很好的清洁剂 00:10:37.394 --> 00:10:42.888 这就是你为什么可以用水来做最美好的事情(清洗?)。谁是做过这样的实验? 00:10:42.888 --> 00:10:51.704 你找到一个水龙头。然后你打开一点非常小的水流。不是很快,水只是涓涓溪流 00:10:51.704 --> 00:11:02.317 然后你拿一个气球,在你的头发上擦一擦。然后拿着气球慢慢接近水流,水会朝着气球弯曲 00:11:02.317 --> 00:11:06.252 正如所有的水分子转身把它跟踪的电荷 00:11:06.252 --> 00:11:09.585 当然,这不是我们应该谈论的 00:11:10.815 --> 00:11:19.975 这次演讲的题目叫做“架构----逝去的年华”。这个演讲我已经做了好几年。 00:11:20.057 --> 00:11:28.490 这是关于干净的设计简洁的代码后的下一个步骤。 00:11:28.535 --> 00:11:33.876 这是关于干净的系统结构 00:11:33.876 --> 00:11:38.162 这从那开始, 00:11:38.162 --> 00:11:47.069 这是一个十年前左右我写的应用程序的目录结构。 00:11:47.069 --> 00:11:53.089 那时我正在研究的Rails。有人是Ruby on Rails的程吗序员? 00:11:53.089 --> 00:11:56.906 在那边有一个,他在挥舞他的手。很好。 00:11:57.047 --> 00:12:04.404 我当时学习的Rails。我写了这个应用程序 00:12:04.404 --> 00:12:09.735 它是我跟着书写的。你得到的书籍了。你按照书的提示 00:12:09.767 --> 00:12:13.839 我按照书上的所有建议完成了它。 00:12:13.839 --> 00:12:20.077 对此我很高兴。然后我很长时间没有去管他 00:12:20.140 --> 00:12:27.376 然后大约三年前,我的儿子在我的要求下,写了一个应用程序 00:12:27.376 --> 00:12:32.325 然后我看着那个程序,我看到那个目录结构。 00:12:32.325 --> 00:12:36.174 一模一样的目录结构 00:12:36.174 --> 00:12:40.573 这是两个完全不同的应用程序。它们之间一点关系也没有 00:12:40.614 --> 00:12:45.121 可是他们有相同的目录结构。我看着它开始思考。 00:12:45.121 --> 00:12:52.868 为什么这两个应用程序具有相同的高层次目录结构? 00:12:52.868 --> 00:12:56.646 它们是两个完全不同的应用程序。 00:12:56.646 --> 00:13:02.958 我认为他们有相同的目录结构,是因为他们都是Rails应用程序。 00:13:02.958 --> 00:13:09.304 我又问我自己,好的,但为什么那么重要? 00:13:09.304 --> 00:13:20.673 为什么Rails或者其他框架这么重要,它会占据应用程序的高层次目录结构主导地位? 00:13:20.726 --> 00:13:25.183 而我之所以对自己的资产问题是因为这个。 00:13:25.986 --> 00:13:34.191 Web是一个传递机制,是一个I / O通道 00:13:34.191 --> 00:13:37.913 Web不是架构设计中的重点 00:13:37.980 --> 00:13:42.263 我们认为自己是编写Web应用程序。 00:13:42.263 --> 00:13:44.786 其实我们不是编写Web应用程序。 00:13:44.786 --> 00:13:51.940 我们正在编写一种通过I/O通道(我们熟知的Web)传递它们的内容的应用程序 00:13:51.940 --> 00:13:57.675 那为什么应该是I / O通道主宰我们? 00:13:59.785 --> 00:14:06.705 有谁还记得当Web变得重要起来?1980年?1990年? 00:14:06.705 --> 00:14:13.833 有谁还记得那是一个怎样的变化?如何从根本上让一切变得不同? 00:14:13.833 --> 00:14:19.872 除非它不是,因为我们没有真正在做什么新的东西? 00:14:19.872 --> 00:14:26.511 我们只是从输入源将输入汇集起来,处理它,将它扔向一个输出源 00:14:26.573 --> 00:14:28.374 这就是Web的所有 00:14:28.374 --> 00:14:31.082 为什么Web会主宰这么多? 00:14:31.082 --> 00:14:39.189 于是我开始思考架构。我开始寻找蓝图 00:14:40.649 --> 00:14:49.599 这里有一张蓝图。如果那上面没有写着”library“这个单词。不用多久,你也会弄清楚这是一个图书馆 00:14:49.599 --> 00:14:51.729 因为它显然是一个图书馆。 00:14:51.805 --> 00:14:55.882 那里有一堆书架。那里有一堆读书用的桌子。 00:14:55.882 --> 00:15:05.705 桌子上有一些电脑。还有的地方,那里有可以借阅或归还图书的前台 00:15:05.705 --> 00:15:11.167 这不会花太长时间让你弄清楚去:“嗯..这一定是这样一个图书馆。“ 00:15:11.167 --> 00:15:15.704 这里还有另外一个。这是一个教堂。这显然是一个教堂。 00:15:15.704 --> 00:15:20.999 哦,你可能会误以为这是一个剧场。剧院和教会确实有一定的相似的地方。 00:15:20.999 --> 00:15:29.736 但是,没有,这绝对是一个教堂。PUSE,祭坛,在外面教室,前面围绕着的祷告区 00:15:29.736 --> 00:15:31.703 这显然是一个教堂。 00:15:31.703 --> 00:15:42.315 建筑的架构不会告诉你它们是如何构建的。而是告诉你它们是什么。 00:15:42.315 --> 00:15:49.020 他们的意图。架构就是关于意图的。 00:15:49.020 --> 00:15:56.016 但是Rails应用程序的高层次目录结构并没有传达意图给我。 00:15:56.154 --> 00:16:03.104 他们只是告诉我,他们是Rails应用。更蹊跷的是 00:16:03.104 --> 00:16:05.902 然后它发生在我身上。 00:16:05.902 --> 00:16:15.087 这是已知问题?这是已解决问题。人们意识到,1992年伊娃·雅各布森解决并写了这本书。 00:16:15.087 --> 00:16:20.638 谁有这本书?谁读过这本书?这里有一个人,其他人呢? 00:16:20.677 --> 00:16:23.431 《面向对象的软件工程》,如果你有,它是一本精彩的书。 00:16:23.503 --> 00:16:29.821 1992年有点老了,但没关系。里面的原则还是非常好的。 00:16:29.916 --> 00:16:37.212 注意副标题,它写着:”用例驱动法“ 00:16:37.212 --> 00:16:45.107 谁还记得”用例“?呃..看到了吗?那是在90年代初非常流行。非常的火 00:16:45.107 --> 00:16:54.199 事实上,它是如此受欢迎,甚至被那些侵入并摧毁了用例本来该有的样子的顾问们彻底摧毁 00:16:54.199 --> 00:17:06.426 如果你还记得那个时代,你可能还会记得一个又一个的顾问在互联网上发布自己特定格式的用例 00:17:06.426 --> 00:17:15.840 格式成为了所有的重点。我们的PDF文件都在那里,它让你填写一个标准的用例形式的模板。 00:17:15.840 --> 00:17:23.318 就是填上用例的名称,再填上”输入“以及用例"前提"和"后置" 00:17:23.318 --> 00:17:29.726 和主要角色和次级角色以及三级角色。三级角色是什么鬼? 00:17:29.726 --> 00:17:38.276 你必须填写所有的东西,用例的整个问题变成了一种形式,而不是一种功能。 00:17:38.306 --> 00:17:44.214 当正好时代的巅峰,敏捷运动开始。 00:17:44.214 --> 00:17:52.242 我们不再讨论用例,我们开始讨论故事,用例有关的全部东西被扔弃在地板上。没有人再讨论它 00:17:52.242 --> 00:17:58.310 于是我又将它拿出来,重新一遍手册,读一遍书 00:17:58.310 --> 00:18:02.105 我想起了雅各布森写到的东西 00:18:02.168 --> 00:18:11.409 这里是一个用例。雅各布森写的那种典型的用例 00:18:11.444 --> 00:18:15.646 你是否注意到它只有有非常小的形式。 00:18:15.646 --> 00:18:24.568 哦~一点点而已。 created order是它的名字,想象一下,这是订单处理系统的用例。 00:18:24.568 --> 00:18:31.952 它有一个像” customer id“和”客户联系信息“和”运输目的地“等一些输入数据。 00:18:31.952 --> 00:18:37.657 注意,我没有提供任何细节,我不是在强调”客户ID“是什么 00:18:37.657 --> 00:18:40.146 无论是数字或字符串,我不在乎。 00:18:40.187 --> 00:18:48.264 我没讨论客户联系方式是什么,我只是认为它有名字和日期和地址以及其他的东西。但我不在乎 00:18:48.328 --> 00:18:51.560 我并不想在此处指定明确的细节。 00:18:51.599 --> 00:18:55.268 然后,到了主要课题 00:18:55.268 --> 00:19:04.557 主要课题一套计算机运行满足用例的步骤,是处理的步骤 00:19:04.557 --> 00:19:11.037 第一步是订单业务员发布的创建订单的命令,实际上不是计算机的做的事。 00:19:11.073 --> 00:19:18.511 第二步是有系统来验证数据是否有效。注意,我没有讨论如何验证,怎么验证并不重要 00:19:18.575 --> 00:19:23.542 第三步是系统创建的订单,并且确定订单的ID。 00:19:23.542 --> 00:19:29.089 我假定这是某种数据库的操作,但我不会在这里讨论。 00:19:29.089 --> 00:19:35.036 第四步是系统将订单号返回给业务员,也许是一个Web网页,但我不会在这里讨论 00:19:35.106 --> 00:19:40.426 事实上,整个的用例只字未提Web。 00:19:40.426 --> 00:19:44.456 这个用例的运行与什么样的输入输出通道没有任何关系。 00:19:44.456 --> 00:19:53.606 我可以在一个控制台应用程序,桌面应用程序,一个Web应用程序,面向服务的架构应用这个用例的运行,我的手机应用程式也没关系。 00:19:53.606 --> 00:19:59.609 因为用例是无关的,它并不关心I / O通道的具体实现。 00:19:59.609 --> 00:20:09.559 雅各布森说,你可以采取的这样的用例,并把它变成对象。 00:20:09.559 --> 00:20:19.878 他把这个对象叫做控制对象。我已经更名为”interactor “,以避免与Model View Controller混乱。 00:20:19.878 --> 00:20:26.239 也许我应该把名字改成”用例“。但我没有。我在这里写的是interactor 。 00:20:26.239 --> 00:20:38.028 interactor 对象实现了用例,它的输入,就是用例的输入,它提供的输出,就作为用例的输出。 00:20:38.028 --> 00:20:45.857 它至少在高层去实现那些用例的具体的规则,处理步骤等 00:20:45.857 --> 00:20:54.166 注意下面的注释,它写着interactors拥有具体的应用业务规则 00:20:54.166 --> 00:20:56.768 其实有两种业务规则 00:20:56.768 --> 00:21:06.166 有一些是全局性的业务规则,他们不关心应用程序是什么。 00:21:06.166 --> 00:21:11.695 然后还有一些是绑你正在编写应用程序的其他业务规则 00:21:11.695 --> 00:21:22.020 例如,我们有一个订单录入应用程序和订单处理应用程序。两个完全不同的应用程序 00:21:22.020 --> 00:21:29.693 他们都可能有一个订单对象,并且订单对象可能有共同的业务规则 00:21:29.693 --> 00:21:40.739 无论你使用哪一个应用,只有其中一个应用程序有插入订单用例 00:21:40.739 --> 00:21:47.910 所以这样的用例是应用程序特定的,他们有他们的特殊应用 00:21:47.910 --> 00:21:56.696 不在特定应用中的业务规则就绑定到实体对象,有些人称为业务对象 00:21:56.727 --> 00:22:01.325 我不喜欢这个词,雅各布森提出的entity objects(实体) 比较合适 00:22:01.405 --> 00:22:10.785 你把所有的应用程序业务规则放到实体里,interactor将控制的这些实体。 00:22:10.785 --> 00:22:17.332 然后你要搞清楚,如何从一个用例获得输入和输出放到到另外一个用例 00:22:17.332 --> 00:22:24.159 我们用接口来实现这个功能,我画了这些作为面向对象的接口 00:22:24.159 --> 00:22:31.266 注意,interactor使用了其中一个接口,以及从其他派生的。 00:22:31.266 --> 00:22:38.233 其中一个是从输入接口派生的,另一个是输出接口 00:22:38.233 --> 00:22:42.174 注意,箭头指向相同的方向,这很重要 00:22:42.174 --> 00:22:45.106 我们马上会知道为什么它会这么重要 00:22:45.106 --> 00:22:53.455 这三个对象都是雅各布森提出的应用程序架构的一部分 00:22:53.455 --> 00:22:57.683 现在让我们继续探索,就让我们看看它是如何工作的。 00:22:57.683 --> 00:23:02.934 这是一个典型的应用。那里有一个用户,这么个小人站在那里 00:23:02.934 --> 00:23:10.518 那是一个真实的人,他正在通过某种传递机制与系统进行交互 00:23:10.518 --> 00:23:13.561 也许是Web,也许不是,谁在乎 00:23:13.561 --> 00:23:25.915 用户,也许是按下键盘上的一个键,也许类似的事情,来通知系统接收数据 00:23:25.915 --> 00:23:31.619 这种传递机制也许是网络,也许不是,没多大的关系 00:23:31.619 --> 00:23:37.030 可以理解这是一种请求模型。 00:23:37.030 --> 00:23:39.844 请求模型是一个纯粹的数据结构 00:23:39.844 --> 00:23:50.698 一个普通的旧.NET对象(POCO)或普通Java对象(POJO),一种原始数据,它不知道数据从哪里来, there's no trappings of the web 00:23:50.698 --> 00:23:52.764 If there's the trappings of the web anywhere 00:23:52.764 --> 00:23:56.904 这只是一个普通的数据结构,它没有函数。什么都没有。 00:23:56.904 --> 00:24:01.911 一串的数据结构的公共元素,它包含了所有的输入数据 00:24:01.976 --> 00:24:11.846 然后被传递到interactor派生的输入边界接口, 00:24:11.846 --> 00:24:20.717 interactor收到请求模型,读取它,并解释它 00:24:20.717 --> 00:24:26.778 并且把它变成一套发送到实体的较小的命令 00:24:26.778 --> 00:24:33.196 所有小业务对象在那里,它控制着密密麻麻的所有调用实体的函数。 00:24:33.196 --> 00:24:41.673 一旦工作完成,那么它反向转动。它开始查询这些实体,并说,'OK,你有什么事?“ 00:24:41.673 --> 00:24:47.942 同时他也制造出了另一种数据结构,叫做结果模型 00:24:47.942 --> 00:25:01.338 结果模型也是一个普通的数据结构,不知道任何事情,只是一个POCO或者POJO,只有公共字段,没有函数 00:25:01.338 --> 00:25:11.928 结果模型通过输出边界接口被传递到用户那被显示或者其他的 00:25:11.928 --> 00:25:18.006 那些都是其他应用程序的流程了。 00:25:18.006 --> 00:25:23.632 你能测试Interactor吗? 00:25:23.632 --> 00:25:24.772 当然可以,而且很简单,不是吗? 00:25:24.772 --> 00:25:30.610 创建输入数据结构,调用Interactor看看输出的数据结构 00:25:30.610 --> 00:25:33.424 你需要一台Web服务来完成这件事吗? 00:25:33.424 --> 00:25:40.663 不,因为interactor,只是POCO或者POJO,这就是所有的数据结构 00:25:40.663 --> 00:25:45.237 你需要一个数据库来完成这件事吗?好吧,也许你需要,但是我们只需要一分钟就能完成 00:25:45.237 --> 00:25:50.665 我可以在没有具体传递机制的情况下测试它 00:25:50.665 --> 00:25:56.218 我的传递机制是否是Web?我不在乎!我不需要运行一台Web服务器 00:25:56.218 --> 00:25:58.291 我也不需要通过网页来进行测试 00:25:58.291 --> 00:26:07.206 我可以测试整个系统的运作而不去关心具体的输入/输出的传递机制 00:26:12.286 --> 00:26:14.110 什么是MVC? 00:26:14.110 --> 00:26:18.936 是不是MVC的事情是我们都应该做的? 00:26:20.206 --> 00:26:28.683 MVC代表什么? Model View Controler 是谁发明的MVC 00:26:28.683 --> 00:26:35.480 那家伙。现在我要彻底念错他的名字。你也许可以念的比我好 00:26:35.480 --> 00:26:41.514 可是我要叫他Trygve Reenskaug。你也许可以念的比我好 00:26:41.514 --> 00:26:47.317 我见过他一次。那个在70年代末发明的模型视图控制器的家伙。 00:26:47.317 --> 00:26:50.027 我见过他一次。两年前我在这里遇见了他。 00:26:50.027 --> 00:26:57.378 我在休息室找电源插座,而这个老家伙走到我面前,递给我一个电源插座 00:26:57.378 --> 00:27:01.080 我抬头......Trygve Reenskaug! 00:27:01.080 --> 00:27:07.596 当他递给我电源插座时,我们手指接触了 00:27:07.596 --> 00:27:13.075 我当然不会洗手,Trygve Reenskaug! 00:27:13.075 --> 00:27:19.293 有些人过来找我合影,现在我也是一个有fans的人了。 00:27:21.423 --> 00:27:27.284 在80年代初和70年代末,Trygve Reenskaug想出了这个结构称为模型-视图-控制器。 00:27:27.284 --> 00:27:30.099 他在Smalltalk的平台上这样做 00:27:30.099 --> 00:27:33.042 它背后的原理非常简单 00:27:33.042 --> 00:27:36.704 你已经有了一个模型对象,模型对象包含业务规则。 00:27:36.704 --> 00:27:41.326 它不知道它是如何显示的。它不知道输入从哪里来 00:27:41.326 --> 00:27:48.182 这是纯粹的业务规则,这里有一个控制器,它处理所有的输入 00:27:48.182 --> 00:27:59.586 控制器的工作就是盯着输入,不管是什么设备,键盘什么的都无所谓,然后翻译用户的动作为命令针对model 00:27:59.586 --> 00:28:01.094 然后,View就出现了 00:28:01.094 --> 00:28:06.610 我画的View有一个有趣的双箭头,这是一个观察的关系 00:28:06.610 --> 00:28:15.161 View 在Model 上注册,当Model发生变化,便回调到View,告诉View重新显示 00:28:15.201 --> 00:28:25.347 View的工作是要显示或代表或以某种方式传达模型的内容的。 00:28:25.347 --> 00:28:35.495 它可以很好地在一个图形用户界面上工作,它同样也可以同样运行在其他的你想要运行的系统上 00:28:35.495 --> 00:28:40.718 你有一些东西控制输入(Controler),你有一些东西控制过程(Model),你有一些东西控制输出(View) 00:28:40.718 --> 00:28:51.260 这可能是最早命名的设计模式了,运用在Smalltalk中 00:28:51.260 --> 00:29:01.770 按钮里有MVC模式,复选框里有MVC模式,文本字段里有MVC模式 00:29:01.770 --> 00:29:05.310 我们还没有关于屏幕的MVC模式 00:29:06.600 --> 00:29:14.920 在那些早期的日子里,这已经被扭曲和摧毁,像在软件里的任何东西一样。 00:29:14.920 --> 00:29:23.961 如果它原本是一个好主意,其他人就会复制它。并使用同样的名字,但是完全不同的东西,还要叫好 00:29:23.961 --> 00:29:29.431 OO发生过这事情,structurel发生过这事情,ObjectsIt 发生过这事情,Agile发生过这事情 00:29:29.431 --> 00:29:37.781 这会发生在任何东西上,只要它们的名字与好的东西有关,就有人把他们自己的东西冠以其名,并叫好 00:29:37.781 --> 00:29:44.013 那么MVC现在怎么样了?现在我们拥有许多MVC框架 00:29:44.013 --> 00:29:50.485 他们不顾任何事情,他们不是Trygve Reenskaug所描述MVC 00:29:50.485 --> 00:29:54.416 它们是非常不同的东西,事实上,他们看起来是这样的 00:29:55.416 --> 00:30:07.350 那里你有一大堆的controler,现在的controler,如果我们想一想Web,控制器是空中的Web框架通过某种方式激活的 00:30:07.350 --> 00:30:12.779 空中的Web框架?管它是什么,谁在乎。Rails?spring?上帝才知道那是什么 00:30:12.779 --> 00:30:21.848 通过某种方式从Web路由过来复杂又可怕的URL到一堆函数里,这就是我们说的Controler 00:30:21.848 --> 00:30:27.079 它会把从Web获得的参数和数据传入每个Controler 00:30:27.079 --> 00:30:33.950 然后Controler会接收这些数据,并且向business objects大喊,告诉business objects该做什么 00:30:33.950 --> 00:30:45.731 然后Controler收集来自business objects的数据,又开始向View大喊,然后视图将返回到业务对象,并收集了一堆数据,并表示它们 00:30:46.391 --> 00:30:55.422 最终你的下场就是business objects 被Controller 的功能和View的功能给污染了 00:30:55.422 --> 00:31:05.373 把不同的功能放在什么地方,这很难知道。有时候他们在business objects 里,但是确实却是不应该的 00:31:09.133 --> 00:31:13.874 我们如何处理与输出端? 00:31:15.764 --> 00:31:20.086 在这里,我向您展示了Interactor。Interactor完成了这些事情 00:31:20.086 --> 00:31:32.300 它从用例处理好的实体中收集的数据。该Response Model已经准备好。我们要通过输出边界传递Response Model 00:31:32.300 --> 00:31:38.262 什么实现了输出边界?一种叫Presenter的东西 00:31:38.262 --> 00:31:44.851 Presenter的工作是获得Response Model,记住它,是一个纯粹的数据结构。 00:31:44.914 --> 00:31:52.427 并且把它转换成另一种纯粹的数据结构,我们称之为View Model 00:31:52.427 --> 00:32:03.575 View Model 是输出的一种Model,一种输出的表示。它仍然是一个数据结构 00:32:03.575 --> 00:32:10.685 但是,如果有屏幕上有一个表格,那么数据结构中也将会有一个表格 00:32:10.753 --> 00:32:17.424 如果屏幕上有一个文本字段,那么数据结构中也会有一个文本字段 00:32:17.490 --> 00:32:28.201 如果屏幕上的数字需要保留小数点后的两位,View Model中会有TrimToTwoDecimalPlaces和ConvertIntToStrings 00:32:28.201 --> 00:32:37.522 如果他们被括号括起来,或者需要取消那些由Presenter加上的括号,这也被放在 View Model里 00:32:37.596 --> 00:32:43.097 如果有一个菜单,那么每一个菜单项的名字也在 View Model里 00:32:43.191 --> 00:32:52.862 如果有一些菜单因为不可用而需要变成灰色,控制这个状态的booleans 也在View Model里 00:32:52.862 --> 00:33:05.357 任何可以显示的表示都在 View Model 数据结构里,当然是以一种抽象的形式 00:33:05.357 --> 00:33:08.167 然后,它将影响到View 00:33:08.167 --> 00:33:15.893 View是愚蠢的,它什么事情也没做,只是把View Model中的字段拿出来,放到该放的地方,Boom Boom Boom 00:33:15.893 --> 00:33:21.133 没有处理,没有if语句,也许有一个循环用来加载表,仅此而已 00:33:21.133 --> 00:33:29.606 View太愚蠢了,以至于我们通常不会担心怎么去测试它。因为就算我们直接用眼睛来检查View也无妨 00:33:29.606 --> 00:33:33.069 那么Presenter可以测试吗? 00:33:33.100 --> 00:33:38.321 你可以将Response Model的数据结构交给它。然后检查生成的View Model数据结构 00:33:38.321 --> 00:33:43.797 所以Presenter是可以测试的。你需要搭建Web服务器并运行来测试Presenter吗? 00:33:43.844 --> 00:33:48.844 不,你在没有Web服务器运行的情况下测试Presenter 00:33:48.844 --> 00:33:53.198 你不需要Spring或者其他类似的那些只有上帝才知道的容器 00:33:53.198 --> 00:33:55.956 你不需要知道任何关于它们的细节 00:33:55.956 --> 00:33:59.520 你可以像小的普通对象一样测试所有的东西 00:33:59.520 --> 00:34:06.612 顺便提一句,我们有一个目标,就是不用启动任何东西就可以进行尽可能多的测试 00:34:06.612 --> 00:34:13.095 你不需要启动一个服务器,光是启动服务器这个事情就得花上30秒到1分钟 00:34:13.095 --> 00:34:21.176 你不需要做任何那样的事情,你可以以最快的速度进行测试,就像Boom Boom Boom 00:34:23.496 --> 00:34:26.049 这就是在整个过程 00:34:26.049 --> 00:34:32.487 从Interactor开始,你可以发现他通过输入边界获得来自Resquest Model的数据 00:34:32.487 --> 00:34:37.909 通过输出边界,将数据通过Response Model 传递给Presidenter 00:34:37.909 --> 00:34:48.541 现在注意那条黑线。黑线代表应用程序的传送机制(比如Web)。 00:34:48.541 --> 00:34:54.756 注意每一个穿过黑色的箭头,它们都指向应用程序 00:34:54.756 --> 00:35:00.046 应用程序对Controler和Presenter一无所知 00:35:00.046 --> 00:35:04.828 它不知道关于Web的细节,也不知道关于I/O通道的细节 00:35:04.828 --> 00:35:10.755 I/O通道知道应用程序,但是应用程序不知道I/O通道 00:35:10.755 --> 00:35:21.160 如果你把他们放在单独的Jar包里,应用程序Jar包和Web Jar包没有任何依赖 00:35:21.160 --> 00:35:30.222 也许你会这么做,你想把它们放在独立的Jar包中,所以,你可能有一个Web的Jar包,一个应用程序的Jar包 00:35:30.222 --> 00:35:33.517 也许还有另外一个I/O传递机制的jar包 00:35:33.517 --> 00:35:38.750 那么,你替换I/O机制的方式就是替换Jar包而已 00:35:38.750 --> 00:35:50.047 想一想I/O机制作为一个插件,就像Web。你们有多少人使用.Net Sharp? 00:35:50.085 --> 00:35:54.910 你们全部都用.Net?谁用.Net? 00:35:54.910 --> 00:36:04.165 你们用什么IDE?你们用了插件吗?那么 一个插件 00:36:05.885 --> 00:36:14.954 IDE的作者和插件的作者,他们彼此了解吗? 00:36:17.049 --> 00:36:25.830 插件的作者了解IDE的作者,但是IDE的作者不了解插件的作者 00:36:25.830 --> 00:36:27.668 一点都不关心 00:36:27.716 --> 00:36:35.500 他们谁能影响谁?插件的作者是不是可以影响到Visual Studio的正常运行? 00:36:35.500 --> 00:36:42.543 有谁使用ReSharper,使用ReSharper的人和使用JerBrains的人能影响到Visual Studio吗? 00:36:42.643 --> 00:36:46.554 - (听众)是的,没错 - (罗伯特·马丁)好了,他们可以打破它。 00:36:46.554 --> 00:36:52.545 但是他们这样影响它,Visual Studio的作者需要做出响应吗? 00:36:52.545 --> 00:36:58.895 微软的软件开发者,他们会响应JetBrains吗? 00:36:58.895 --> 00:37:02.410 不,他们根本不关心JetBrains 00:37:02.483 --> 00:37:10.232 微软开发者可以强迫JetBrains的开发者响应他们吗? 00:37:10.232 --> 00:37:13.757 当然,经常这样! 00:37:13.757 --> 00:37:17.791 现在,从应用程序的角度想想这件事 00:37:17.791 --> 00:37:25.851 你的应用程序哪些部分应该得到保护,从而免受其他部分的影响 00:37:25.851 --> 00:37:34.771 哪些部分是你希望强制响应改变,哪些部分不需要强制响应改变? 00:37:34.771 --> 00:37:38.838 而答案应该是非常非常清楚的 00:37:38.838 --> 00:37:45.152 你想保护你的业务规则,不被Web的变化所影响 00:37:45.152 --> 00:37:49.706 相反,你不想再你的业务规则发生变化时,Web却被保护起来没办法改变 00:37:49.706 --> 00:37:59.528 但是,Web上的任何变化都不应该影响到你的业务规则,这就是架构能提供的保障 00:37:59.528 --> 00:38:06.710 所有的依赖都朝向应用程序,记住这一点,这既是plug-in architecture 00:38:07.680 --> 00:38:11.912 将我们来看看数据库 00:38:14.882 --> 00:38:18.116 什么是数据库? 00:38:20.396 --> 00:38:22.991 这是不是你对数据库的认识? 00:38:23.931 --> 00:38:31.846 你是不是认为数据库才是中间的上帝,周围都围绕着各种各样的小应用程序 00:38:31.846 --> 00:38:37.573 有谁的工作是DBA?这间屋子里有DBA吗? 00:38:37.573 --> 00:38:39.368 哦~我很安全,很好 00:38:39.368 --> 00:38:48.993 有人知道DBA是什么吗?将应用程序颠覆,脱离该有的模式。确保数据库才是正确的 00:38:49.066 --> 00:38:52.413 这是不是就是你心中的数据库? 00:38:52.540 --> 00:38:55.061 因为这是我的观点 00:38:55.135 --> 00:38:57.389 数据库是一个细节上的东西 00:38:57.389 --> 00:39:00.753 这不是架构中重要的东西 00:39:00.885 --> 00:39:05.017 数据库是一个装满Bits的桶而已 00:39:05.876 --> 00:39:10.112 这不是你的系统架构中最重要的东西 00:39:10.112 --> 00:39:12.791 它和业务规则没有任何关系 00:39:12.791 --> 00:39:17.263 我的天,除非你把业务规则放在存储过程里 00:39:17.263 --> 00:39:25.818 存储过程里做的事情都是查询,验证,完整性检查只是增强功能,但不是业务规则 00:39:29.898 --> 00:39:32.519 为什么我们要有数据库? 00:39:34.219 --> 00:39:40.744 数据库这种东西从哪来?为什么会有Oracle? 00:39:45.614 --> 00:39:48.768 我们所有的数据存放在哪里? 00:39:48.768 --> 00:39:53.494 我们存储在磁盘上,有人写过磁盘驱动吗? 00:39:53.494 --> 00:39:58.375 有人写过一个软件,用来控制内存的数据和磁盘交互吗? 00:39:58.456 --> 00:40:06.569 没有人写过!哦,天哪,哦 耶!磁盘驱动程序! 00:40:06.569 --> 00:40:10.454 什么?软盘?那还不够 00:40:10.454 --> 00:40:15.290 从磁盘获取数据,或者将数据存入磁盘是非常难得事情,为什么? 00:40:15.290 --> 00:40:18.183 因为数据在磁盘上的组织方式 00:40:18.261 --> 00:40:21.820 数据在磁盘上圆形轨道上的组织方式 00:40:21.820 --> 00:40:26.055 这种圆形轨道可以在布满盘面 00:40:26.055 --> 00:40:35.025 有一个磁头可以在这些轨道上来回移动,所以,你的控制磁头移动到正确的轨道上 00:40:35.066 --> 00:40:38.941 然后磁盘开始转动,你就可以读取你需要的数据了 00:40:38.941 --> 00:40:46.894 你找到你想要的扇区,一个轨道的表面大概有5,60个扇区,每个扇区可能有4K字节 00:40:46.894 --> 00:40:52.282 所以你必须等待磁盘旋转,当你的扇区到来时,你就可以读取数据了 00:40:52.282 --> 00:40:57.368 然后你进入扇区,读取你想要的字节 00:40:57.368 --> 00:41:01.134 这非常痛苦,而且速度非常慢 00:41:01.144 --> 00:41:05.141 如果你不优化,这可能会花上一辈子时间来读取或者存储数据 00:41:05.141 --> 00:41:13.866 所以我们写了一个解决这个问题的系统,我们叫他数据库 00:41:15.106 --> 00:41:17.502 可是发生了一些事情 00:41:17.502 --> 00:41:28.339 看见我的笔记本了吗?我的笔记本有512M SSD内存,没有磁盘。这对你来说应该不会惊讶 00:41:28.339 --> 00:41:30.936 这间屋子有人还在用硬盘吗? 00:41:30.936 --> 00:41:34.012 这间屋子里有人用旋转的那种硬盘吗? 00:41:34.012 --> 00:41:37.070 哦 我的天,这是真的吗? 00:41:37.070 --> 00:41:43.249 嗯,大家都知道,现在已经没有人再去考虑旋转的那种磁盘了,我们考虑的都是SSD 00:41:43.249 --> 00:41:50.126 哦,我们想想服务器机房里,也许还有那种硬盘,不过它们始终会被淘汰 00:41:51.019 --> 00:41:55.998 放眼未来几年,那种硬盘开始消失,所有的都被RAM取代 00:41:56.081 --> 00:41:59.719 我说的是RAM? 00:41:59.719 --> 00:42:04.421 RAM直接在字节级寻址 00:42:04.498 --> 00:42:13.672 我们正在使用直接寻址的内存几乎无限量也是持久的。 00:42:13.672 --> 00:42:17.634 这就是我们将要存储数据的地方 00:42:17.634 --> 00:42:27.902 如果这就是我们要存储数据的地方,为什么还要像在地狱般一样使用SQL来访问它们呢? 00:42:27.902 --> 00:42:32.101 SQL是痛苦的,Table也是苦痛的 00:42:32.101 --> 00:42:36.937 在你打算用哈希表来查询事情的时候难道不是跟随周围的人一样? 00:42:36.937 --> 00:42:44.662 那就是你所做的吗?你把所所有的table读入内存里,然后把数据组织的更好,你真的可以使用它吗? 00:42:44.662 --> 00:42:49.298 如果我们只是把它放在我们想使用的格式? 00:42:49.298 --> 00:42:52.746 如果我是Oracle我会被吓死。 00:42:52.801 --> 00:42:57.280 因为我存在的理由彻底消失了 00:42:57.280 --> 00:43:02.567 大数据库系统的概念已经开始消失了 00:43:05.317 --> 00:43:10.279 我们应该怎么样保护我们的应用程序不受这种细节的影响? 00:43:10.279 --> 00:43:19.105 数据库这个细节往往主宰了一切,但是我可以保护我们的应用程序就像保护它们不受Web的影响一样 00:43:19.105 --> 00:43:23.129 我画了另一条可爱的黑线在那 00:43:23.198 --> 00:43:30.444 我确保所有的穿过这条线的依赖都指向应用程序里面 00:43:30.444 --> 00:43:35.554 我把数据库做成了一个应用程序的插件 00:43:35.554 --> 00:43:38.924 那么,我就可以切换使用Oracle还是MySQL 00:43:38.924 --> 00:43:42.706 又或者我可以放弃MySQL从而使用CouchDB 00:43:42.706 --> 00:43:47.465 又或者我可以放弃CouchDB从而使用CouchDB或者别的我想用的 00:43:47.465 --> 00:43:50.248 我可以像插件一样使用数据库 00:43:50.248 --> 00:43:57.116 也许你永远都不会更换数据库,但是会的话会更好,即使你没必要这么做。 00:43:57.116 --> 00:44:01.082 我们怎么实现呢?好吧,相当直接了当,在那里还有另外一个接口 00:44:01.082 --> 00:44:03.062 我在这里把它叫做Entity Gateway 00:44:03.102 --> 00:44:10.159 Gateway中有对应的每一个Entity的查询方法 00:44:10.159 --> 00:44:14.247 他提供一个功能一个函数,让你你要查询任何你想要的东西, 00:44:14.247 --> 00:44:19.247 你在黑线以下的Entity Gateway Implementation 实现具体的方法 00:44:19.247 --> 00:44:23.128 这个实现可以是数据库,或者其他什么,管他的 00:44:23.128 --> 00:44:29.917 注意,没有任何数据库的管理部分渗透到了应用程序中 00:44:29.917 --> 00:44:39.838 从Entity Object来的地方。Entity Object 有可能被数据库以某种疯狂的方式取出,并映射到Table里 00:44:39.838 --> 00:44:44.115 Entity Gateway 的实现会汇集数据 00:44:44.115 --> 00:44:47.673 创建Entity Object,然后将他们送过黑线 00:44:47.673 --> 00:44:53.114 因此,一旦它们穿越黑线之后,它们就是真正的Entity Object了 00:44:55.164 --> 00:45:00.283 有人用过类似Hibernate的东西吗?或者ORM工具? 00:45:00.283 --> 00:45:02.045 谁在使用类似的东西? 00:45:02.066 --> 00:45:06.983 它们在这个图里面的哪个位置呢? 00:45:06.983 --> 00:45:10.485 在具体实现里,黑线的下面 00:45:10.485 --> 00:45:16.176 黑线以上对ORM工具的任何部分都不了解 00:45:16.176 --> 00:45:21.664 你在Business Object中使用那些有趣的小的注解或者属性吗?让他们从你的Business Object滚开 00:45:21.664 --> 00:45:25.465 你不会想要让你的Business Object知道他们是由Hibernatez创建的 00:45:25.465 --> 00:45:31.817 让它被那些已经被数据库污染了得来自黑线下面的东西创建 00:45:31.831 --> 00:45:36.131 保持你的Business Object 纯洁,为什么? 00:45:37.741 --> 00:45:40.556 一个对象是什么? 00:45:43.106 --> 00:45:45.360 我为这个问题花了很多时间 00:45:46.610 --> 00:45:54.363 什么是对象,一个对象就是一组公共方法的集合 00:45:54.363 --> 00:46:00.727 你想知道其他的东西?这是不允许的,不是吗? 00:46:00.727 --> 00:46:04.792 那里可能有数据,但是你不能看到它,这些都是私有的,不是吗? 00:46:04.792 --> 00:46:11.459 从你的角度来看,Object是一堆方法,不是一堆数据 00:46:11.459 --> 00:46:13.618 既然Object是一堆方法 00:46:13.618 --> 00:46:17.075 那么Obejct就是和行为有关的 00:46:17.075 --> 00:46:20.863 一个Object是和业务规则有关的 00:46:20.863 --> 00:46:23.213 它不是和数据有关的 00:46:23.213 --> 00:46:26.228 我们估计那里面有数据,但是我们不知道在哪 00:46:26.228 --> 00:46:29.990 我们也不知道是什么格式,我们也不想知道 00:46:29.990 --> 00:46:32.973 什么是数据结构? 00:46:32.973 --> 00:46:40.606 一个数据结构是一组已知的数据元素,公开的,大家都能看到 00:46:40.606 --> 00:46:42.761 并且,里面没有任何方法 00:46:42.761 --> 00:46:44.779 数据结构没有任何功能 00:46:44.779 --> 00:46:47.263 对象和数据结构正好是相反的 00:46:47.263 --> 00:46:54.612 数据结构有明显的数据,但没有方法,对象有明显的功能,但没有明显的数据 00:46:54.612 --> 00:46:56.112 正好相反 00:46:56.112 --> 00:47:00.133 没有任何东西能这样,比如ORM 00:47:00.175 --> 00:47:03.339 对象关系映射?不能做到 00:47:03.411 --> 00:47:10.222 因为从数据库得到的是一种数据结构,你不能把一个数据结构映射到一个对象 00:47:10.268 --> 00:47:12.452 因为他们根本就是不同的两个东西 00:47:12.452 --> 00:47:19.613 实际上这只是Entity所需要的数据被存放在什么地方了,上帝才知道在哪 00:47:19.613 --> 00:47:26.614 然后不知道怎么神奇的传递到Entity那,又不知道怎么神奇的开始使用它。我不在乎 00:47:26.615 --> 00:47:31.151 这些Entity不会有Hibernate创建 00:47:31.151 --> 00:47:38.006 也许这些Entity使用了一些由Hibernate创建的数据结构,但我不在乎 00:47:38.006 --> 00:47:45.239 我不想任何Hibernate的功能或者其他框架的功能穿过这条黑线 00:47:45.239 --> 00:47:48.358 黑线以下的东西可以被污染 00:47:48.358 --> 00:47:54.559 黑线以上的东西都是我的宝贝,我要保护他们不受影响 00:47:54.559 --> 00:48:02.572 这些都是我的业务规则,我不会让我的业务规则受到框架的污染 00:48:02.572 --> 00:48:09.793 框架。我们都喜欢使用框架,我们觉得它们很酷 00:48:09.793 --> 00:48:14.859 我们认为框架做的事情确实为我们节省了不少时间 00:48:14.859 --> 00:48:21.820 但框架的作者引诱我们绑定到他们那 00:48:21.820 --> 00:48:26.278 他们为我们提供的父类让我们继承 00:48:26.278 --> 00:48:32.477 当你从一个父类继承时,你已经嫁给了那个类 00:48:32.477 --> 00:48:40.045 你将自己的狠狠的绑定在了父类上,没有比继承更强的关系了 00:48:40.045 --> 00:48:47.430 因此,通过从别人的父类派生,你给了它们一个巨大的许诺 00:48:47.493 --> 00:48:52.156 但是,作为他们来说,没有做任何一种对你的许诺 00:48:52.156 --> 00:48:54.684 所以这是一种不对称关系 00:48:54.684 --> 00:49:02.244 框架的作者从你的承诺中获得好处,但是框架的作者却不对你做出任何承诺 00:49:02.305 --> 00:49:06.524 我让你自己去评估一下其中的利弊 00:49:06.524 --> 00:49:12.336 聪明的架构师不会去做这种绑定 00:49:12.336 --> 00:49:20.705 聪明的架构师对框架不屑一顾,它们认为框架会绑架我 00:49:20.705 --> 00:49:25.605 这个框架希望我绑定到它那,但我想我不会这么做 00:49:25.605 --> 00:49:30.985 我想我应该在我的业务规则与框架之间设置一个边界 00:49:30.985 --> 00:49:39.477 那么我的业务规则永远不会依赖于Spring或者hibernate或者其他什么只有上帝才知道的东西 00:49:39.477 --> 00:49:43.966 我会去使用框架,并且我会小心的使用它们 00:49:43.966 --> 00:49:49.822 因为框架的作者对于我最感兴趣的部分一点都不在乎 00:49:55.672 --> 00:50:03.334 很久以前,我和儿子还有其他几个人写了一个叫做FitNess的工具 00:50:03.334 --> 00:50:06.533 有人在使用FitNesse吗?哦,大部分都用了,很好 00:50:06.533 --> 00:50:13.149 FitNesse是用于编写客户验收测试的工具 00:50:13.188 --> 00:50:17.576 它是基于Wiki的一个东西,这就是你确切该知道的所有事情 00:50:17.576 --> 00:50:19.397 谁发明的Wiki 00:50:19.437 --> 00:50:22.483 沃德·坎宁安。谁是沃德·坎宁安? 00:50:22.483 --> 00:50:26.765 创造Wiki的人,啊,他的事迹不止这些 00:50:26.765 --> 00:50:35.256 沃德·坎宁安是大师中的大师。所有的大师都知道沃德·坎宁安是谁 00:50:35.256 --> 00:50:39.995 那些到处去演讲的人,比如我,我们都知道沃德·坎宁安是谁 00:50:40.028 --> 00:50:42.006 我们仰望他 00:50:42.006 --> 00:50:46.424 他是那个打电话给埃里克·伽马的人 00:50:46.424 --> 00:50:49.760 他说:"你知道吗埃里克,你得写一本书叫做设计模式。" 00:50:49.800 --> 00:50:58.122 他也辅导过肯·贝克,传授一些东西比如结对编程,测试驱动开发和敏捷开发 00:50:58.122 --> 00:51:06.937 如果你知道很多关于软件业的历史,你就会知道他。你会发现:“哇~哪里都有他的身影” 00:51:06.937 --> 00:51:12.160 FitNesse是基于沃德的两项发明,Wiki和Fit 00:51:12.204 --> 00:51:16.212 我不打算描述Fit。不过我会描述Wiki 00:51:16.212 --> 00:51:24.896 大约12或者13年前,我和我的儿子还有其他一些人觉得用java来写这一个类似Wiki的东西 00:51:24.896 --> 00:51:27.807 我知道我们想要一个Wiki 00:51:27.807 --> 00:51:34.958 所以我们这么想:“好了,我们有一个地方来存储网页,让我存储他们在数据库里吧,我们该用什么样的数据库呢?” 00:51:34.992 --> 00:51:41.925 嗯,在那些日子里,只有MySQL是开源数据库,所以我们觉得把一切都放在MySQL中 00:51:43.245 --> 00:51:50.880 于是我们准备去搭建MySQL环境,建立一套规则,这时有人说 00:51:50.880 --> 00:51:53.550 “你明白,其实我们不需要马上这么做” 00:51:53.550 --> 00:51:58.793 ”我的意思是,我们以后会这么做,但不是现在。因为我们可以先处理另外一个问题“ 00:51:58.793 --> 00:52:03.312 这个问题就是怎么把Wiki文本翻译成HTML,这才是Wiki该做的事情 00:52:03.382 --> 00:52:07.185 把你的输入的有趣内容放到Wiki里,然后把他转换成HTML 00:52:07.251 --> 00:52:10.156 我们需要做很多翻译的事情 00:52:10.156 --> 00:52:14.258 于是接下来的3个月 ,我们把数据库彻底遗忘了 00:52:14.258 --> 00:52:21.379 我们把Wiki文本翻译成HTML,我们需要一个叫做WikiPage的对象 00:52:21.414 --> 00:52:22.968 你可以在这里看到 00:52:22.968 --> 00:52:29.995 我们创建一个名为WikiPage的抽象类,我们有一个叫做MockWikiPage的实现 00:52:29.995 --> 00:52:37.446 WikiPage的方法里有一些很想数据库功能的方法,比如Load和Save 00:52:37.490 --> 00:52:40.028 但是它们没有被实现,它们没做任何事情 00:52:40.028 --> 00:52:43.364 3个月以来我们都这么干 00:52:43.364 --> 00:52:49.177 当我们把所有翻译工作都完成的时,我们说:“嗯,是时候搭建数据库了” 00:52:49.177 --> 00:52:52.039 “因为现在我们确实需要把这些页面存储在实际的地方了” 00:52:52.070 --> 00:52:54.670 又有人说:“嗯,其实我们不需要那样做” 00:52:54.670 --> 00:53:00.006 “因为我们可以替换成用哈希表在RAM中存储这些页面” 00:53:00.056 --> 00:53:02.590 “我的意思是,我们并不需要真正的把他们存储在硬盘上,不是吗?” 00:53:02.590 --> 00:53:06.356 这个当然是肯定的,因为所有我们该做的事情反正只是编写单元测试 00:53:06.356 --> 00:53:14.871 因此,我们决定创建名为InMemoryPage的WikiPage,其中存储的实现是用哈希表完成的 00:53:14.871 --> 00:53:17.436 然后我们继续工作了一年 00:53:17.786 --> 00:53:21.611 继续编写更多的FitNesse,并且把所有的数据都放在内存里 00:53:21.611 --> 00:53:24.294 事实上,我们让所有的FitNesse都正常工作了 00:53:24.744 --> 00:53:27.280 而且并没有把他们任何一个放入磁盘里 00:53:27.332 --> 00:53:32.169 这非常酷,因为没有数据库的所有的测试都非常快 00:53:32.169 --> 00:53:38.712 但是另一方面它也让人失望,因为我们创建的这一大堆测试再计算机关闭后都消失了 00:53:38.712 --> 00:53:44.721 所以这个时候我们终于说:“现在好了,我们需要数据库,让我们搭建MySQL吧” 00:53:44.721 --> 00:53:46.664 那时候 Michael Feathers在那 00:53:46.664 --> 00:53:50.274 他说:“嗯,你还是没有必要搭建MySQL呢” 00:53:50.350 --> 00:53:58.620 “你真的需要的是持久化,而你可以采取将哈希表写入到文件的方式来实现,这也更容易” 00:53:59.260 --> 00:54:06.860 我们认为这是一种丑陋的实现,但工作初期它暂时可以运行。以后我们再把他换成MySQL,因此我们这么做了 00:54:06.860 --> 00:54:11.112 又是3个月的时间,我们继续开发了越来越多的fitnesse 00:54:11.112 --> 00:54:16.374 那非常酷,因为我们可以在带着他出门,向别人展示。我们可以保持网页 00:54:16.426 --> 00:54:18.278 像真的Wiki一样开始运行 00:54:18.551 --> 00:54:27.959 3个月以后,我们说:“我们不需要数据库” 00:54:28.539 --> 00:54:31.807 “它的工作很正常,文件的速度已经足够快了” 00:54:31.879 --> 00:54:33.691 “这种方式运行得很好” 00:54:33.732 --> 00:54:38.884 我们选择的架构中更重要的部分,并且把数据库仍到了地球的另一端 00:54:38.884 --> 00:54:43.799 我们从来没有使用数据库,虽然那确实不是很正确 00:54:44.249 --> 00:54:49.745 我们的一位客户说:“我得在数据库中存储数据” 00:54:49.745 --> 00:54:52.371 我们说:"为什么?他在文件上工作的一样很好" 00:54:52.495 --> 00:54:59.032 他说:“公司的规定,公司所有的资产都必须在数据库中” 00:54:59.032 --> 00:55:04.115 我不知道谁和他们这么说的,但是这些数据库推销员确实很有说服力 00:55:04.415 --> 00:55:11.854 所以,我们说:“好吧,你看,如果你真的需要在数据库存储它” 00:55:13.624 --> 00:55:24.208 “这就是这种结构,你要做的就是创建一个MySQL Page的子类,然后一切都会运行的很好” 00:55:24.251 --> 00:55:27.162 一天以后他再来的时候,所有的东西都通过MySQL存储了 00:55:27.162 --> 00:55:31.468 我们把它当作一个插件来使用,但是没有人这么用,所以我们把它停止了 00:55:32.018 --> 00:55:37.936 这就是一个我们推迟了无数遍的架构中的决定 00:55:38.003 --> 00:55:41.034 我们推迟直到项目结束,我们也没这么做 00:55:41.034 --> 00:55:45.352 那些我们想放在最开始做的事情,却最终没有做 00:55:45.352 --> 00:55:49.120 这告诉我们终极原则 00:55:50.619 --> 00:56:00.209 一个良好的架构是允许架构中重要决定被推迟的,延期的 00:56:00.209 --> 00:56:05.107 架构师的目标不是做决定 00:56:05.147 --> 00:56:12.618 尽可能长的推迟这些事情,那么到最后你将有更多的选择来做决定 00:56:12.618 --> 00:56:21.953 你的架构设计,你的代码的高层目录结构,这些高层次的决策都可以推迟了 00:56:21.953 --> 00:56:28.929 别告诉我,你的应用程序的架构是一堆框架 00:56:28.970 --> 00:56:35.568 “你们应用程序的架构是什么?我们使用的SQL服务器和MVVM和等等等框架” 00:56:35.568 --> 00:56:37.347 你没有告诉我任何事情 00:56:37.347 --> 00:56:40.744 你只是告诉我工具而已 00:56:40.744 --> 00:56:43.011 这不是你的应用程序的架构 00:56:43.075 --> 00:56:46.104 你的应用程序的架构是那些用例 00:56:46.104 --> 00:56:50.460 而你并不希望用例依赖这些工具 00:56:50.460 --> 00:56:54.847 你想把工具的使用推迟的越晚越好 00:56:54.847 --> 00:57:02.270 你应该让整个应用程序在没有数据库没有Web的情况也可以运行 00:57:02.270 --> 00:57:07.483 也许你会搭建一个简单的小型的Web,然后你可以使用个一两天 00:57:07.483 --> 00:57:12.177 那样,你不需要搭建巨大的框架就可以看到一些网页 00:57:12.177 --> 00:57:17.485 也许你搭建了一些简陋的类似数据库的东西,来让持久化的工作开始运行 00:57:17.485 --> 00:57:23.627 这样你就不需要Oracle的许可来运行持久化的工作了。 00:57:25.347 --> 00:57:34.497 下次你再编写应用程序的时候,想一想什么是可以推迟的事情 00:57:37.757 --> 00:57:48.049 系统中的应用程序应该使用插件架构,系统中的架构应该是插件架构 00:57:48.083 --> 00:57:55.076 UI,数据库,框架的细节都以插件的形式接入用例 00:57:55.076 --> 00:57:57.959 哪些才是应用程序的核心? 00:57:59.809 --> 00:58:03.990 当然现在的客户希望看到网页 00:58:03.990 --> 00:58:10.463 好吧,你依然可以构建一个插件架构,然后让他们看见网页在运行 00:58:10.514 --> 00:58:13.339 你不需要向框架承诺太多 00:58:13.339 --> 00:58:16.647 你不需要向Web框架承诺太多 00:58:16.647 --> 00:58:19.084 你可以先使用一些简单的东西来代替 00:58:19.084 --> 00:58:25.807 或者如果你真的想使用一个真正的框架,请吧,但请保持插件架构 00:58:25.807 --> 00:58:29.285 那么在某个时候,你可以轻易的卸载它,并使用其他的来代替 00:58:31.245 --> 00:58:35.177 这可能就是我最后要谈论的东西了 00:58:35.857 --> 00:58:39.417 我们先前已经讨论过这个问题(TDD) 00:58:41.737 --> 00:58:45.463 所以非常感谢大家,有什么问题吗? 00:58:47.635 --> 00:58:50.950 有点看不清楚,那么我要站过来一点 00:58:54.550 --> 00:58:56.664 哦,这一点用也没有 00:58:57.464 --> 00:59:01.372 好吧,有人有什么问题吗? 00:59:01.372 --> 00:59:04.449 你不得不喜欢空洞。因为我无法看到你的手。 00:59:04.449 --> 00:59:05.236 没错。 00:59:05.236 --> 00:59:11.963 (有人问一个问题) 00:59:11.963 --> 00:59:21.120 那么我猜测Oracle和关系数据库会灭亡。那么我建议用了什么来将取代他们? 00:59:21.151 --> 00:59:24.337 多么有趣的一个问题 00:59:24.337 --> 00:59:30.947 那必定是RAM了,还有什么可以取代他妈? 00:59:30.947 --> 00:59:36.511 如果你要组织数据在RAM中,如果RAM是持久性的,你还需要什么呢? 00:59:36.562 --> 00:59:38.857 哦,但让我们说你需要的东西。 00:59:38.857 --> 00:59:41.488 好吧什么可能是什么样子的? 00:59:42.458 --> 00:59:45.557 嘛,谁听说过CQRS的? 00:59:46.437 --> 00:59:47.719 噢,你们几个。 00:59:47.719 --> 00:59:49.933 CQRS,多么有趣的想法? 00:59:49.957 --> 00:59:54.999 如果我们有高速内存无限量。高速RAM。 00:59:54.999 --> 00:59:58.390 或者,也许它甚至磁盘,但谁在乎。 00:59:58.390 --> 01:00:02.369 我们为什么要存储什么状态。 01:00:02.369 --> 01:00:05.195 为什么不我们只是简单地存储交易? 01:00:05.195 --> 01:00:12.043 而不是存储银行账户的,为什么不我们存储使我们创造了银行账户的交易。 01:00:12.043 --> 01:00:19.295 01:00:19.295 --> 01:00:23.381 01:00:23.381 --> 01:00:25.870 01:00:25.870 --> 01:00:28.512 01:00:28.512 --> 01:00:35.131 01:00:40.331 --> 01:00:44.299 01:00:44.299 --> 01:00:46.264 01:00:46.296 --> 01:00:53.412 01:00:53.412 --> 01:00:55.640 01:00:55.709 --> 01:00:59.436 01:00:59.436 --> 01:01:04.702 01:01:04.702 --> 01:01:08.752 01:01:08.752 --> 01:01:13.568 01:01:13.568 --> 01:01:17.292 01:01:17.292 --> 01:01:20.151 01:01:20.151 --> 01:01:25.436 01:01:25.468 --> 01:01:27.139 01:01:27.139 --> 01:01:31.633 01:01:32.703 --> 01:01:35.381 01:01:35.381 --> 01:01:37.632 01:01:37.632 --> 01:01:40.162 01:01:40.162 --> 01:01:45.731 01:01:45.778 --> 01:01:51.519 01:01:51.583 --> 01:01:53.852 01:01:53.852 --> 01:01:58.568 01:02:00.218 --> 01:02:02.967 01:02:02.967 --> 01:02:04.775 01:02:07.625 --> 01:02:09.379 01:02:09.379 --> 01:02:12.216 01:02:15.066 --> 01:02:27.373 01:02:27.373 --> 01:02:31.933 01:02:33.923 --> 01:02:42.383 01:02:43.633 --> 01:02:47.074 01:02:47.074 --> 01:02:49.872 01:02:49.872 --> 01:02:52.603 01:02:52.603 --> 01:02:56.085 01:02:56.085 --> 01:03:02.232 01:03:02.232 --> 01:03:06.887 01:03:06.887 --> 01:03:09.262 01:03:09.752 --> 01:03:13.585 01:03:13.585 --> 01:03:22.817 01:03:23.467 --> 01:03:31.163 01:03:31.206 --> 01:03:36.086 01:03:36.086 --> 01:03:43.321 01:03:43.321 --> 01:03:44.936 01:03:44.936 --> 01:03:53.693 01:03:55.893 --> 01:03:59.233 01:03:59.233 --> 01:04:03.986 01:04:04.031 --> 01:04:05.521 01:04:05.561 --> 01:04:09.075 01:04:09.075 --> 01:04:12.391 01:04:12.441 --> 01:04:17.674 01:04:18.904 --> 01:04:20.818 01:04:21.862 --> 01:04:24.136 01:04:24.136 --> 01:04:29.320 01:04:29.320 --> 01:04:31.535 01:04:31.535 --> 01:04:34.765 01:04:36.205 --> 01:04:40.168 01:04:40.261 --> 01:04:41.959 01:04:42.249 --> 01:04:48.166 01:04:48.198 --> 01:04:50.189 01:04:50.759 --> 01:04:56.185 01:04:56.185 --> 01:04:58.463 01:04:58.463 --> 01:05:01.495 01:05:01.495 --> 01:05:04.145 01:05:04.145 --> 01:05:08.029 01:05:08.029 --> 01:05:12.065 01:05:12.065 --> 01:05:17.045 01:05:17.045 --> 01:05:20.421 01:05:20.421 --> 01:05:23.928 01:05:23.928 --> 01:05:27.915 01:05:28.022 --> 01:05:32.956 99:59:59.999 --> 99:59:59.999