1-
2-
31# 编辑器使用引擎创建场景
42
53## 需求
1311
1412
1513## 给出UML
14+ ** 领域模型**
1615
1716
1817![ 领域模型图] ( ./story_before/UML.png )
1918
20- 总体来看,分为用户 、编辑器、引擎这三个部分
19+ 总体来看,领域模型分为用户 、编辑器、引擎这三个部分
2120
2221我们看下用户这个部分:
22+
2323Client是用户
2424
2525我们看下编辑器这个部分:
26+
2627Editor是编辑器
2728
2829我们看下引擎这个部分:
30+
2931Three.js是Three.js引擎
3032
3133
@@ -80,6 +82,7 @@ Editor引入Three.js引擎,并在createScene函数中调用它来创建场景
8082- Editor改为引入Babylon.js引擎,并修改Editor中与引擎相关的代码
8183
8284## 给出UML
85+ ** 领域模型**
8386
8487
8588![ 领域模型图] ( ./story_after/UML.png )
@@ -101,7 +104,8 @@ Editor引入Three.js引擎,并在createScene函数中调用它来创建场景
101104
102105Client代码跟之前一样,故省略
103106
104- 我们看下Editor的代码:
107+ 我们看下Editor的代码
108+
105109### Editor的代码
106110
107111Editor
@@ -123,25 +127,26 @@ Editor改为引入Babylon.js引擎,并修改createScene函数中与引擎相
123127
124128## 提出问题
125129
126- - 替换引擎的成本太高
130+ - 替换引擎的成本太高
127131替换引擎需要修改Editor中所有与引擎相关代码,成本太高了。有没有办法能在不修改Editor代码的情况下实现替换引擎呢?
128132
129133
130134# 使用依赖隔离模式来改进
131135
132136## 概述解决方案
133137
134- - 解除依赖
138+ - 解除依赖
135139只要解除Editor和引擎的依赖,把替换引擎的逻辑隔离出去就可以实现
136140
137141
138142
139143## 给出UML
140144
145+ ** 领域模型**
141146
142147![ 领域模型图] ( ./story_improve/UML.png )
143148
144- 总体来看,分为用户 、编辑器、引擎、容器这四个部分
149+ 总体来看,领域模型分为用户 、编辑器、引擎、容器这四个部分
145150
146151我们看下用户这个部分:
147152
@@ -171,14 +176,15 @@ Three.js是Three.js引擎
171176
172177DependencyContainer是保存注入的Engine接口实现的容器,提供操作它的get和set函数
173178
174- ** 依赖关系**
179+ <!-- **依赖关系** -->
180+ 我们来看下依赖关系:
175181
176182Client通过依赖注入的方式注入Engine接口的一个实现(BabylonImplement或者ThreeImplement),从而使Editor能够调用它来创建场景
177183
178184
179185## 结合UML图,描述如何具体地解决问题
180186
181- - 替换Three.js为Babylon.js引擎现在不再影响Editor了,只需要增加BabylonImplement,并让Client从注入ThreeImplement改为注入BabylonImplement即可
187+ - 替换Three.js为Babylon.js引擎现在不再影响Editor了,只需要增加BabylonImplement,并让Client从注入ThreeImplement改为注入BabylonImplement即可
182188因为Editor只依赖Engine接口,所以Engine接口的实现的变化不会影响Editor
183189
184190
@@ -226,7 +232,7 @@ Editor增加了injectDependencies函数,它将Client注入的Engine接口实
226232
227233createScene函数通过DependencyContainer获得注入的Engine接口实现,它的类型是Engine接口,用来创建场景
228234
229- 值得注意的是:
235+ 值得注意的是:
230236Editor只依赖Engine接口,没有依赖Engine接口的实现
231237
232238
@@ -337,61 +343,62 @@ BabylonImplement的implement函数使用了Babylon.js引擎,返回了Engine接
337343
338344## 通用UML
339345
346+ ** 领域模型**
340347
341348![ 领域模型图] ( ./role_abstract/UML.png )
342349
343350
344351<!-- ## 分析角色 -->
345352
346- 我们来看看模式的相关角色:
353+ <!-- 我们来看看模式的相关角色: -->
347354
348- 总体来看,分为用户 、系统、外部依赖、容器这四个部分
355+ 总体来看,领域模型分为用户 、系统、外部依赖、容器这四个部分
349356
350357
351358我们看下用户这个部分:
352359
353- - Client
360+ - Client
354361该角色是用户,通过依赖注入的方式注入DependencyImplement
355362
356363我们看下系统这个部分:
357364
358- - System
365+ - System
359366该角色使用了外部依赖,它只知道外部依赖的接口(Dependency)而不知道具体实现(DependencyImplement)
360367
361368
362369我们看下外部依赖这个部分:
363370
364- - Dependency
371+ - Dependency
365372该角色是外部依赖的接口
366- - DependencyImplement
373+ - DependencyImplement
367374该角色是Dependency的实现
368- - DependencyLibrary
375+ - DependencyLibrary
369376该角色是一个库
370377
371378
372379我们看下容器这个部分:
373380
374- - DependencyContainer
381+ - DependencyContainer
375382该角色是保存注入的DependencyImplement的容器,提供操作它的get和set函数
376383
377384
378- ## 角色之间的关系
385+ ** 角色之间的关系**
379386
380- - 可以有多个Dependency
387+ - 可以有多个Dependency
381388如除了Engine以外,还可以有File、Server等Dependency,其中每个Dependency对应一个外部依赖
382389
383- - 一个Dependency可以有多个DependencyImplement来实现
390+ - 一个Dependency可以有多个DependencyImplement来实现
384391如Engine的实现除了有ThreeImplement,还可以有BabylonImplement等实现
385392
386393
387394- Client可以依赖注入多个Dependency接口的实现。其中,对于一个Dependency接口而言,Client只依赖注入实现它的一个DependencyImplement
388395
389396- 因为System可以使用多个Dependency接口,所以它们是一对多的关系
390397
391- - 一个DependencyImplement一般只使用一个DependencyLibrary,但也可以使用多个DependencyLibrary
398+ - 一个DependencyImplement一般只使用一个DependencyLibrary,但也可以使用多个DependencyLibrary
392399如可以增加实现Engine接口的ThreeAndBabylonImplement,它同时使用Three.js和Babylon.js这两个DependencyLibrary来创建场景
393400
394- - 只有一个DependencyContainer容器,它保存了所有注入的DependencyImplement,为每个DependencyImplement都提供了get和set函数
401+ - 只有一个DependencyContainer容器,它保存了所有注入的DependencyImplement,为每个DependencyImplement都提供了get和set函数
395402
396403
397404
@@ -500,9 +507,9 @@ export let api1 = function () {
500507
501508依赖隔离模式主要遵循下面的设计原则:
502509
503- - 依赖倒置原则
510+ - 依赖倒置原则
504511系统依赖于外部依赖的抽象(Dependency)而不是外部依赖的细节(DependencyImplement和DependencyLibrary),从而外部依赖的细节的变化不会影响系统
505- - 开闭原则
512+ - 开闭原则
506513要隔离更多的外部依赖,只需要增加对应的Dependency、DependencyImplement和DependencyLibrary,以及DependencyContainer增加对应的闭包变量和get、set函数即可,无需修改System;要替换外部依赖的实现,只需要对它的Dependency增加更多的DependencyImplement,然后Client改为注入新的DependencyImplement即可,无需修改System;要修改已有的外部依赖(如升级版本),只需要修改DependencyImplement和DependencyLibrary即可,无需修改System
507514
508515依赖隔离模式也应用了“依赖注入”、“控制反转”的思想
@@ -513,11 +520,11 @@ export let api1 = function () {
513520
514521## 优点
515522
516- - 提高系统的稳定性
523+ - 提高系统的稳定性
517524外部依赖的变化不会影响系统
518- - 提高系统的扩展性
525+ - 提高系统的扩展性
519526可以任意替换外部依赖而不影响系统
520- - 提高系统的可维护性
527+ - 提高系统的可维护性
521528系统与外部依赖解耦,便于维护
522529
523530
@@ -538,23 +545,19 @@ export let api1 = function () {
538545
539546- 编辑器使用的引擎、UI库等第三方库需要替换
540547
541- - 编辑器使用的引擎、UI库等第三方库的版本需要升级
542-
548+ - 编辑器使用的引擎、UI库等第三方库的版本需要升级
543549如需要升级编辑器使用的Three.js引擎的版本,只需要升级作为DependencyLibrary的Three.js,并修改ThreeImplement,使其使用升级后的Three.js即可
544550
545- - 需要增加编辑器使用的引擎、UI库等第三方库
546-
551+ - 需要增加编辑器使用的引擎、UI库等第三方库
547552如需要让编辑器在已使用Three.js引擎的基础上增加使用Babylon.js引擎,则只需要加入ThreeAndBabylonImplement,让它同时使用Three.js和Babylon.js这两个DependencyLibrary
548553
549554
550555
551556
552557## 注意事项
553558
554- - Dependency要足够抽象,才不至于在修改或增加DependencyImplement时需要修改Dependency,从而影响System
555-
556- 当然,在开发阶段难免考虑不足,如一开始只有一个DependencyImplement时,Dependency往往只会考虑这个DependencyImplement。这导致在增加实现该Dependency的其它DependencyImplement时发现需要修改Dependency,使其更加抽象,这样才能容纳因增加更多的DependencyImplement而带来的变化
557-
559+ - Dependency要足够抽象,才不至于在修改或增加DependencyImplement时需要修改Dependency,从而影响System
560+ 当然,在开发阶段难免考虑不足,如一开始只有一个DependencyImplement时,Dependency往往只会考虑这个DependencyImplement。这导致在增加实现该Dependency的其它DependencyImplement时发现需要修改Dependency,使其更加抽象,这样才能容纳因增加更多的DependencyImplement而带来的变化
558561因此,我们可以允许在开发阶段修改Dependency,但是在发布前则确保Dependency已经足够抽象和稳定
559562
560563<!-- - 有多少个Dependency接口,DependencyContainer就有多少个get和set函数 -->
@@ -572,7 +575,7 @@ export let api1 = function () {
572575
573576- 划分4个层:外部依赖层、应用服务层、领域服务层、领域模型层,其中前者为上层,前者依赖后者
574577<!-- - 外部依赖都位于外部依赖层,它们是按照依赖隔离模式设计的,在运行时由用户注入 -->
575- - 将系统的所有外部依赖都使用依赖隔离模式来隔离出去,为每个外部依赖抽象一个Dependency接口。将DependencyImplement放到最上层的外部依赖层,将Dependency放到最下层的领域模型层。
578+ - 将系统的所有外部依赖都使用依赖隔离模式来隔离出去,为每个外部依赖抽象一个Dependency接口。将DependencyImplement放到最上层的外部依赖层,将Dependency放到最下层的领域模型层。
576579这是因为DependencyImplement容易变化,所以将其放到最上层,这样它的变化不会影响其它层;而Dependency非常稳定不易变化,且被领域模型依赖,所以将其放到领域模型层
577580<!-- 其它三层不依赖外部依赖层,而是依赖领域模型层中的Application Core(具体就是依赖Dependency) -->
578581- 运用领域驱动设计来设计系统,将系统的核心逻辑建模为领域模型,将其放到领域模型层
@@ -637,7 +640,7 @@ export let api1 = function () {
637640
638641### 满足各种修改外部依赖的用户需求
639642
640- 我遇到过这种问题:3D应用开发完成后,交给3个外部用户使用。用了一段时间后,这3个用户提出了不同的修改外部依赖的要求:第一个用户想要升级3D应用依赖的引擎A,第二个用户想要替换引擎A为引擎B,第三个用户想要同时使用引擎B和升级后的引擎A。
643+ 我遇到过这种问题:3D应用开发完成后,交给3个外部用户使用。用了一段时间后,这3个用户提出了不同的修改外部依赖的要求:第一个用户想要升级3D应用依赖的引擎A,第二个用户想要替换引擎A为引擎B,第三个用户想要同时使用引擎B和升级后的引擎A。
641644如果3D应用没有使用依赖隔离模式,而是使用调用引擎这个外部依赖的话,我们就需要将交付的代码修改为3个版本,分别满足3个用户的需求。交付的3个版本的代码如下:
642645
643646- 使用了升级A的3D应用代码
0 commit comments