1 00:00:00,000 --> 00:04:43,738 (演讲从 4:45 开始) 2 00:04:46,478 --> 00:04:50,376 好吧,你们都能听到我吗? 3 00:04:50,785 --> 00:04:56,304 这种备用的方式能听到我吗?好的,不错不错不错。 4 00:04:56,401 --> 00:05:00,828 嗯...什么是电子? 5 00:05:04,013 --> 00:05:07,984 那么我想每个人都知道,它是原子的一部分 6 00:05:08,290 --> 00:05:17,718 嗯...(从其他房间的尖叫声)是啊,我是这么认为的。我认为这是严肃的(不会翻译)?? 7 00:05:19,734 --> 00:05:22,415 电子是原子的一部分 8 00:05:22,604 --> 00:05:32,113 它携带单位负电荷,对立于带有正电荷的质子。 9 00:05:32,230 --> 00:05:42,408 它显然也有质量,但是却非常小,成千上万的电子或类似的东西。质量非常非常小 10 00:05:42,922 --> 00:05:55,041 我们认为它是一个粒子,但是这是误导。电子的实际行为更像是一个比波就像一个粒子 11 00:05:55,041 --> 00:05:58,778 那为什么我们认为它是粒子? 12 00:05:58,780 --> 00:06:08,906 ,我们这样做的原因是,当一个电子相互作用,在它就像一个粒子那样在一个位置相互作用 13 00:06:09,151 --> 00:06:16,302 但当电子通过空间移动时它不会像粒子那样移动 14 00:06:16,418 --> 00:06:21,498 它像波浪一样移动,并且波浪移动式没有具体位置的 15 00:06:21,498 --> 00:06:27,334 比如大海的波浪,它可以变得巨大 16 00:06:27,334 --> 00:06:33,819 它有明确的能量,但没有明显的位置 17 00:06:33,819 --> 00:06:36,450 这就是电子如何移动的 18 00:06:36,450 --> 00:06:42,191 电子遵守一个原则,就是mistruster原则 19 00:06:42,191 --> 00:06:45,566 它被称为泡利不相容原理 20 00:06:45,566 --> 00:06:53,202 两个电子绑定到同一个的系统不会有相同的状态 21 00:06:53,202 --> 00:06:57,328 无论出于何种原因,他们不能有相同的状态 22 00:06:57,328 --> 00:07:00,677 因此,如果有两个电子中的原子 23 00:07:00,677 --> 00:07:04,066 那两只电子一定是不同的 24 00:07:04,066 --> 00:07:07,709 现在,他们可能以不同的方向旋转 25 00:07:07,709 --> 00:07:10,899 电子可以向左旋转,也可以向右旋转 26 00:07:10,899 --> 00:07:18,355 两个电子具有完全相同的能量可以结合在一起,一个向左转,一个向右转 27 00:07:18,355 --> 00:07:21,442 有没有办法让第三个电子也在那里? 28 00:07:21,450 --> 00:07:28,412 所以下一电子必须具有更高的能量,那么你可以另外有两个 29 00:07:28,412 --> 00:07:30,806 一个向左边旋转,一个向右边旋转 30 00:07:30,806 --> 00:07:34,769 但是如果你想增加另一个,那将是不同的东西 31 00:07:34,769 --> 00:07:37,917 而你可以在不同的空间安排他们 32 00:07:37,985 --> 00:07:48,589 所以,你可以有一组的两个这样的,另一组两个这样的,另一组两个这样,一组的两个球体的这汇总方式增加了八个。 33 00:07:49,633 --> 00:07:57,677 我们所知道的大部分原子都有8个电子在它们的壳中 34 00:07:57,690 --> 00:08:07,827 原因是眼前这个可爱的小几何形状。球体中三行每行可以包含两个 35 00:08:07,827 --> 00:08:12,868 你有没有想过水? 36 00:08:12,868 --> 00:08:16,880 我这里就有一些。迷人的物质 37 00:08:16,880 --> 00:08:19,898 它是什么做的? 38 00:08:20,484 --> 00:08:25,082 两个氢原子一个氧原子构成一个水分子 39 00:08:25,472 --> 00:08:28,076 它是什么形状? 40 00:08:28,076 --> 00:08:33,482 米老鼠!是的,中间一个大的氧原子和旁边两个像耳朵一样的氢原子 41 00:08:33,482 --> 00:08:42,437 它们的角度是外壳安排的结果 42 00:08:42,437 --> 00:08:46,772 是什么使氢气和氧气粘在一起? 43 00:08:46,772 --> 00:08:51,162 想想两个原子,上面覆盖着电子,两个电子互相平行 44 00:08:51,162 --> 00:08:56,287 是什么让两个氢原子和一个氧粘在一起? 45 00:08:56,287 --> 00:09:03,380 而事实证明,如果你把这些两个氢原子和氧放在一起 46 00:09:03,380 --> 00:09:12,156 那么你想想电子的波浪,而不是粒子,那些波浪会想去哪里呢? 47 00:09:12,156 --> 00:09:20,400 那些波浪都想在氢原子的质子和氧原子的质子之间 48 00:09:20,400 --> 00:09:25,099 因此,他们聚集电子的波浪会越集越多 49 00:09:25,099 --> 00:09:30,861 两个原子之间,实际上是三个,然后再是其他地方 50 00:09:30,861 --> 00:09:34,964 它们还是会去其他地方,只是这更可能是质子之间 51 00:09:34,964 --> 00:09:47,718 这意味着原子之间有额外的负电荷,并且负电荷还要跟踪质子,这就是共价键 52 00:09:47,718 --> 00:09:54,029 现在,如果你把这个米老鼠原子并且一分为二 53 00:09:54,136 --> 00:09:57,675 你发现有更多的负电荷以上的一个和下面的一个 54 00:09:57,675 --> 00:10:02,000 原因是全部负电荷都喜欢坐在两个氢原子和氧原子之间 55 00:10:02,000 --> 00:10:08,075 因此,有比另一侧更多的负电荷,这意味着这是水分子的偶极 56 00:10:08,075 --> 00:10:10,676 它一面是负极,一面是正极 57 00:10:10,676 --> 00:10:12,702 这就是为什么水是湿的。 58 00:10:12,702 --> 00:10:20,463 水粘到你的手,因为所有的水分子旋转,粘到你的皮肤上带电的表面 59 00:10:20,463 --> 00:10:22,614 即使它不是很带电 60 00:10:22,614 --> 00:10:33,736 这就是为什么水会是良好的溶剂,因为小的水分子会旋转吸引他们想要的粘上其它分子的部分 61 00:10:33,772 --> 00:10:37,394 这就是为什么水使它成为一个很好的清洁剂 62 00:10:37,394 --> 00:10:42,888 这就是你为什么可以用水来做最美好的事情(清洗?)。谁是做过这样的实验? 63 00:10:42,888 --> 00:10:51,704 你找到一个水龙头。然后你打开一点非常小的水流。不是很快,水只是涓涓溪流 64 00:10:51,704 --> 00:11:02,317 然后你拿一个气球,在你的头发上擦一擦。然后拿着气球慢慢接近水流,水会朝着气球弯曲 65 00:11:02,317 --> 00:11:06,252 正如所有的水分子转身把它跟踪的电荷 66 00:11:06,252 --> 00:11:09,585 当然,这不是我们应该谈论的 67 00:11:10,815 --> 00:11:19,975 这次演讲的题目叫做“架构----逝去的年华”。这个演讲我已经做了好几年。 68 00:11:20,057 --> 00:11:28,490 这是关于干净的设计简洁的代码后的下一个步骤。 69 00:11:28,535 --> 00:11:33,876 这是关于干净的系统结构 70 00:11:33,876 --> 00:11:38,162 这从那开始, 71 00:11:38,162 --> 00:11:47,069 这是一个十年前左右我写的应用程序的目录结构。 72 00:11:47,069 --> 00:11:53,089 那时我正在研究的Rails。有人是Ruby on Rails的程吗序员? 73 00:11:53,089 --> 00:11:56,906 在那边有一个,他在挥舞他的手。很好。 74 00:11:57,047 --> 00:12:04,404 我当时学习的Rails。我写了这个应用程序 75 00:12:04,404 --> 00:12:09,735 它是我跟着书写的。你得到的书籍了。你按照书的提示 76 00:12:09,767 --> 00:12:13,839 我按照书上的所有建议完成了它。 77 00:12:13,839 --> 00:12:20,077 对此我很高兴。然后我很长时间没有去管他 78 00:12:20,140 --> 00:12:27,376 然后大约三年前,我的儿子在我的要求下,写了一个应用程序 79 00:12:27,376 --> 00:12:32,325 然后我看着那个程序,我看到那个目录结构。 80 00:12:32,325 --> 00:12:36,174 一模一样的目录结构 81 00:12:36,174 --> 00:12:40,573 这是两个完全不同的应用程序。它们之间一点关系也没有 82 00:12:40,614 --> 00:12:45,121 可是他们有相同的目录结构。我看着它开始思考。 83 00:12:45,121 --> 00:12:52,868 为什么这两个应用程序具有相同的高层次目录结构? 84 00:12:52,868 --> 00:12:56,646 它们是两个完全不同的应用程序。 85 00:12:56,646 --> 00:13:02,958 我认为他们有相同的目录结构,是因为他们都是Rails应用程序。 86 00:13:02,958 --> 00:13:09,304 我又问我自己,好的,但为什么那么重要? 87 00:13:09,304 --> 00:13:20,673 为什么Rails或者其他框架这么重要,它会占据应用程序的高层次目录结构主导地位? 88 00:13:20,726 --> 00:13:25,183 而我之所以对自己的资产问题是因为这个。 89 00:13:25,986 --> 00:13:34,191 Web是一个传递机制,是一个I / O通道 90 00:13:34,191 --> 00:13:37,913 Web不是架构设计中的重点 91 00:13:37,980 --> 00:13:42,263 我们认为自己是编写Web应用程序。 92 00:13:42,263 --> 00:13:44,786 其实我们不是编写Web应用程序。 93 00:13:44,786 --> 00:13:51,940 我们正在编写一种通过I/O通道(我们熟知的Web)传递它们的内容的应用程序 94 00:13:51,940 --> 00:13:57,675 那为什么应该是I / O通道主宰我们? 95 00:13:59,785 --> 00:14:06,705 有谁还记得当Web变得重要起来?1980年?1990年? 96 00:14:06,705 --> 00:14:13,833 有谁还记得那是一个怎样的变化?如何从根本上让一切变得不同? 97 00:14:13,833 --> 00:14:19,872 除非它不是,因为我们没有真正在做什么新的东西? 98 00:14:19,872 --> 00:14:26,511 我们只是从输入源将输入汇集起来,处理它,将它扔向一个输出源 99 00:14:26,573 --> 00:14:28,374 这就是Web的所有 100 00:14:28,374 --> 00:14:31,082 为什么Web会主宰这么多? 101 00:14:31,082 --> 00:14:39,189 于是我开始思考架构。我开始寻找蓝图 102 00:14:40,649 --> 00:14:49,599 这里有一张蓝图。如果那上面没有写着”library“这个单词。不用多久,你也会弄清楚这是一个图书馆 103 00:14:49,599 --> 00:14:51,729 因为它显然是一个图书馆。 104 00:14:51,805 --> 00:14:55,882 那里有一堆书架。那里有一堆读书用的桌子。 105 00:14:55,882 --> 00:15:05,705 桌子上有一些电脑。还有的地方,那里有可以借阅或归还图书的前台 106 00:15:05,705 --> 00:15:11,167 这不会花太长时间让你弄清楚去:“嗯..这一定是这样一个图书馆。“ 107 00:15:11,167 --> 00:15:15,704 这里还有另外一个。这是一个教堂。这显然是一个教堂。 108 00:15:15,704 --> 00:15:20,999 哦,你可能会误以为这是一个剧场。剧院和教会确实有一定的相似的地方。 109 00:15:20,999 --> 00:15:29,736 但是,没有,这绝对是一个教堂。PUSE,祭坛,在外面教室,前面围绕着的祷告区 110 00:15:29,736 --> 00:15:31,703 这显然是一个教堂。 111 00:15:31,703 --> 00:15:42,315 建筑的架构不会告诉你它们是如何构建的。而是告诉你它们是什么。 112 00:15:42,315 --> 00:15:49,020 他们的意图。架构就是关于意图的。 113 00:15:49,020 --> 00:15:56,016 但是Rails应用程序的高层次目录结构并没有传达意图给我。 114 00:15:56,154 --> 00:16:03,104 他们只是告诉我,他们是Rails应用。更蹊跷的是 115 00:16:03,104 --> 00:16:05,902 然后它发生在我身上。 116 00:16:05,902 --> 00:16:15,087 这是已知问题?这是已解决问题。人们意识到,1992年伊娃·雅各布森解决并写了这本书。 117 00:16:15,087 --> 00:16:20,638 谁有这本书?谁读过这本书?这里有一个人,其他人呢? 118 00:16:20,677 --> 00:16:23,431 《面向对象的软件工程》,如果你有,它是一本精彩的书。 119 00:16:23,503 --> 00:16:29,821 1992年有点老了,但没关系。里面的原则还是非常好的。 120 00:16:29,916 --> 00:16:37,212 注意副标题,它写着:”用例驱动法“ 121 00:16:37,212 --> 00:16:45,107 谁还记得”用例“?呃..看到了吗?那是在90年代初非常流行。非常的火 122 00:16:45,107 --> 00:16:54,199 事实上,它是如此受欢迎,甚至被那些侵入并摧毁了用例本来该有的样子的顾问们彻底摧毁 123 00:16:54,199 --> 00:17:06,426 如果你还记得那个时代,你可能还会记得一个又一个的顾问在互联网上发布自己特定格式的用例 124 00:17:06,426 --> 00:17:15,840 格式成为了所有的重点。我们的PDF文件都在那里,它让你填写一个标准的用例形式的模板。 125 00:17:15,840 --> 00:17:23,318 就是填上用例的名称,再填上”输入“以及用例"前提"和"后置" 126 00:17:23,318 --> 00:17:29,726 和主要角色和次级角色以及三级角色。三级角色是什么鬼? 127 00:17:29,726 --> 00:17:38,276 你必须填写所有的东西,用例的整个问题变成了一种形式,而不是一种功能。 128 00:17:38,306 --> 00:17:44,214 当正好时代的巅峰,敏捷运动开始。 129 00:17:44,214 --> 00:17:52,242 我们不再讨论用例,我们开始讨论故事,用例有关的全部东西被扔弃在地板上。没有人再讨论它 130 00:17:52,242 --> 00:17:58,310 于是我又将它拿出来,重新一遍手册,读一遍书 131 00:17:58,310 --> 00:18:02,105 我想起了雅各布森写到的东西 132 00:18:02,168 --> 00:18:11,409 这里是一个用例。雅各布森写的那种典型的用例 133 00:18:11,444 --> 00:18:15,646 你是否注意到它只有有非常小的形式。 134 00:18:15,646 --> 00:18:24,568 哦~一点点而已。 created order是它的名字,想象一下,这是订单处理系统的用例。 135 00:18:24,568 --> 00:18:31,952 它有一个像” customer id“和”客户联系信息“和”运输目的地“等一些输入数据。 136 00:18:31,952 --> 00:18:37,657 注意,我没有提供任何细节,我不是在强调”客户ID“是什么 137 00:18:37,657 --> 00:18:40,146 无论是数字或字符串,我不在乎。 138 00:18:40,187 --> 00:18:48,264 我没讨论客户联系方式是什么,我只是认为它有名字和日期和地址以及其他的东西。但我不在乎 139 00:18:48,328 --> 00:18:51,560 我并不想在此处指定明确的细节。 140 00:18:51,599 --> 00:18:55,268 然后,到了主要课题 141 00:18:55,268 --> 00:19:04,557 主要课题一套计算机运行满足用例的步骤,是处理的步骤 142 00:19:04,557 --> 00:19:11,037 第一步是订单业务员发布的创建订单的命令,实际上不是计算机的做的事。 143 00:19:11,073 --> 00:19:18,511 第二步是有系统来验证数据是否有效。注意,我没有讨论如何验证,怎么验证并不重要 144 00:19:18,575 --> 00:19:23,542 第三步是系统创建的订单,并且确定订单的ID。 145 00:19:23,542 --> 00:19:29,089 我假定这是某种数据库的操作,但我不会在这里讨论。 146 00:19:29,089 --> 00:19:35,036 第四步是系统将订单号返回给业务员,也许是一个Web网页,但我不会在这里讨论 147 00:19:35,106 --> 00:19:40,426 事实上,整个的用例只字未提Web。 148 00:19:40,426 --> 00:19:44,456 这个用例的运行与什么样的输入输出通道没有任何关系。 149 00:19:44,456 --> 00:19:53,606 我可以在一个控制台应用程序,桌面应用程序,一个Web应用程序,面向服务的架构应用这个用例的运行,我的手机应用程式也没关系。 150 00:19:53,606 --> 00:19:59,609 因为用例是无关的,它并不关心I / O通道的具体实现。 151 00:19:59,609 --> 00:20:09,559 雅各布森说,你可以采取的这样的用例,并把它变成对象。 152 00:20:09,559 --> 00:20:19,878 他把这个对象叫做控制对象。我已经更名为”interactor “,以避免与Model View Controller混乱。 153 00:20:19,878 --> 00:20:26,239 也许我应该把名字改成”用例“。但我没有。我在这里写的是interactor 。 154 00:20:26,239 --> 00:20:38,028 interactor 对象实现了用例,它的输入,就是用例的输入,它提供的输出,就作为用例的输出。 155 00:20:38,028 --> 00:20:45,857 它至少在高层去实现那些用例的具体的规则,处理步骤等 156 00:20:45,857 --> 00:20:54,166 注意下面的注释,它写着interactors拥有具体的应用业务规则 157 00:20:54,166 --> 00:20:56,768 其实有两种业务规则 158 00:20:56,768 --> 00:21:06,166 有一些是全局性的业务规则,他们不关心应用程序是什么。 159 00:21:06,166 --> 00:21:11,695 然后还有一些是绑你正在编写应用程序的其他业务规则 160 00:21:11,695 --> 00:21:22,020 例如,我们有一个订单录入应用程序和订单处理应用程序。两个完全不同的应用程序 161 00:21:22,020 --> 00:21:29,693 他们都可能有一个订单对象,并且订单对象可能有共同的业务规则 162 00:21:29,693 --> 00:21:40,739 无论你使用哪一个应用,只有其中一个应用程序有插入订单用例 163 00:21:40,739 --> 00:21:47,910 所以这样的用例是应用程序特定的,他们有他们的特殊应用 164 00:21:47,910 --> 00:21:56,696 不在特定应用中的业务规则就绑定到实体对象,有些人称为业务对象 165 00:21:56,727 --> 00:22:01,325 我不喜欢这个词,雅各布森提出的entity objects(实体) 比较合适 166 00:22:01,405 --> 00:22:10,785 你把所有的应用程序业务规则放到实体里,interactor将控制的这些实体。 167 00:22:10,785 --> 00:22:17,332 然后你要搞清楚,如何从一个用例获得输入和输出放到到另外一个用例 168 00:22:17,332 --> 00:22:24,159 我们用接口来实现这个功能,我画了这些作为面向对象的接口 169 00:22:24,159 --> 00:22:31,266 注意,interactor使用了其中一个接口,以及从其他派生的。 170 00:22:31,266 --> 00:22:38,233 其中一个是从输入接口派生的,另一个是输出接口 171 00:22:38,233 --> 00:22:42,174 注意,箭头指向相同的方向,这很重要 172 00:22:42,174 --> 00:22:45,106 我们马上会知道为什么它会这么重要 173 00:22:45,106 --> 00:22:53,455 这三个对象都是雅各布森提出的应用程序架构的一部分 174 00:22:53,455 --> 00:22:57,683 现在让我们继续探索,就让我们看看它是如何工作的。 175 00:22:57,683 --> 00:23:02,934 这是一个典型的应用。那里有一个用户,这么个小人站在那里 176 00:23:02,934 --> 00:23:10,518 那是一个真实的人,他正在通过某种传递机制与系统进行交互 177 00:23:10,518 --> 00:23:13,561 也许是Web,也许不是,谁在乎 178 00:23:13,561 --> 00:23:25,915 用户,也许是按下键盘上的一个键,也许类似的事情,来通知系统接收数据 179 00:23:25,915 --> 00:23:31,619 这种传递机制也许是网络,也许不是,没多大的关系 180 00:23:31,619 --> 00:23:37,030 可以理解这是一种请求模型。 181 00:23:37,030 --> 00:23:39,844 请求模型是一个纯粹的数据结构 182 00:23:39,844 --> 00:23:50,698 一个普通的旧.NET对象(POCO)或普通Java对象(POJO),一种原始数据,它不知道数据从哪里来, there's no trappings of the web 183 00:23:50,698 --> 00:23:52,764 If there's the trappings of the web anywhere 184 00:23:52,764 --> 00:23:56,904 这只是一个普通的数据结构,它没有函数。什么都没有。 185 00:23:56,904 --> 00:24:01,911 一串的数据结构的公共元素,它包含了所有的输入数据 186 00:24:01,976 --> 00:24:11,846 然后被传递到interactor派生的输入边界接口, 187 00:24:11,846 --> 00:24:20,717 interactor收到请求模型,读取它,并解释它 188 00:24:20,717 --> 00:24:26,778 并且把它变成一套发送到实体的较小的命令 189 00:24:26,778 --> 00:24:33,196 所有小业务对象在那里,它控制着密密麻麻的所有调用实体的函数。 190 00:24:33,196 --> 00:24:41,673 一旦工作完成,那么它反向转动。它开始查询这些实体,并说,'OK,你有什么事?“ 191 00:24:41,673 --> 00:24:47,942 同时他也制造出了另一种数据结构,叫做结果模型 192 00:24:47,942 --> 00:25:01,338 结果模型也是一个普通的数据结构,不知道任何事情,只是一个POCO或者POJO,只有公共字段,没有函数 193 00:25:01,338 --> 00:25:11,928 结果模型通过输出边界接口被传递到用户那被显示或者其他的 194 00:25:11,928 --> 00:25:18,006 那些都是其他应用程序的流程了。 195 00:25:18,006 --> 00:25:23,632 你能测试Interactor吗? 196 00:25:23,632 --> 00:25:24,772 当然可以,而且很简单,不是吗? 197 00:25:24,772 --> 00:25:30,610 创建输入数据结构,调用Interactor看看输出的数据结构 198 00:25:30,610 --> 00:25:33,424 你需要一台Web服务来完成这件事吗? 199 00:25:33,424 --> 00:25:40,663 不,因为interactor,只是POCO或者POJO,这就是所有的数据结构 200 00:25:40,663 --> 00:25:45,237 你需要一个数据库来完成这件事吗?好吧,也许你需要,但是我们只需要一分钟就能完成 201 00:25:45,237 --> 00:25:50,665 我可以在没有具体传递机制的情况下测试它 202 00:25:50,665 --> 00:25:56,218 我的传递机制是否是Web?我不在乎!我不需要运行一台Web服务器 203 00:25:56,218 --> 00:25:58,291 我也不需要通过网页来进行测试 204 00:25:58,291 --> 00:26:07,206 我可以测试整个系统的运作而不去关心具体的输入/输出的传递机制 205 00:26:12,286 --> 00:26:14,110 什么是MVC? 206 00:26:14,110 --> 00:26:18,936 是不是MVC的事情是我们都应该做的? 207 00:26:20,206 --> 00:26:28,683 MVC代表什么? Model View Controler 是谁发明的MVC 208 00:26:28,683 --> 00:26:35,480 那家伙。现在我要彻底念错他的名字。你也许可以念的比我好 209 00:26:35,480 --> 00:26:41,514 可是我要叫他Trygve Reenskaug。你也许可以念的比我好 210 00:26:41,514 --> 00:26:47,317 我见过他一次。那个在70年代末发明的模型视图控制器的家伙。 211 00:26:47,317 --> 00:26:50,027 我见过他一次。两年前我在这里遇见了他。 212 00:26:50,027 --> 00:26:57,378 我在休息室找电源插座,而这个老家伙走到我面前,递给我一个电源插座 213 00:26:57,378 --> 00:27:01,080 我抬头......Trygve Reenskaug! 214 00:27:01,080 --> 00:27:07,596 当他递给我电源插座时,我们手指接触了 215 00:27:07,596 --> 00:27:13,075 我当然不会洗手,Trygve Reenskaug! 216 00:27:13,075 --> 00:27:19,293 有些人过来找我合影,现在我也是一个有fans的人了。 217 00:27:21,423 --> 00:27:27,284 在80年代初和70年代末,Trygve Reenskaug想出了这个结构称为模型-视图-控制器。 218 00:27:27,284 --> 00:27:30,099 他在Smalltalk的平台上这样做 219 00:27:30,099 --> 00:27:33,042 它背后的原理非常简单 220 00:27:33,042 --> 00:27:36,704 你已经有了一个模型对象,模型对象包含业务规则。 221 00:27:36,704 --> 00:27:41,326 它不知道它是如何显示的。它不知道输入从哪里来 222 00:27:41,326 --> 00:27:48,182 这是纯粹的业务规则,这里有一个控制器,它处理所有的输入 223 00:27:48,182 --> 00:27:59,586 控制器的工作就是盯着输入,不管是什么设备,键盘什么的都无所谓,然后翻译用户的动作为命令针对model 224 00:27:59,586 --> 00:28:01,094 然后,View就出现了 225 00:28:01,094 --> 00:28:06,610 我画的View有一个有趣的双箭头,这是一个观察的关系 226 00:28:06,610 --> 00:28:15,161 View 在Model 上注册,当Model发生变化,便回调到View,告诉View重新显示 227 00:28:15,201 --> 00:28:25,347 View的工作是要显示或代表或以某种方式传达模型的内容的。 228 00:28:25,347 --> 00:28:35,495 它可以很好地在一个图形用户界面上工作,它同样也可以同样运行在其他的你想要运行的系统上 229 00:28:35,495 --> 00:28:40,718 你有一些东西控制输入(Controler),你有一些东西控制过程(Model),你有一些东西控制输出(View) 230 00:28:40,718 --> 00:28:51,260 这可能是最早命名的设计模式了,运用在Smalltalk中 231 00:28:51,260 --> 00:29:01,770 按钮里有MVC模式,复选框里有MVC模式,文本字段里有MVC模式 232 00:29:01,770 --> 00:29:05,310 我们还没有关于屏幕的MVC模式 233 00:29:06,600 --> 00:29:14,920 在那些早期的日子里,这已经被扭曲和摧毁,像在软件里的任何东西一样。 234 00:29:14,920 --> 00:29:23,961 如果它原本是一个好主意,其他人就会复制它。并使用同样的名字,但是完全不同的东西,还要叫好 235 00:29:23,961 --> 00:29:29,431 OO发生过这事情,structurel发生过这事情,ObjectsIt 发生过这事情,Agile发生过这事情 236 00:29:29,431 --> 00:29:37,781 这会发生在任何东西上,只要它们的名字与好的东西有关,就有人把他们自己的东西冠以其名,并叫好 237 00:29:37,781 --> 00:29:44,013 那么MVC现在怎么样了?现在我们拥有许多MVC框架 238 00:29:44,013 --> 00:29:50,485 他们不顾任何事情,他们不是Trygve Reenskaug所描述MVC 239 00:29:50,485 --> 00:29:54,416 它们是非常不同的东西,事实上,他们看起来是这样的 240 00:29:55,416 --> 00:30:07,350 那里你有一大堆的controler,现在的controler,如果我们想一想Web,控制器是空中的Web框架通过某种方式激活的 241 00:30:07,350 --> 00:30:12,779 空中的Web框架?管它是什么,谁在乎。Rails?spring?上帝才知道那是什么 242 00:30:12,779 --> 00:30:21,848 通过某种方式从Web路由过来复杂又可怕的URL到一堆函数里,这就是我们说的Controler 243 00:30:21,848 --> 00:30:27,079 它会把从Web获得的参数和数据传入每个Controler 244 00:30:27,079 --> 00:30:33,950 然后Controler会接收这些数据,并且向business objects大喊,告诉business objects该做什么 245 00:30:33,950 --> 00:30:45,731 然后Controler收集来自business objects的数据,又开始向View大喊,然后视图将返回到业务对象,并收集了一堆数据,并表示它们 246 00:30:46,391 --> 00:30:55,422 最终你的下场就是business objects 被Controller 的功能和View的功能给污染了 247 00:30:55,422 --> 00:31:05,373 把不同的功能放在什么地方,这很难知道。有时候他们在business objects 里,但是确实却是不应该的 248 00:31:09,133 --> 00:31:13,874 我们如何处理与输出端? 249 00:31:15,764 --> 00:31:20,086 在这里,我向您展示了Interactor。Interactor完成了这些事情 250 00:31:20,086 --> 00:31:32,300 它从用例处理好的实体中收集的数据。该Response Model已经准备好。我们要通过输出边界传递Response Model 251 00:31:32,300 --> 00:31:38,262 什么实现了输出边界?一种叫Presenter的东西 252 00:31:38,262 --> 00:31:44,851 Presenter的工作是获得Response Model,记住它,是一个纯粹的数据结构。 253 00:31:44,914 --> 00:31:52,427 并且把它转换成另一种纯粹的数据结构,我们称之为View Model 254 00:31:52,427 --> 00:32:03,575 View Model 是输出的一种Model,一种输出的表示。它仍然是一个数据结构 255 00:32:03,575 --> 00:32:10,685 但是,如果有屏幕上有一个表格,那么数据结构中也将会有一个表格 256 00:32:10,753 --> 00:32:17,424 如果屏幕上有一个文本字段,那么数据结构中也会有一个文本字段 257 00:32:17,490 --> 00:32:28,201 如果屏幕上的数字需要保留小数点后的两位,View Model中会有TrimToTwoDecimalPlaces和ConvertIntToStrings 258 00:32:28,201 --> 00:32:37,522 如果他们被括号括起来,或者需要取消那些由Presenter加上的括号,这也被放在 View Model里 259 00:32:37,596 --> 00:32:43,097 如果有一个菜单,那么每一个菜单项的名字也在 View Model里 260 00:32:43,191 --> 00:32:52,862 如果有一些菜单因为不可用而需要变成灰色,控制这个状态的booleans 也在View Model里 261 00:32:52,862 --> 00:33:05,357 任何可以显示的表示都在 View Model 数据结构里,当然是以一种抽象的形式 262 00:33:05,357 --> 00:33:08,167 然后,它将影响到View 263 00:33:08,167 --> 00:33:15,893 View是愚蠢的,它什么事情也没做,只是把View Model中的字段拿出来,放到该放的地方,Boom Boom Boom 264 00:33:15,893 --> 00:33:21,133 没有处理,没有if语句,也许有一个循环用来加载表,仅此而已 265 00:33:21,133 --> 00:33:29,606 View太愚蠢了,以至于我们通常不会担心怎么去测试它。因为就算我们直接用眼睛来检查View也无妨 266 00:33:29,606 --> 00:33:33,069 那么Presenter可以测试吗? 267 00:33:33,100 --> 00:33:38,321 你可以将Response Model的数据结构交给它。然后检查生成的View Model数据结构 268 00:33:38,321 --> 00:33:43,797 所以Presenter是可以测试的。你需要搭建Web服务器并运行来测试Presenter吗? 269 00:33:43,844 --> 00:33:48,844 不,你在没有Web服务器运行的情况下测试Presenter 270 00:33:48,844 --> 00:33:53,198 你不需要Spring或者其他类似的那些只有上帝才知道的容器 271 00:33:53,198 --> 00:33:55,956 你不需要知道任何关于它们的细节 272 00:33:55,956 --> 00:33:59,520 你可以像小的普通对象一样测试所有的东西 273 00:33:59,520 --> 00:34:06,612 顺便提一句,我们有一个目标,就是不用启动任何东西就可以进行尽可能多的测试 274 00:34:06,612 --> 00:34:13,095 你不需要启动一个服务器,光是启动服务器这个事情就得花上30秒到1分钟 275 00:34:13,095 --> 00:34:21,176 你不需要做任何那样的事情,你可以以最快的速度进行测试,就像Boom Boom Boom 276 00:34:23,496 --> 00:34:26,049 这就是在整个过程 277 00:34:26,049 --> 00:34:32,487 从Interactor开始,你可以发现他通过输入边界获得来自Resquest Model的数据 278 00:34:32,487 --> 00:34:37,909 通过输出边界,将数据通过Response Model 传递给Presidenter 279 00:34:37,909 --> 00:34:48,541 现在注意那条黑线。黑线代表应用程序的传送机制(比如Web)。 280 00:34:48,541 --> 00:34:54,756 注意每一个穿过黑色的箭头,它们都指向应用程序 281 00:34:54,756 --> 00:35:00,046 应用程序对Controler和Presenter一无所知 282 00:35:00,046 --> 00:35:04,828 它不知道关于Web的细节,也不知道关于I/O通道的细节 283 00:35:04,828 --> 00:35:10,755 I/O通道知道应用程序,但是应用程序不知道I/O通道 284 00:35:10,755 --> 00:35:21,160 如果你把他们放在单独的Jar包里,应用程序Jar包和Web Jar包没有任何依赖 285 00:35:21,160 --> 00:35:30,222 也许你会这么做,你想把它们放在独立的Jar包中,所以,你可能有一个Web的Jar包,一个应用程序的Jar包 286 00:35:30,222 --> 00:35:33,517 也许还有另外一个I/O传递机制的jar包 287 00:35:33,517 --> 00:35:38,750 那么,你替换I/O机制的方式就是替换Jar包而已 288 00:35:38,750 --> 00:35:50,047 想一想I/O机制作为一个插件,就像Web。你们有多少人使用.Net Sharp? 289 00:35:50,085 --> 00:35:54,910 你们全部都用.Net?谁用.Net? 290 00:35:54,910 --> 00:36:04,165 你们用什么IDE?你们用了插件吗?那么 一个插件 291 00:36:05,885 --> 00:36:14,954 IDE的作者和插件的作者,他们彼此了解吗? 292 00:36:17,049 --> 00:36:25,830 插件的作者了解IDE的作者,但是IDE的作者不了解插件的作者 293 00:36:25,830 --> 00:36:27,668 一点都不关心 294 00:36:27,716 --> 00:36:35,500 他们谁能影响谁?插件的作者是不是可以影响到Visual Studio的正常运行? 295 00:36:35,500 --> 00:36:42,543 有谁使用ReSharper,使用ReSharper的人和使用JerBrains的人能影响到Visual Studio吗? 296 00:36:42,643 --> 00:36:46,554 - (听众)是的,没错 - (罗伯特·马丁)好了,他们可以打破它。 297 00:36:46,554 --> 00:36:52,545 但是他们这样影响它,Visual Studio的作者需要做出响应吗? 298 00:36:52,545 --> 00:36:58,895 微软的软件开发者,他们会响应JetBrains吗? 299 00:36:58,895 --> 00:37:02,410 不,他们根本不关心JetBrains 300 00:37:02,483 --> 00:37:10,232 微软开发者可以强迫JetBrains的开发者响应他们吗? 301 00:37:10,232 --> 00:37:13,757 当然,经常这样! 302 00:37:13,757 --> 00:37:17,791 现在,从应用程序的角度想想这件事 303 00:37:17,791 --> 00:37:25,851 你的应用程序哪些部分应该得到保护,从而免受其他部分的影响 304 00:37:25,851 --> 00:37:34,771 哪些部分是你希望强制响应改变,哪些部分不需要强制响应改变? 305 00:37:34,771 --> 00:37:38,838 而答案应该是非常非常清楚的 306 00:37:38,838 --> 00:37:45,152 你想保护你的业务规则,不被Web的变化所影响 307 00:37:45,152 --> 00:37:49,706 相反,你不想再你的业务规则发生变化时,Web却被保护起来没办法改变 308 00:37:49,706 --> 00:37:59,528 但是,Web上的任何变化都不应该影响到你的业务规则,这就是架构能提供的保障 309 00:37:59,528 --> 00:38:06,710 所有的依赖都朝向应用程序,记住这一点,这既是plug-in architecture 310 00:38:07,680 --> 00:38:11,912 将我们来看看数据库 311 00:38:14,882 --> 00:38:18,116 什么是数据库? 312 00:38:20,396 --> 00:38:22,991 这是不是你对数据库的认识? 313 00:38:23,931 --> 00:38:31,846 你是不是认为数据库才是中间的上帝,周围都围绕着各种各样的小应用程序 314 00:38:31,846 --> 00:38:37,573 有谁的工作是DBA?这间屋子里有DBA吗? 315 00:38:37,573 --> 00:38:39,368 哦~我很安全,很好 316 00:38:39,368 --> 00:38:48,993 有人知道DBA是什么吗?将应用程序颠覆,脱离该有的模式。确保数据库才是正确的 317 00:38:49,066 --> 00:38:52,413 这是不是就是你心中的数据库? 318 00:38:52,540 --> 00:38:55,061 因为这是我的观点 319 00:38:55,135 --> 00:38:57,389 数据库是一个细节上的东西 320 00:38:57,389 --> 00:39:00,753 这不是架构中重要的东西 321 00:39:00,885 --> 00:39:05,017 数据库是一个装满Bits的桶而已 322 00:39:05,876 --> 00:39:10,112 这不是你的系统架构中最重要的东西 323 00:39:10,112 --> 00:39:12,791 它和业务规则没有任何关系 324 00:39:12,791 --> 00:39:17,263 我的天,除非你把业务规则放在存储过程里 325 00:39:17,263 --> 00:39:25,818 存储过程里做的事情都是查询,验证,完整性检查只是增强功能,但不是业务规则 326 00:39:29,898 --> 00:39:32,519 为什么我们要有数据库? 327 00:39:34,219 --> 00:39:40,744 数据库这种东西从哪来?为什么会有Oracle? 328 00:39:45,614 --> 00:39:48,768 我们所有的数据存放在哪里? 329 00:39:48,768 --> 00:39:53,494 我们存储在磁盘上,有人写过磁盘驱动吗? 330 00:39:53,494 --> 00:39:58,375 有人写过一个软件,用来控制内存的数据和磁盘交互吗? 331 00:39:58,456 --> 00:40:06,569 没有人写过!哦,天哪,哦 耶!磁盘驱动程序! 332 00:40:06,569 --> 00:40:10,454 什么?软盘?那还不够 333 00:40:10,454 --> 00:40:15,290 从磁盘获取数据,或者将数据存入磁盘是非常难得事情,为什么? 334 00:40:15,290 --> 00:40:18,183 因为数据在磁盘上的组织方式 335 00:40:18,261 --> 00:40:21,820 数据在磁盘上圆形轨道上的组织方式 336 00:40:21,820 --> 00:40:26,055 这种圆形轨道可以在布满盘面 337 00:40:26,055 --> 00:40:35,025 有一个磁头可以在这些轨道上来回移动,所以,你的控制磁头移动到正确的轨道上 338 00:40:35,066 --> 00:40:38,941 然后磁盘开始转动,你就可以读取你需要的数据了 339 00:40:38,941 --> 00:40:46,894 你找到你想要的扇区,一个轨道的表面大概有5,60个扇区,每个扇区可能有4K字节 340 00:40:46,894 --> 00:40:52,282 所以你必须等待磁盘旋转,当你的扇区到来时,你就可以读取数据了 341 00:40:52,282 --> 00:40:57,368 然后你进入扇区,读取你想要的字节 342 00:40:57,368 --> 00:41:01,134 这非常痛苦,而且速度非常慢 343 00:41:01,144 --> 00:41:05,141 如果你不优化,这可能会花上一辈子时间来读取或者存储数据 344 00:41:05,141 --> 00:41:13,866 所以我们写了一个解决这个问题的系统,我们叫他数据库 345 00:41:15,106 --> 00:41:17,502 可是发生了一些事情 346 00:41:17,502 --> 00:41:28,339 看见我的笔记本了吗?我的笔记本有512M SSD内存,没有磁盘。这对你来说应该不会惊讶 347 00:41:28,339 --> 00:41:30,936 这间屋子有人还在用硬盘吗? 348 00:41:30,936 --> 00:41:34,012 这间屋子里有人用旋转的那种硬盘吗? 349 00:41:34,012 --> 00:41:37,070 哦 我的天,这是真的吗? 350 00:41:37,070 --> 00:41:43,249 嗯,大家都知道,现在已经没有人再去考虑旋转的那种磁盘了,我们考虑的都是SSD 351 00:41:43,249 --> 00:41:50,126 哦,我们想想服务器机房里,也许还有那种硬盘,不过它们始终会被淘汰 352 00:41:51,019 --> 00:41:55,998 放眼未来几年,那种硬盘开始消失,所有的都被RAM取代 353 00:41:56,081 --> 00:41:59,719 我说的是RAM? 354 00:41:59,719 --> 00:42:04,421 RAM直接在字节级寻址 355 00:42:04,498 --> 00:42:13,672 我们正在使用直接寻址的内存几乎无限量也是持久的。 356 00:42:13,672 --> 00:42:17,634 这就是我们将要存储数据的地方 357 00:42:17,634 --> 00:42:27,902 如果这就是我们要存储数据的地方,为什么还要像在地狱般一样使用SQL来访问它们呢? 358 00:42:27,902 --> 00:42:32,101 SQL是痛苦的,Table也是苦痛的 359 00:42:32,101 --> 00:42:36,937 在你打算用哈希表来查询事情的时候难道不是跟随周围的人一样? 360 00:42:36,937 --> 00:42:44,662 那就是你所做的吗?你把所所有的table读入内存里,然后把数据组织的更好,你真的可以使用它吗? 361 00:42:44,662 --> 00:42:49,298 如果我们只是把它放在我们想使用的格式? 362 00:42:49,298 --> 00:42:52,746 如果我是Oracle我会被吓死。 363 00:42:52,801 --> 00:42:57,280 因为我存在的理由彻底消失了 364 00:42:57,280 --> 00:43:02,567 大数据库系统的概念已经开始消失了 365 00:43:05,317 --> 00:43:10,279 我们应该怎么样保护我们的应用程序不受这种细节的影响? 366 00:43:10,279 --> 00:43:19,105 数据库这个细节往往主宰了一切,但是我可以保护我们的应用程序就像保护它们不受Web的影响一样 367 00:43:19,105 --> 00:43:23,129 我画了另一条可爱的黑线在那 368 00:43:23,198 --> 00:43:30,444 我确保所有的穿过这条线的依赖都指向应用程序里面 369 00:43:30,444 --> 00:43:35,554 我把数据库做成了一个应用程序的插件 370 00:43:35,554 --> 00:43:38,924 那么,我就可以切换使用Oracle还是MySQL 371 00:43:38,924 --> 00:43:42,706 又或者我可以放弃MySQL从而使用CouchDB 372 00:43:42,706 --> 00:43:47,465 又或者我可以放弃CouchDB从而使用CouchDB或者别的我想用的 373 00:43:47,465 --> 00:43:50,248 我可以像插件一样使用数据库 374 00:43:50,248 --> 00:43:57,116 也许你永远都不会更换数据库,但是会的话会更好,即使你没必要这么做。 375 00:43:57,116 --> 00:44:01,082 我们怎么实现呢?好吧,相当直接了当,在那里还有另外一个接口 376 00:44:01,082 --> 00:44:03,062 我在这里把它叫做Entity Gateway 377 00:44:03,102 --> 00:44:10,159 Gateway中有对应的每一个Entity的查询方法 378 00:44:10,159 --> 00:44:14,247 他提供一个功能一个函数,让你你要查询任何你想要的东西, 379 00:44:14,247 --> 00:44:19,247 你在黑线以下的Entity Gateway Implementation 实现具体的方法 380 00:44:19,247 --> 00:44:23,128 这个实现可以是数据库,或者其他什么,管他的 381 00:44:23,128 --> 00:44:29,917 注意,没有任何数据库的管理部分渗透到了应用程序中 382 00:44:29,917 --> 00:44:39,838 从Entity Object来的地方。Entity Object 有可能被数据库以某种疯狂的方式取出,并映射到Table里 383 00:44:39,838 --> 00:44:44,115 Entity Gateway 的实现会汇集数据 384 00:44:44,115 --> 00:44:47,673 创建Entity Object,然后将他们送过黑线 385 00:44:47,673 --> 00:44:53,114 因此,一旦它们穿越黑线之后,它们就是真正的Entity Object了 386 00:44:55,164 --> 00:45:00,283 有人用过类似Hibernate的东西吗?或者ORM工具? 387 00:45:00,283 --> 00:45:02,045 谁在使用类似的东西? 388 00:45:02,066 --> 00:45:06,983 它们在这个图里面的哪个位置呢? 389 00:45:06,983 --> 00:45:10,485 在具体实现里,黑线的下面 390 00:45:10,485 --> 00:45:16,176 黑线以上对ORM工具的任何部分都不了解 391 00:45:16,176 --> 00:45:21,664 你在Business Object中使用那些有趣的小的注解或者属性吗?让他们从你的Business Object滚开 392 00:45:21,664 --> 00:45:25,465 你不会想要让你的Business Object知道他们是由Hibernatez创建的 393 00:45:25,465 --> 00:45:31,817 让它被那些已经被数据库污染了得来自黑线下面的东西创建 394 00:45:31,831 --> 00:45:36,131 保持你的Business Object 纯洁,为什么? 395 00:45:37,741 --> 00:45:40,556 一个对象是什么? 396 00:45:43,106 --> 00:45:45,360 我为这个问题花了很多时间 397 00:45:46,610 --> 00:45:54,363 什么是对象,一个对象就是一组公共方法的集合 398 00:45:54,363 --> 00:46:00,727 你想知道其他的东西?这是不允许的,不是吗? 399 00:46:00,727 --> 00:46:04,792 那里可能有数据,但是你不能看到它,这些都是私有的,不是吗? 400 00:46:04,792 --> 00:46:11,459 从你的角度来看,Object是一堆方法,不是一堆数据 401 00:46:11,459 --> 00:46:13,618 既然Object是一堆方法 402 00:46:13,618 --> 00:46:17,075 那么Obejct就是和行为有关的 403 00:46:17,075 --> 00:46:20,863 一个Object是和业务规则有关的 404 00:46:20,863 --> 00:46:23,213 它不是和数据有关的 405 00:46:23,213 --> 00:46:26,228 我们估计那里面有数据,但是我们不知道在哪 406 00:46:26,228 --> 00:46:29,990 我们也不知道是什么格式,我们也不想知道 407 00:46:29,990 --> 00:46:32,973 什么是数据结构? 408 00:46:32,973 --> 00:46:40,606 一个数据结构是一组已知的数据元素,公开的,大家都能看到 409 00:46:40,606 --> 00:46:42,761 并且,里面没有任何方法 410 00:46:42,761 --> 00:46:44,779 数据结构没有任何功能 411 00:46:44,779 --> 00:46:47,263 对象和数据结构正好是相反的 412 00:46:47,263 --> 00:46:54,612 数据结构有明显的数据,但没有方法,对象有明显的功能,但没有明显的数据 413 00:46:54,612 --> 00:46:56,112 正好相反 414 00:46:56,112 --> 00:47:00,133 没有任何东西能这样,比如ORM 415 00:47:00,175 --> 00:47:03,339 对象关系映射?不能做到 416 00:47:03,411 --> 00:47:10,222 因为从数据库得到的是一种数据结构,你不能把一个数据结构映射到一个对象 417 00:47:10,268 --> 00:47:12,452 因为他们根本就是不同的两个东西 418 00:47:12,452 --> 00:47:19,613 实际上这只是Entity所需要的数据被存放在什么地方了,上帝才知道在哪 419 00:47:19,613 --> 00:47:26,614 然后不知道怎么神奇的传递到Entity那,又不知道怎么神奇的开始使用它。我不在乎 420 00:47:26,615 --> 00:47:31,151 这些Entity不会有Hibernate创建 421 00:47:31,151 --> 00:47:38,006 也许这些Entity使用了一些由Hibernate创建的数据结构,但我不在乎 422 00:47:38,006 --> 00:47:45,239 我不想任何Hibernate的功能或者其他框架的功能穿过这条黑线 423 00:47:45,239 --> 00:47:48,358 黑线以下的东西可以被污染 424 00:47:48,358 --> 00:47:54,559 黑线以上的东西都是我的宝贝,我要保护他们不受影响 425 00:47:54,559 --> 00:48:02,572 这些都是我的业务规则,我不会让我的业务规则受到框架的污染 426 00:48:02,572 --> 00:48:09,793 框架。我们都喜欢使用框架,我们觉得它们很酷 427 00:48:09,793 --> 00:48:14,859 我们认为框架做的事情确实为我们节省了不少时间 428 00:48:14,859 --> 00:48:21,820 但框架的作者引诱我们绑定到他们那 429 00:48:21,820 --> 00:48:26,278 他们为我们提供的父类让我们继承 430 00:48:26,278 --> 00:48:32,477 当你从一个父类继承时,你已经嫁给了那个类 431 00:48:32,477 --> 00:48:40,045 你将自己的狠狠的绑定在了父类上,没有比继承更强的关系了 432 00:48:40,045 --> 00:48:47,430 因此,通过从别人的父类派生,你给了它们一个巨大的许诺 433 00:48:47,493 --> 00:48:52,156 但是,作为他们来说,没有做任何一种对你的许诺 434 00:48:52,156 --> 00:48:54,684 所以这是一种不对称关系 435 00:48:54,684 --> 00:49:02,244 框架的作者从你的承诺中获得好处,但是框架的作者却不对你做出任何承诺 436 00:49:02,305 --> 00:49:06,524 我让你自己去评估一下其中的利弊 437 00:49:06,524 --> 00:49:12,336 聪明的架构师不会去做这种绑定 438 00:49:12,336 --> 00:49:20,705 聪明的架构师对框架不屑一顾,它们认为框架会绑架我 439 00:49:20,705 --> 00:49:25,605 这个框架希望我绑定到它那,但我想我不会这么做 440 00:49:25,605 --> 00:49:30,985 我想我应该在我的业务规则与框架之间设置一个边界 441 00:49:30,985 --> 00:49:39,477 那么我的业务规则永远不会依赖于Spring或者hibernate或者其他什么只有上帝才知道的东西 442 00:49:39,477 --> 00:49:43,966 我会去使用框架,并且我会小心的使用它们 443 00:49:43,966 --> 00:49:49,822 因为框架的作者对于我最感兴趣的部分一点都不在乎 444 00:49:55,672 --> 00:50:03,334 很久以前,我和儿子还有其他几个人写了一个叫做FitNess的工具 445 00:50:03,334 --> 00:50:06,533 有人在使用FitNesse吗?哦,大部分都用了,很好 446 00:50:06,533 --> 00:50:13,149 FitNesse是用于编写客户验收测试的工具 447 00:50:13,188 --> 00:50:17,576 它是基于Wiki的一个东西,这就是你确切该知道的所有事情 448 00:50:17,576 --> 00:50:19,397 谁发明的Wiki 449 00:50:19,437 --> 00:50:22,483 沃德·坎宁安。谁是沃德·坎宁安? 450 00:50:22,483 --> 00:50:26,765 创造Wiki的人,啊,他的事迹不止这些 451 00:50:26,765 --> 00:50:35,256 沃德·坎宁安是大师中的大师。所有的大师都知道沃德·坎宁安是谁 452 00:50:35,256 --> 00:50:39,995 那些到处去演讲的人,比如我,我们都知道沃德·坎宁安是谁 453 00:50:40,028 --> 00:50:42,006 我们仰望他 454 00:50:42,006 --> 00:50:46,424 他是那个打电话给埃里克·伽马的人 455 00:50:46,424 --> 00:50:49,760 他说:"你知道吗埃里克,你得写一本书叫做设计模式。" 456 00:50:49,800 --> 00:50:58,122 他也辅导过肯·贝克,传授一些东西比如结对编程,测试驱动开发和敏捷开发 457 00:50:58,122 --> 00:51:06,937 如果你知道很多关于软件业的历史,你就会知道他。你会发现:“哇~哪里都有他的身影” 458 00:51:06,937 --> 00:51:12,160 FitNesse是基于沃德的两项发明,Wiki和Fit 459 00:51:12,204 --> 00:51:16,212 我不打算描述Fit。不过我会描述Wiki 460 00:51:16,212 --> 00:51:24,896 大约12或者13年前,我和我的儿子还有其他一些人觉得用java来写这一个类似Wiki的东西 461 00:51:24,896 --> 00:51:27,807 我知道我们想要一个Wiki 462 00:51:27,807 --> 00:51:34,958 所以我们这么想:“好了,我们有一个地方来存储网页,让我存储他们在数据库里吧,我们该用什么样的数据库呢?” 463 00:51:34,992 --> 00:51:41,925 嗯,在那些日子里,只有MySQL是开源数据库,所以我们觉得把一切都放在MySQL中 464 00:51:43,245 --> 00:51:50,880 于是我们准备去搭建MySQL环境,建立一套规则,这时有人说 465 00:51:50,880 --> 00:51:53,550 “你明白,其实我们不需要马上这么做” 466 00:51:53,550 --> 00:51:58,793 ”我的意思是,我们以后会这么做,但不是现在。因为我们可以先处理另外一个问题“ 467 00:51:58,793 --> 00:52:03,312 这个问题就是怎么把Wiki文本翻译成HTML,这才是Wiki该做的事情 468 00:52:03,382 --> 00:52:07,185 把你的输入的有趣内容放到Wiki里,然后把他转换成HTML 469 00:52:07,251 --> 00:52:10,156 我们需要做很多翻译的事情 470 00:52:10,156 --> 00:52:14,258 于是接下来的3个月 ,我们把数据库彻底遗忘了 471 00:52:14,258 --> 00:52:21,379 我们把Wiki文本翻译成HTML,我们需要一个叫做WikiPage的对象 472 00:52:21,414 --> 00:52:22,968 你可以在这里看到 473 00:52:22,968 --> 00:52:29,995 我们创建一个名为WikiPage的抽象类,我们有一个叫做MockWikiPage的实现 474 00:52:29,995 --> 00:52:37,446 WikiPage的方法里有一些很想数据库功能的方法,比如Load和Save 475 00:52:37,490 --> 00:52:40,028 但是它们没有被实现,它们没做任何事情 476 00:52:40,028 --> 00:52:43,364 3个月以来我们都这么干 477 00:52:43,364 --> 00:52:49,177 当我们把所有翻译工作都完成的时,我们说:“嗯,是时候搭建数据库了” 478 00:52:49,177 --> 00:52:52,039 “因为现在我们确实需要把这些页面存储在实际的地方了” 479 00:52:52,070 --> 00:52:54,670 又有人说:“嗯,其实我们不需要那样做” 480 00:52:54,670 --> 00:53:00,006 “因为我们可以替换成用哈希表在RAM中存储这些页面” 481 00:53:00,056 --> 00:53:02,590 “我的意思是,我们并不需要真正的把他们存储在硬盘上,不是吗?” 482 00:53:02,590 --> 00:53:06,356 这个当然是肯定的,因为所有我们该做的事情反正只是编写单元测试 483 00:53:06,356 --> 00:53:14,871 因此,我们决定创建名为InMemoryPage的WikiPage,其中存储的实现是用哈希表完成的 484 00:53:14,871 --> 00:53:17,436 然后我们继续工作了一年 485 00:53:17,786 --> 00:53:21,611 继续编写更多的FitNesse,并且把所有的数据都放在内存里 486 00:53:21,611 --> 00:53:24,294 事实上,我们让所有的FitNesse都正常工作了 487 00:53:24,744 --> 00:53:27,280 而且并没有把他们任何一个放入磁盘里 488 00:53:27,332 --> 00:53:32,169 这非常酷,因为没有数据库的所有的测试都非常快 489 00:53:32,169 --> 00:53:38,712 但是另一方面它也让人失望,因为我们创建的这一大堆测试再计算机关闭后都消失了 490 00:53:38,712 --> 00:53:44,721 所以这个时候我们终于说:“现在好了,我们需要数据库,让我们搭建MySQL吧” 491 00:53:44,721 --> 00:53:46,664 那时候 Michael Feathers在那 492 00:53:46,664 --> 00:53:50,274 他说:“嗯,你还是没有必要搭建MySQL呢” 493 00:53:50,350 --> 00:53:58,620 “你真的需要的是持久化,而你可以采取将哈希表写入到文件的方式来实现,这也更容易” 494 00:53:59,260 --> 00:54:06,860 我们认为这是一种丑陋的实现,但工作初期它暂时可以运行。以后我们再把他换成MySQL,因此我们这么做了 495 00:54:06,860 --> 00:54:11,112 又是3个月的时间,我们继续开发了越来越多的fitnesse 496 00:54:11,112 --> 00:54:16,374 那非常酷,因为我们可以在带着他出门,向别人展示。我们可以保持网页 497 00:54:16,426 --> 00:54:18,278 像真的Wiki一样开始运行 498 00:54:18,551 --> 00:54:27,959 3个月以后,我们说:“我们不需要数据库” 499 00:54:28,539 --> 00:54:31,807 “它的工作很正常,文件的速度已经足够快了” 500 00:54:31,879 --> 00:54:33,691 “这种方式运行得很好” 501 00:54:33,732 --> 00:54:38,884 我们选择的架构中更重要的部分,并且把数据库仍到了地球的另一端 502 00:54:38,884 --> 00:54:43,799 我们从来没有使用数据库,虽然那确实不是很正确 503 00:54:44,249 --> 00:54:49,745 我们的一位客户说:“我得在数据库中存储数据” 504 00:54:49,745 --> 00:54:52,371 我们说:"为什么?他在文件上工作的一样很好" 505 00:54:52,495 --> 00:54:59,032 他说:“公司的规定,公司所有的资产都必须在数据库中” 506 00:54:59,032 --> 00:55:04,115 我不知道谁和他们这么说的,但是这些数据库推销员确实很有说服力 507 00:55:04,415 --> 00:55:11,854 所以,我们说:“好吧,你看,如果你真的需要在数据库存储它” 508 00:55:13,624 --> 00:55:24,208 “这就是这种结构,你要做的就是创建一个MySQL Page的子类,然后一切都会运行的很好” 509 00:55:24,251 --> 00:55:27,162 一天以后他再来的时候,所有的东西都通过MySQL存储了 510 00:55:27,162 --> 00:55:31,468 我们把它当作一个插件来使用,但是没有人这么用,所以我们把它停止了 511 00:55:32,018 --> 00:55:37,936 这就是一个我们推迟了无数遍的架构中的决定 512 00:55:38,003 --> 00:55:41,034 我们推迟直到项目结束,我们也没这么做 513 00:55:41,034 --> 00:55:45,352 那些我们想放在最开始做的事情,却最终没有做 514 00:55:45,352 --> 00:55:49,120 这告诉我们终极原则 515 00:55:50,619 --> 00:56:00,209 一个良好的架构是允许架构中重要决定被推迟的,延期的 516 00:56:00,209 --> 00:56:05,107 架构师的目标不是做决定 517 00:56:05,147 --> 00:56:12,618 尽可能长的推迟这些事情,那么到最后你将有更多的选择来做决定 518 00:56:12,618 --> 00:56:21,953 你的架构设计,你的代码的高层目录结构,这些高层次的决策都可以推迟了 519 00:56:21,953 --> 00:56:28,929 别告诉我,你的应用程序的架构是一堆框架 520 00:56:28,970 --> 00:56:35,568 “你们应用程序的架构是什么?我们使用的SQL服务器和MVVM和等等等框架” 521 00:56:35,568 --> 00:56:37,347 你没有告诉我任何事情 522 00:56:37,347 --> 00:56:40,744 你只是告诉我工具而已 523 00:56:40,744 --> 00:56:43,011 这不是你的应用程序的架构 524 00:56:43,075 --> 00:56:46,104 你的应用程序的架构是那些用例 525 00:56:46,104 --> 00:56:50,460 而你并不希望用例依赖这些工具 526 00:56:50,460 --> 00:56:54,847 你想把工具的使用推迟的越晚越好 527 00:56:54,847 --> 00:57:02,270 你应该让整个应用程序在没有数据库没有Web的情况也可以运行 528 00:57:02,270 --> 00:57:07,483 也许你会搭建一个简单的小型的Web,然后你可以使用个一两天 529 00:57:07,483 --> 00:57:12,177 那样,你不需要搭建巨大的框架就可以看到一些网页 530 00:57:12,177 --> 00:57:17,485 也许你搭建了一些简陋的类似数据库的东西,来让持久化的工作开始运行 531 00:57:17,485 --> 00:57:23,627 这样你就不需要Oracle的许可来运行持久化的工作了。 532 00:57:25,347 --> 00:57:34,497 下次你再编写应用程序的时候,想一想什么是可以推迟的事情 533 00:57:37,757 --> 00:57:48,049 系统中的应用程序应该使用插件架构,系统中的架构应该是插件架构 534 00:57:48,083 --> 00:57:55,076 UI,数据库,框架的细节都以插件的形式接入用例 535 00:57:55,076 --> 00:57:57,959 哪些才是应用程序的核心? 536 00:57:59,809 --> 00:58:03,990 当然现在的客户希望看到网页 537 00:58:03,990 --> 00:58:10,463 好吧,你依然可以构建一个插件架构,然后让他们看见网页在运行 538 00:58:10,514 --> 00:58:13,339 你不需要向框架承诺太多 539 00:58:13,339 --> 00:58:16,647 你不需要向Web框架承诺太多 540 00:58:16,647 --> 00:58:19,084 你可以先使用一些简单的东西来代替 541 00:58:19,084 --> 00:58:25,807 或者如果你真的想使用一个真正的框架,请吧,但请保持插件架构 542 00:58:25,807 --> 00:58:29,285 那么在某个时候,你可以轻易的卸载它,并使用其他的来代替 543 00:58:31,245 --> 00:58:35,177 这可能就是我最后要谈论的东西了 544 00:58:35,857 --> 00:58:39,417 我们先前已经讨论过这个问题(TDD) 545 00:58:41,737 --> 00:58:45,463 所以非常感谢大家,有什么问题吗? 546 00:58:47,635 --> 00:58:50,950 有点看不清楚,那么我要站过来一点 547 00:58:54,550 --> 00:58:56,664 哦,这一点用也没有 548 00:58:57,464 --> 00:59:01,372 好吧,有人有什么问题吗? 549 00:59:01,372 --> 00:59:04,449 你不得不喜欢空洞。因为我无法看到你的手。 550 00:59:04,449 --> 00:59:05,236 没错。 551 00:59:05,236 --> 00:59:11,963 (有人问一个问题) 552 00:59:11,963 --> 00:59:21,120 那么我猜测Oracle和关系数据库会灭亡。那么我建议用了什么来将取代他们? 553 00:59:21,151 --> 00:59:24,337 多么有趣的一个问题 554 00:59:24,337 --> 00:59:30,947 那必定是RAM了,还有什么可以取代他妈? 555 00:59:30,947 --> 00:59:36,511 如果你要组织数据在RAM中,如果RAM是持久性的,你还需要什么呢? 556 00:59:36,562 --> 00:59:38,857 哦,但让我们说你需要的东西。 557 00:59:38,857 --> 00:59:41,488 好吧什么可能是什么样子的? 558 00:59:42,458 --> 00:59:45,557 嘛,谁听说过CQRS的? 559 00:59:46,437 --> 00:59:47,719 噢,你们几个。 560 00:59:47,719 --> 00:59:49,933 CQRS,多么有趣的想法? 561 00:59:49,957 --> 00:59:54,999 如果我们有高速内存无限量。高速RAM。 562 00:59:54,999 --> 00:59:58,390 或者,也许它甚至磁盘,但谁在乎。 563 00:59:58,390 --> 01:00:02,369 我们为什么要存储什么状态。 564 01:00:02,369 --> 01:00:05,195 为什么不我们只是简单地存储交易? 565 01:00:05,195 --> 01:00:12,043 而不是存储银行账户的,为什么不我们存储使我们创造了银行账户的交易。 566 01:00:12,043 --> 01:00:19,295 567 01:00:19,295 --> 01:00:23,381 568 01:00:23,381 --> 01:00:25,870 569 01:00:25,870 --> 01:00:28,512 570 01:00:28,512 --> 01:00:35,131 571 01:00:40,331 --> 01:00:44,299 572 01:00:44,299 --> 01:00:46,264 573 01:00:46,296 --> 01:00:53,412 574 01:00:53,412 --> 01:00:55,640 575 01:00:55,709 --> 01:00:59,436 576 01:00:59,436 --> 01:01:04,702 577 01:01:04,702 --> 01:01:08,752 578 01:01:08,752 --> 01:01:13,568 579 01:01:13,568 --> 01:01:17,292 580 01:01:17,292 --> 01:01:20,151 581 01:01:20,151 --> 01:01:25,436 582 01:01:25,468 --> 01:01:27,139 583 01:01:27,139 --> 01:01:31,633 584 01:01:32,703 --> 01:01:35,381 585 01:01:35,381 --> 01:01:37,632 586 01:01:37,632 --> 01:01:40,162 587 01:01:40,162 --> 01:01:45,731 588 01:01:45,778 --> 01:01:51,519 589 01:01:51,583 --> 01:01:53,852 590 01:01:53,852 --> 01:01:58,568 591 01:02:00,218 --> 01:02:02,967 592 01:02:02,967 --> 01:02:04,775 593 01:02:07,625 --> 01:02:09,379 594 01:02:09,379 --> 01:02:12,216 595 01:02:15,066 --> 01:02:27,373 596 01:02:27,373 --> 01:02:31,933 597 01:02:33,923 --> 01:02:42,383 598 01:02:43,633 --> 01:02:47,074 599 01:02:47,074 --> 01:02:49,872 600 01:02:49,872 --> 01:02:52,603 601 01:02:52,603 --> 01:02:56,085 602 01:02:56,085 --> 01:03:02,232 603 01:03:02,232 --> 01:03:06,887 604 01:03:06,887 --> 01:03:09,262 605 01:03:09,752 --> 01:03:13,585 606 01:03:13,585 --> 01:03:22,817 607 01:03:23,467 --> 01:03:31,163 608 01:03:31,206 --> 01:03:36,086 609 01:03:36,086 --> 01:03:43,321 610 01:03:43,321 --> 01:03:44,936 611 01:03:44,936 --> 01:03:53,693 612 01:03:55,893 --> 01:03:59,233 613 01:03:59,233 --> 01:04:03,986 614 01:04:04,031 --> 01:04:05,521 615 01:04:05,561 --> 01:04:09,075 616 01:04:09,075 --> 01:04:12,391 617 01:04:12,441 --> 01:04:17,674 618 01:04:18,904 --> 01:04:20,818 619 01:04:21,862 --> 01:04:24,136 620 01:04:24,136 --> 01:04:29,320 621 01:04:29,320 --> 01:04:31,535 622 01:04:31,535 --> 01:04:34,765 623 01:04:36,205 --> 01:04:40,168 624 01:04:40,261 --> 01:04:41,959 625 01:04:42,249 --> 01:04:48,166 626 01:04:48,198 --> 01:04:50,189 627 01:04:50,759 --> 01:04:56,185 628 01:04:56,185 --> 01:04:58,463 629 01:04:58,463 --> 01:05:01,495 630 01:05:01,495 --> 01:05:04,145 631 01:05:04,145 --> 01:05:08,029 632 01:05:08,029 --> 01:05:12,065 633 01:05:12,065 --> 01:05:17,045 634 01:05:17,045 --> 01:05:20,421 635 01:05:20,421 --> 01:05:23,928 636 01:05:23,928 --> 01:05:27,915 637 01:05:28,022 --> 01:05:32,956 638 99:59:59,999 --> 99:59:59,999