复杂软件设计之道:领域驱动设计全面解析与实战
上QQ阅读APP看书,第一时间看更新

2.1 有界上下文

使用第1章的一套基本逻辑来理解有界上下文:领域即边界,边界靠分类,分类需从内外部入手。

当人们想认识一个事物时,需要注意其边界,如同在平面上画一个圈,就有了圈内和圈外,圈内属于事物的内部,而圈外属于事物所处的上下文、环境和背景。

事物外部上下文会对事物内部属性产生影响,虽然不是本质上的,但是人类观察到的结果如此。由于领域模型是人类主观的认识模型,因此不能将模型与其环境割裂开来。

上下文还体现在语言的表达上,例如下面的句子。

1)冬天:能穿多少穿多少;夏天:能穿多少穿多少。

2)剩女产生的原因有两个,一是谁都看不上,二是谁都看不上。

3)单身的原因:原来是喜欢一个人,现在是喜欢一个人。

“能穿多少穿多少”“谁都看不上”“喜欢一个人”字面上一样,意思却完全不一样。那么其真实含意从哪里去推敲呢?很显然从句子内部去分析得不到任何结果,那么从句子所处的上下文来理解。当在冬天这个上下文说“能穿多少穿多少”时,表示穿得越多越好;而在夏天这个上下文说:“能穿多少穿多少”时,表示穿得越少越好。第二、三句中的含意则是从上下文中的“剩女”“单身”去理解。

当提炼一个概念或模型时,不能只着眼于这个概念的内部特征,这是不自觉的直观认识,还需要注意其所处上下文,那么在DDD中有界上下文的定义是什么呢?

有界上下文是根据逻辑一致性划分软件的模式。也就是说:有界上下文是人根据客观事物中的一致性逻辑去划分软件。注意上下文不是软件中的模块,而是一个微服务。

有界上下文是主观和客观结合的一套方法,客观事物是一直变化运动的,没有静止的一条边界,DDD中的边界是人为的一种划分,但是这种划分也不是随意主观的,而是根据客观事物的逻辑来划分,应保持逻辑一致性。

那么逻辑一致性代表什么?其实很简单,不矛盾的逻辑即为一致性,避免矛盾可从避免重复开始,重复重叠是最明显的矛盾。

“上下文”这个中文词语本身就有“承上文启下文”的意思,语境中需要照顾到上下文,不能太突兀,比如本来上文谈论A事情,下文突然又谈到B事情,有可能就变成话题终结者了。

当然,这样的上下文也不是没有边界,比如昨天谈的是A事情,过了一天还在谈A事情,这个上下文未免隔得太久了。

有界上下文是指在空间或时间上有边界的一段环境背景,它确定了每个模型的适用范围,模型体现了这个范围内的逻辑一致性。

这里有一个难点是:有界上下文是无形的,它不能直接反映出来,其逻辑一致性只能通过模型显现出来。模型的内部定义体现了其所在的上下文,例如从某个人说话的口音可以推断他是南方人还是北方人,南方或北方是上下文,口音是模型的内部定义。

看看下面这个Person的模型:

这个模型内部有工号、职位,体现了这个模型是出于一个与职场有关的上下文,如果这个模型还有一个属性为“有几个孩子”,那么这个属性就出现与职位、工号等矛盾的地方了。

注意,Person这个名称可能存在误导,对于一个人,他有工作,也有生活,因此,在Person里面放入工作和生活相关的属性都是允许的,所以,这里引出了命名问题。名称其实显式界定了该模型所处的有界上下文,这个名称可以称为统一语言,不过这个统一语言不是在整个领域统一的语言,而是在有界上下文边界内统一的语言。

图2-1所示为领域、有界上下文和边界内统一语言的关系。“销售”这个词语是圆圈所示上下文内的统一语言。如果说模型必须反映有界上下文的一致性,那么统一语言就是模型中这种一致性边界的更加抽象和严格化的表示。

图2-1 三者关系

2.1.1 统一语言:统一项目中的交流语言

语言是DDD的核心,DDD使用语言来表达想法、探索问题并定义解决方案。如果领域是复杂的,那么这种语言将是丰富而复杂的。

统一语言又称为无处不在的语言或通用语言,从这些名称可以知道,统一语言是大家统一使用的术语,不会让别人产生歧义。

统一语言是在整个团队的协商下发展起来的,这种无所不在的、通用语言必须在团队成员之间的任何场合都可以表达,以及可以用软件模型来表示。它是由团队中的开发人员、领域专家和其他参与者共享的语言。

统一语言不是由领域专家指定的语言,而是应该由每个参与者共同协商,所以,类似ERP里面指定术语的方式是不可行的,因为即使人们被强行接受这些标准用语,对其理解也可能是片面的。它不应该在组织中由上而下,而应该由下而上整理出来。

没有统一语言会发生下面这些不利现象。

1)由于缺少统一语言导致人们对其他人的概念需要进行“翻译”,这对领域模型建立非常不利,并会导致错误的领域模型。

产品团队和技术团队的沟通产生问题大部分是因为这种翻译。技术团队喜欢从技术角度去理解业务,使用关系数据库或NoSQL数据库替代业务描述,喜欢用Map或集合数据结构替代业务聚集概念,并且自以为在使用一种抽象的方法对业务进行总结,当他们这样和产品团队进行沟通时,可能很难理解产品经理表达的概念,或者经常会反问:这个具体怎么实现?技术人员所询问的“怎么实现”意思是:用技术概念进一步阐述怎么实现。产品团队设计好对象模型以后,技术团队可能疑惑其中的继承关系如何在数据库中实现。

2)更多的公司是缺乏共同的统一语言,团队成员使用不同的术语而没有意识到。

对于这一点最能感同身受的应该是新员工。新员工与不同团队成员打交道时,意识到他们不同的用词实际代表一个意思,但是又没有形成统一语言,这种情况下,软件系统不但异常不稳定,而且无法得到进一步维护和发展,因为大家对业务的理解产生了分歧。语言代表思想,不同术语也代表了理解的分歧。

3)还有很多公司实际上存在统一语言,但是却并不使用。

例如在召开项目销售大会或技术大会时,却使用不同的词语,这使得项目销售人员和技术人员之间形成了天然的隔阂。这种划分销售大会和技术大会的做法客观上加深了业务人员和技术人员的边界划分,造成他们的一种错觉——他们属于不同的圈子,可以使用不同的语言来谈论同一件事。

4)业务数据不开放导致人们接触不到统一语言。在大型企业中,数据因为安全考虑并没有开放给所有人,比如业务分析专家能够接触的数据,普通技术人员可能无法接触到,技术人员因此而无法使用准确的业务语言去表达,这是造成企业内部信息隔阂的主要原因。

假设为保险公司建立一个投保系统,如果能够形成一种能够以精确的方式谈论领域知识而又没有误解风险的语言,那岂不是很好吗?如果是这样,保险行业的人们谈论的内容就正是模型的内容。技术人员会和保险专家共同决定“停保和退保”的真正含义以及两者的差异,停保以后可以续保,但是退保以后就无法续保,也就是说,退保才是真正地停止投保,而所谓“停保”只是暂时停保的意思。明白了这些用词的差别以后,“某人刚刚投保了两个月就停保了,后来隔了一年又续保”,这句话就可以被明确地理解并且正确无误地实现。

统一语言必须在领域模型中表达出来,主要体现在领域模型中的名称上。

例如投保领域中停保或退保这些动作是对什么模型进行操作?停止保险是暂停保险,那么这个“保险”是什么意思?是保险产品吗?如果是,那么停保或退保就放入保险产品这个对象里。注意这样其实是把投保的结果强加到保险产品中了,而投保是表达参保人和保险产品的一种关系。这种投保关系必须使用统一语言表达出来,如果只使用“新参保”、“新投保”或“参保关系”这样的语言表达,无法专业表达领域模型,这时应该在保险业务领域寻找专门的名词来表达。这么重要的名称在保险领域不可能没有专门术语,例如可以使用“保单”一词,“保单”代表了投保关系,停保或续保是对保单进行操作,改变了保单状态。

统一语言也能帮助解决领域专家表达的不准确和逻辑矛盾之处。有界上下文的模型必须体现逻辑一致性,不能发生逻辑矛盾,这一点从讨论模型的语言中也可以看出,如果需求人员使用略有不同的词语谈论同一件事情,但是他又不承认他可能在谈不同的事情,在这种言语表达和指称不统一的矛盾中可以发现隐含的模型。

例如为电力公司开发一套电费结算系统,技术人员与电力财务专家接触时会发现,电价是一个表达模型的统一语言,但是电力公司从供电企业采购的电价与电力公司通过电表计价销售给最终用户的电价还不一样,两者虽然都是使用“电价”这个词语,但属于不同上下文,由此可以判定采购电价与销售电价是两个不同模型,甚至销售电价还有企业销售电价和家庭销售电价之分。

统一语言也必须落实到代码中。图纸、术语表和其他文档的一个主要缺点是难以跟随时间随时更新,如果代码能够直接反映统一语言,那么显然形成了从需求、建模到代码的直通车,这样的代码易于理解、没有歧义、不掺杂技术因素,能够准备表达业务模型。例如上一节的Person案例,Person这个类名与其内部职业属性相比过于宽泛,名不副实,相应的语言表达就没有落实到代码的类名称上。代码必须表达无所不在的统一语言。

2.1.2 如何发现有界上下文和统一语言?

统一语言比较难确定,主要是取决于上下文。上下文界定需要根据时间和空间维度,例如开一次会议形成统一语言和模型,那么这个会议就成了这个统一语言的上下文,这非常类似于法律名词解释,某个名词如果在某条法规中强行被解释,那么就必须引用这条法规来解释这个名词,例如前面提及的番茄是属于蔬菜还是水果,法律规定了它是蔬菜,那么它在法律这个上下文中就只能是蔬菜了。

领域专家、产品团队之间的交流也可能存在歧义,虽然不推荐做语言“警察”,逢歧义必较真,但是也只有通过公开讨论,如头脑风暴和分析现有文档、词典、标准等,才能提出更好、更精确、更通用的统一语言。

1)首先,可以通过画图的方式去发现。画图简单扼要,可以在白板上表达自己的业务领域,不要担心它们是否为正式设计;也可以事先使用UML工具画出UML图来表达自己的理解。最好的软件是没有软件,代码实现非常昂贵,因为代码实现涉及太多细节,一旦整个思考方向发生变化,所有的细节就要全部摧毁,重新来一次,而使用画图方式则很便宜。

2)其次,通过专家小组会议创建词汇表,定义所有所需术语的词汇表。注意这个词汇表应该是在征询意见的基础上,切不可由上而下强行推广。改变人们的语言习惯很难,如同改掉方言口音一样。

3)最后,可以通过事件风暴的方式发现统一语言和有界上下文,领域专家和开发人员可以实现对业务流程迭代学习的快速循环,从而促进统一语言的发展。

在发现有界上下文和统一语言的过程中可以发现业务规则,而业务规则是一种业务逻辑的强有力的约束表示,业务规则的强逻辑性更容易发现和表达。从流程中寻找规则,发现统一语言,甚至以此可以再造新流程,因为一旦理清业务规则里面的逻辑,并且能彼此衔接全覆盖,那么业务流程可能会更加有效率,例如现在市民去政府办事只需到市民中心一站式办理,这个市民中心整合了多个组织部门的流程,新流程背后的业务规则其实没有变,也就是事务使用的规则审核没有变,而是将办事的步骤进行了合并。

通过业务规则有助于发现强烈的逻辑关系,这是逻辑一致性的体现,进而可以发现最小边界的有界上下文,然后通过上下文中的模型显式表达这种逻辑一致性,这样就能完成DDD建模中最具创新的环节。当然,对于逻辑一致性的认识不是一蹴而就的,业务规则、业务流程只是帮助发现它的手段之一,找到事物内在的一致性需要具有深入的领域知识以及进行不断的思想碰撞。

在实践中,还有一种无意识地发现上下文的方式,这是通过组织形式去发现。这里存在一个康威定理(Conways Law),它的要义是:组织形式决定架构。它指出系统架构代表实现系统的组织的沟通结构,对于软件中的每个模块,都对应有一个组织单元;组织单元之间存在沟通依赖,软件中对应的模块之间同样存在依赖关系。如果一个组织想要开发一个大系统,由于大型团队中的沟通很困难,团队会在某个特定大规模下崩溃。由于沟通和架构相互影响,沟通不良会导致混乱的架构和额外的复杂性。它可以解释为什么一个大型重要项目可能有一个糟糕的架构,即使是非常重要的项目也难以进一步发展。

很多公司和组织已经意识到这个问题,因此会将一个大型项目划分为一个个小项目,分配给一个个小团队去完成。关键问题是,依据什么标准将一个大型项目划分为小项目?答案是根据领域边界去划分,一个子域对应一个团队;也可以按照解决方案中的有界上下文去划分。子域划分比较明显,但是有界上下文划分就可能不是那么明显,这需要多年经验或比较强的分析能力,当然如果管理效率高,就可以根据上下文的调整而及时调整团队人员和组织结构。

下面以某电力公司的电费结算领域为案例,说明如何通过组织的形式进行有界上下文的边界发现和划分。本章后面的小节会对其他主要发现方法进行详细探讨,这些方法都可以相互结合。

首先需要了解一下领域知识、业务策略或业务规则。电力公司是干什么的?它是电力这个商品的批发商,从发电企业购买电力,通过电网输送到用电用户,再向用电用户收取电费。其商业模式很简单,它的信息系统主要是管理金钱的进出,在上下游差价和巨额资金截流中赚取利润,这应该是其核心业务策略。

该业务策略落实到业务流程,就体现在部门组织设置上。营销部负责面向供电用户销售电力和收取电费,交易中心负责向电厂集中购电,财务部门则是对购销双方进行统一资金结算。不同部门负责不同的领域,现在可以根据这种原则划分三种有界上下文:购电上下文;销电上下文和结算上下文,同时将参与开发的团队也相应地划分成三个团队。

这是一种根据业务流程涉及的不同组织来划分上下文的方法,这种划分方法是一种朴素直接的方式,但是需要知道,业务流程是业务规则的表象,如果只是根据流程进行划分,有可能没有抓住领域的本质(流程和组织部门可能不断调整,属于变化的因子),造成软件系统成为其落后流程的附属品,可能并没有实现整合资金流、信息流和资源流等高级目相,但是这种方式有助于技术人员第一次接触业务时理解业务项目。

有了三个有界上下文之后,下一步是处理这三个上下文之间的关系。

2.1.3 有界上下文之间的关系

有界上下文之间关系的处理基本原则是以松耦合、解耦合为主,因为不同的有界上下文有不同的团队、代码库、技术体系,如果两两之间过于耦合,就会发生两个团队经常在一起开会沟通、影响效率的情况,也可能是两个上下文的边界划分得不清晰,需要重新审视。

当然,一个大型系统还是需要集成不同的上下文,特别是一个复杂的大型流程可能涉及不同上下文系统的集成,这时可以从集成这个角度去理解上下文关系。

上下文之间的关系有很多类型,这里只讨论常用的几种。

1)共享内核:指两个有界上下文共同使用一份代码内核(例如一个库)。这种方式已经很少使用,因为共享一份代码,如同共享一个数据库一样,单点风险大。

很多公司会有一个独立的平台技术团队,这是团队共享的基础结构层,那么自然很多人就认为在业务领域是否也可以设立这样一个共享团队,例如商品目录管理团队为其他团队提供商品品种的基本信息管理。提供业务基础信息管理的上下文属于共享内核,但是,值得注意的是,除非商品产品模型更改不大,否则引发的修改范围会涉及很多上下文,因此需要花力气做好自动化集成测试。

2)开放主机服务也是一种上下文关系的映射,也称为上下游关系映射或API调用,一个上下文通过RPC等同步方式调用另外一个上下文的API,调用者是被调用者的客户端。

这种方式在如今的微服务架构中比较普及。两个上下文通过服务接口耦合在一起,如果一方服务接口改动,那么调用这个服务接口的另一方代码也需要改动,这就要求团队之间沟通合作紧密(康威定律),而微服务的设计目标是团队之间应该尽量少沟通、少开会,因为这种跨团队沟通的效率是很低的,个别程序员之间私下商量后可能无意中做出影响整个数据设计原则的事情,而架构师无法参与讨论,无法了解具体的实现情况有没有问题。

这种模式在新旧上下文系统之间使用时,需要引入防腐层,防止两个上下文系统直接耦合,旧的上下文系统会影响新上下文系统的代码编写思路,因此必须引入第三层解耦。

3)发布/订阅模式:一个有界上下文是发布者,另外一个有界上下文订阅这个发布者,当发布者有事件发生时,及时将事件发布给所有订阅者(这非常类似微信,当你订阅公众号以后,它们有更新时会及时通知你),这样两个上下文之间不再互相依赖,而是只依赖事件或消息,使得两者之间实现最大化的松耦合,这也是集成模式的主要实现方式。

这种方式也是DDD主导的上下文交互模式,领域事件在建模阶段的事件风暴中发现,然后通过领域事件才会发现有界上下文。也就是说,领域事件已经隐含了发布订阅模式,如果从分析领域的第一步就着眼于这种高度松耦合的思路,那么整个软件系统也自然会高度独立,又易于集成。

当然,这种新的思路需要创新和勇气,更多人还是采取API直接调用的方式,这种同步方式需要注意网络中断等问题,引入断路器等模式防止故障级联爆炸,进行业务事务的重试,同时确保服务的幂等性。断路器模式会有很多中间件去实现,但是这些实现大都没有从业务事务的角度考虑,而只是从弹性角度设计。仅仅有弹性是不够的,怎样保证服务之间的调用故障不会影响业务流程?调用失败再重试后,业务流程是否重新执行了一遍?对业务规则的执行有没有影响?会不会发生数据重复等?如果重试失败或快速失败后,是不是这个调用流程中涉及的所有步骤都能回滚,都恢复到调用之前的状态?

现在回到前面讨论的电费结算领域案例中:交易中心的购电上下文主要是面向供电企业;营销部的销电上下文主要面向用电企业;结算上下文主要负责两者之间的资金结算处理。这三种上下文之间采取上述哪种上下文关系实现通信或集成呢?这三种上下文关心的目标对象并不涉及彼此,结算上下文关心不是交易中心的结果,而购电和销电上下文关心的目标是外部企业,因此,它们之间的相互独立性比较强,那么发布订阅模式就比较适合这个案例。

4)发布的语言(Published Language):两个有界上下文中的模型需要一种共同语言进行相互翻译转换,如同两个不同语言的人常常需要选择英语进行交流一样。所谓发布的语言是指一种大家都能够理解、解释的语言,很多行业基于XML建立各自行业的标准语言,如美国健康医疗领域的HL7标准,为支持临床电子健康信息的交换、集成、共享和检索提供了全面的框架和相关标准。

以上是几种常用的有界上下文关系,下一节从子域视角对有界上下文进行分析。

2.1.4 核心子域、支持子域与通用子域

以电费结算领域为例,当技术团队深入理解结算上下文以后,会发现结算其实不是那么简单,其中还有结算处理、结算清分、支付、财务等流程,结算清分实际就是分账,将向下游用电用户收取的电费分账支付给上游供电企业,财务系统是一套需要符合国家标准的财务电算化系统,这是一种通用的系统。这里需要引入子域的概念。

什么是子域?为了实现公司最终的大目标,必须在不同的小目标范围中工作,它们被称为子域,因为它们自身并不足以使公司取得成功,合起来以后共同构成了公司的业务领域。

子域分为核心子域、支持子域和通用子域。

1)核心子域:这是必须尽最大努力的地方,正是它使公司发挥作用,为公司带来价值,使公司在竞争中脱颖而出。它是最重点的地方,业务策略和规则的重点实施地。

2)支持(辅助)子域:是核心子域的辅助支持,介于核心与通用之间,如果没有它,核心子域无法成功,因此,它也是非常重要的;需要内部开发或外包,因为没有现成的解决方案来实现。注意它是需要介入代码开发或业务设计的,如果只是有人协助进行配置和运维的则不属于这类。支持子域不能提供任何竞争优势,因此不应该很复杂。它的业务逻辑应该足够简单,可以通过一些快速的应用程序开发框架推出。

3)通用子域:是所有公司以相同方式执行的事务。它通常是现成的解决方案,但也可以外包或内部开发。它没有为主要业务带来特定的规则,即在大多数情况下可以作为服务采购使用,当然也可能派人参与运维配置,但是没有介入软件设计或代码修改等。例如应收账款跟踪、费用分类和其他账务信息,这些都可以使用通用的会计财务系统来处理。

那么,结算上下文中哪些是核心子域呢?这也是根据业务策略来判断的,业务策略应该是人们的关心重点。从“结算”这个统一语言词语上应该了解到,它是为上下游企业用户分钱,把下游的钱付给上游,分得清楚准确是其主要关注点,那么结算处理、结算清分应该是其核心子域。而支付会涉及和银行支付方式的接口,属于支持子域,当然如果考虑沉淀资金的可观利息有可能支付也是核心,但是获取利息收入可能属于另外一个上下文,不是结算上下文所关心的。最后,财务子域无疑属于通用子域,这是行业通用系统。

现在可以使用图2-2来表达这个电费结算领域的上下文和子域了。图中虚线代表子域,而实线代表上下文,一共有三个上下文。这里有一个特殊处理:将面向上游供电企业的上下文和面向下游用电用户的上下文合并到了交易营销系统中,财务管控系统是一个独立上下文系统,结算上下文最终涉及四个子域:结算实例、结算处理、清分结算和支付管理子域。

图2-2 电费结算领域的划分

这样整个业务领域的划分结构就比较清晰了,可以依此进行团队划分,对每个上下文指派一个团队负责设计开发,并且规划好上下文之间的访问方式。最佳实践是:一个有界上下文对应一个子域,对应一个团队,对应一个微服务,当然,也不能僵化执行这种对应关系,还是需要根据问题空间的子域划分、解决方案空间的上下文划分,以及团队人员能力等因素综合分析。

2.1.5 实例解析:电费结算系统

前面章节已经在DDD概念阐述中穿插了电费结算领域的案例,这里进行一下总结。

电费结算领域的业务策略是什么?电力公司是电费结算领域的从业者,核心商业模式是从供电企业批发电力,通过电网输送到用电企业,为上下游用户的资金进行结算分账,如图2-3所示。

图2-3 电费结算系统用例

这个系统有两种用户:供电公司和用电用户。供电公司主要关心自己的电力被使用了多少,应该收取多少资金,而用电用户关心的是自己使用了多少电力,应该缴纳多少电费。这两种用户的关心重点是不同的,供电企业希望电价越高越好,用电量越多越好,用电用户希望电价越低越好,这导致了不同的业务策略,那么在这个电费结算领域中需要表达这两种策略。

当然,还有一种更重要的业务策略:结算处理策略。它对资金进出进行跟踪、计算处理和分账,所谓分账是将从用电用户收取的电费分给上游供电企业,当然沉淀的电费资金也有可观的收益,需要进行财务管理和投资。

这样,电费结算领域其实有三种不同的业务策略,结算处理主要关心的策略与上下游用户关心的策略重点是不同的,业务策略不同决定了不同的有界上下文。

当技术团队到电力公司现场进行交流时,会发现这里存在两个部门:交易中心负责对供电企业的购电合同签订,营销部门负责销售电力和收取电费。

一个有界上下文对应一个组织部门,对应一个开发团队,这好像是一种习惯,虽然可以朴素地这么做,但是有时还是需要深入分析领域本质,防止被表面上的组织形式误导。

这三个有界上下文之间如何通信?因为涉及三个不同的组织团队,如果经常开会沟通交流会影响效率。应尽量采取一种松耦合的方式,通过API调用会导致API接口耦合,一方接口变动,调用的另外一方会需要改动代码。幸运的是,在这个案例中,三个有界上下文面向的重点不同,不是盯住彼此,交易中心的服务对象是供电企业,关心的是购电合同;而营销服务的对象是用电企业,关心的是电费收取,结算上下文关心的是资金。这样,它们之间的是非常松散的,如果三个上下文需要互相交流访问,可主要采取发布/订阅的方式,尽可能少地使用共享内核方式。

当进入这三个上下文内部时,发现内部还是比较复杂性的。以结算上下文来看,结算处理涉及三个步骤:预处理、处理和清分分账,这三个子域是核心子域,它们完成电力电费的结算分账工作。

对沉淀的资金进行财务投资管理的系统则可能是通用子域,使用通用的财务投资管理系统就可以完成;财务管控系统则属于支持子域,用于支持对资金进出的监控管理。

核心子域是必须尽最大努力的地方,它是公司核心竞争力的地方,对于核心子域,需要集中精兵强干进行DDD领域建模,将最善于解决复杂性的人力投放于此。

对于支持子域,可使用简单的解决方案,使用普通事务脚本或数据库CRUD模式就足够了,当然,在不会危及核心子域的前提下也可以外包。而通用子域通过购买可能更便宜,不要自己实现,哪怕它的技术很新潮诱人,可能会为简历增光添彩(请记住,绝对不能那么做)。