188年足球比分直播模块依赖关系

随着程序的大小,重要的是要把它分成多个模块,这样你不需要理解所有的小修改。通常这些模块可以由不同的团队,并动态地组合。在这个188年足球比分直播的文章我使用Presentation-Domain-Data分层分割一个小程序。然后我重构这些模块之间的依赖关系引入服务定位器和依赖注入模式。这些适用于不同的语言,但看起来有些不同,所以我给这188足球比分直播在Java和JavaScript没有阶级的风格。.戊酸-长时间癌                                             

2015年10月13日

找到 类似的文章通过观察这些标签: 188年足球比分直播· API bet188足球· 应用程序体系结构

在程序运行超过几百行代码,您需要考虑如何把他们分成模块。至少是有用的小文件,以便更好地管理你的编辑。但更严重你想分割程序,这样您就不需要把它在你的脑海中,以做出改变。.戊酸-长时间癌                                             

井bet188足球ed模块化结构应该允许你只懂一个小一个大程序的一部分,当你需要做一个小变化。有时一个小变化将横切模块,但是大多数时候你只需要了解单个模块及其邻国。.戊酸-长时间癌                                             

把程序分解为模块中最难的部分是决定模块边界应该是什么。没有容易遵循的指导方针,实际上我生活的一个重要主题的作品,试图理解好的模块边界会是什么样子。也许最重要的部分画好模块边界是关注您所作的改变和188足球比分直播你的代码,这样的代码变化一起在附近相同或模块。.戊酸-长时间癌                                             

除此之外的机制使分离的各个部分之间的关系。在最简单的情况下你客户端模块,叫供应商。但往往这些客户和供应商的配置可以纠缠在一起了,因为你总是想让客户端程序不知道太多的供应商是如何组合在一起的。.戊酸-长时间癌                                             

我要和一个例子,探讨这个问题,我将一大块代码,看看它如何能分裂成碎片。事实上我要做这两次,使用两种不同的语言:Java和JavaScript,尽管他们类似的名称是非常不同的时候为模块化的功能。.戊酸-长时间癌                                             


起点(s)

我们从创业开始,进行复杂的数据分析销售数据。他们有这样的有价值的指标,Gondorff数,这是一个非常有用的预测销售的产品。他们的web应用程序需要一个公司的销售数据,将其输入复杂的算法,然后输出一个简单的表的产品及其Gondorff数字。.戊酸-长时间癌                                             

初始状态的代码都是在一个文件中,我将介绍部分。首先是释放在HTML表的代码。.戊酸-长时间癌                                             

应用程序。js

函数emitGondorff(产品)(产品){ {函数行返回['”、“$ {产品}”、“$ { gondorffNumber(产品)。toFixed (2)}”、“']。加入(' \ n ');}返回encodeForHtml ('
      
       \ n $ {产品。地图(线)。加入(“\ n”)} \ n
      
”);}

我不使用多行字符串作为缩进的要求在输出不缩进的源代码。.戊酸-长时间癌                                             

这不是世界上最复杂的UI,积极行人在一个单页的世界和响应。唯一重要的事情,对于这个示例,UI需要调用gondorffNumber函数在不同的点。.戊酸-长时间癌                                             

接下来我将搬到gondorff数量的计算。.戊酸-长时间癌                                             

应用程序。js

函数gondorffNumber(产品){返回salesDataFor(产品,gondorffEpoch(产品),hookerExpiry())。找到(r = > r。日期。匹配(/ 01 /美元))。量*数学。π;}函数gondorffEpoch(产品){ const countingBase = recordCounts(baselineRange(产品);返回deriveEpoch(countingBase);}

函数baselineRange(产品){ / /修订}函数deriveEpoch(countingBase){ / /修订}函数hookerExpiry(){ } / /修订

可能不会看起来像一个百万美元的算法,但值得庆幸的是没有这个代码的重要部分。最重要的部分是这个逻辑是关于计算gondorff数量需要两个函数(salesDataForrecordCounts),只是从某种类型的数据返回基本数据的销售来源。这些数据源功能并不是特别复杂,他们只是过滤一些数据来自一个CSV文件。.戊酸-长时间癌                                             

应用程序。js

函数salesDataFor(产品,开始,结束){返回salesData()。过滤器(r = >(r。产品= = =)& &(新日期(r。开始日期)> =)& &(新日期(r。结束日期)<));}函数recordCounts(开始){返回salesData()。过滤器(r = >新日期(r。日期)> =开始)。长度}函数salesData () {const = readFileSync(的销售数据。csv,{编码:“use utf8”});返回数据。分割(“\ n”)。片(1)。地图(makeRecord);}函数makeRecord(线){常量(产品、日期、quantityString位置)=线。split(\ s * / / \ s *);const数量=方法(quantityString 10);返回{产品、日期、数量、位置};}

这些函数是完全无聊这个讨论的而言,我只告诉他们的完整性。最重要的,他们把数据从数据源,按摩到简单的对象,并提供它在两个不同口味的核心算法的代码。.戊酸-长时间癌                                             

此时的java版本非常相似,第一代的HTML。.戊酸-长时间癌                                             

类应用。.戊酸-长时间癌                                             .戊酸-长时间癌                                             .戊酸-长时间癌                                             

公共字符串emitGondorff(列表
      
       产品){列表
       
        结果= new ArrayList < > ();结果。添加(“\ n
        
         ");(字符串p:产品)的结果。添加(字符串。格式(“"
         "p gondorffNumber (p)));结果。添加(“"
         
% s % 4。2 f
");HtmlUtils返回。编码(结果。流()。收集(收藏家。加入(“\ n”)));}

gondorff算法

类应用。.戊酸-长时间癌                                             .戊酸-长时间癌                                             .戊酸-长时间癌                                             

公共双gondorffNumber产品(String){返回salesDataFor(产品,gondorffEpoch(产品),hookerExpiry())。过滤器(r - > r。获取当前日期()。toString ()。匹配(“.戊酸-长时间癌                                             * 01美元”))。findFirst ()。get()。getQuantity() *数学。π;}私人LocalDate gondorffEpoch(字符串产品){最终长countingBase = recordCounts(baselineRange(产品);返回deriveEpoch(countingBase);}

私人LocalDate baselineRange(字符串产品){/ /修订}私人LocalDate deriveEpoch(长基地){/ /修订}私人LocalDate hookerExpiry(){/ /是的,修订了}

由于数据源的主体代码并不是那么重要,我就显示方法声明

类应用

私人流
      
       salesDataFor(String产品,LocalDate开始,LocalDate){ / /不重要的细节}私人长recordCounts(LocalDate开始){ } / /不重要的细节
      

Presentation-Domain-Data分层

我早些时候说,设置模块边界是一个微妙的和微妙的艺术,但有一个指南,许多人跟随Presentation-Domain-Data分层——分离表示代码(UI)、业务逻辑层和数据访问层。有充分的理由后,这种分裂。这三个类别涉及思考不同的问题,而且经常使用不同的框架来协助工作。此外还有一个渴望替换多个演示使用相同的核心业务逻辑,或者业务逻辑在不同环境中使用不同的数据源。.戊酸-长时间癌                                             

所以对于这个例子我要遵循这一常见的分割,我还将强调代换的理由。毕竟这gondorff数是这样一个有价值的指标,很多人会想利用它——鼓励我包作为一个单元,由多个应用程序可以很容易地重用。而且并不是所有的应用程序将继续他们的销售数据在csv文件中,将使用一个数据库或一个远程microservice。我们希望应用程序开发人员能够以gondorff代码并把它插到她特定的数据源,她可以写或从另一个开发人员。.戊酸-长时间癌                                             

但是在我们开始之前188足球比分直播,让这一切,我需要强调的是,presentation-domain-data分层确实有其局限性。模块化的一般规则是我们想限制的后果如果我们能改变到一个模块。但单独presentation-domain-data模块经常一起做必须改变。添加一个数据字段的简单行为通常会导致所有三个更新。因此我喜欢使用这种方法在较小的范围,但更大的应用程序需要高层模块沿着不同的路线发展。特别是你不应该使用presentation-domain-data层作为团队的基础边界。.戊酸-长时间癌                                             


执行分

我将开始分裂成模块通过分离表示。对于JavaScript的情况,这几乎是仅仅剪切和粘贴代码到一个新的文件。.戊酸-长时间癌                                             

gondorff。es6

出口的默认函数gondorffNumber…gondorffEpoch(产品){…函数baselineRange(产品){…函数deriveEpoch(countingBase){…函数hookerExpiry(){…函数salesDataFor(产品,开始,结束){…函数recordCounts(开始){…函数salesData(){…函数makeRecord(线){…

通过使用出口违约我可以导入参考gondorffNumber我只需要添加一个导入语句。.戊酸-长时间癌                                             

应用程序。es6

从“进口gondorffNumber。/ gondorff。es6”

在java端,几乎直接。我再次复制以外的一切emitGondorff一个新类。.戊酸-长时间癌                                             

类Gondorff……

公共双gondorffNumber(字符串产品){…私人LocalDate gondorffEpoch(字符串产品){…私人LocalDate baselineRange(字符串产品){…私人LocalDate deriveEpoch(长基地){…私人LocalDate hookerExpiry(){…流
      
       salesDataFor (String产品,LocalDate开始,LocalDate){…长recordCounts (LocalDate开始){…流
       
        salesData(){…私人SalesRecord makeSalesRecord (String) {…
       
      

原来的应用程序类我不需要,除非我把新类导入到一个新的包,但我确实需要实例化新类。.戊酸-长时间癌                                             

类应用。.戊酸-长时间癌                                             .戊酸-长时间癌                                             .戊酸-长时间癌                                             

公共字符串emitGondorff(列表
      
       产品){列表
       
        结果= new ArrayList < > ();结果。添加(“\ n
        新Gondorff ()。.戊酸-长时间癌                                             
        
         ");(字符串p:产品)的结果。添加(字符串。格式(“"
         "、磷、gondorffNumber (p)));结果。添加(“"
         
% s % 4。2 f
");HtmlUtils返回。编码(结果。流()。收集(收藏家。加入(“\ n”)));}

我现在想做第二个分离计算逻辑和代码提供的数据记录。.戊酸-长时间癌                                             

数据源。es6……

导出功能salesDataFor(产品,开始,结束){

导出功能recordCounts (start) {

函数salesData(){…函数makeRecord(线){…

之间的区别这一举动和前面的一个是gondorff文件需要导入两个函数,而不是一个。它可以做进口,没有其他需要改变。.戊酸-长时间癌                                             

Gondorff。es6……

从“进口{ salesDataFor,recordCounts }。/数据源。es6”

java版本非常类似于前面的情况下,进入一个新类,实例化一个新对象的类。.戊酸-长时间癌                                             

类数据源……

公共流
      
       salesDataFor(String产品,LocalDate开始,LocalDate){…公众长recordCounts(LocalDate开始){…流
       
        salesData(){…私人SalesRecord makeSalesRecord (String) {…
       
      

类Gondorff。.戊酸-长时间癌                                             .戊酸-长时间癌                                             .戊酸-长时间癌                                             

公共双gondorffNumber(字符串产品){回报新数据源()。.戊酸-长时间癌                                             salesDataFor(产品,gondorffEpoch(产品),hookerExpiry ())。过滤器(r - > r。获取当前日期()。toString ()。匹配(“.戊酸-长时间癌                                             * 01美元”))。findFirst ()。get()。getQuantity() *数学。π;}私人LocalDate gondorffEpoch(字符串产品){最终长countingBase =新数据源()。.戊酸-长时间癌                                             recordCounts(baselineRange(产品));返回deriveEpoch(countingBase);}

这种分离成文件是一个机械的过程,这不是真的那么有趣。但这是一个必要的第一步,我们就会到达有趣188足球比分直播。.戊酸-长时间癌                                             


链接器替换

分割成几个模块的代码是有益的,但所有这些有趣的困难是分发Gondorff计算作为一个单独的组件。目前Gondorff计算假定销售数据来自一个csv文件与一个特定的路径。分离数据源逻辑给我一些变化的能力,但目前的机制是尴尬的,还有其他选择探索。.戊酸-长时间癌                                             

当前的机制是什么?基本上这就是我所说的链接器替换。“链接”这个词是一种倒退回像C编译程序,在链接阶段解决符号在单独的编译单位。在JavaScript中我可以实现通过操纵的道德等效为import命令查找路径的文件。.戊酸-长时间癌                                             

假设我想安装此应用程序的环境中,他们没有销售记录保存在一个CSV文件,而是一个SQL数据库上运行一个查询。做这项工作我首先需要创建一个CorporateDatabaseDataSource文件导出功能salesDataForrecordCounts返回数据的形式Gondorff文件预计它。然后我将数据源文件替换为这个新的一个。当我运行应用程序”链接”替换数据源文件。.戊酸-长时间癌                                             

对于许多动态语言依赖某种路径查找链接机制,链接器替换是一个很好的技术简单的组件替换。我不需要做任何事情与我的代码让它工作,除了简单的分离到的文件我刚完成。如果我有一个构建脚本,可以建立不同的数据来源的代码环境中通过不同的文件复制到适当的点的路径。这说明将一个项目分解成小块的优势——它允许替换那些碎片,即使最初的作家没有替换。它使不可预见的定制。.戊酸-长时间癌                                             

用Java做链接器替换本质上是相同的任务。我需要包数据源在一个单独的jar文件Gondorff,然后指示的用户Gondorff创建一个类数据源用适当的方法,把它放到类路径中。.戊酸-长时间癌                                             

然而与Java我会做一个额外的步骤,应用提取接口在数据源上。.戊酸-长时间癌                                             

公共接口数据源{流
      
       salesDataFor (String产品,LocalDate开始,LocalDate结束);长recordCounts (LocalDate开始);}
      
公共类CsvDataSource实现数据源{

使用一个所需接口这样是有用的因为它使显式函数gondorff预计从它的数据源。.戊酸-长时间癌                                             

动态语言的缺点之一是他们缺乏明确性,这可能是一个问题,当分别结合已经开发。静态JavaScript的模块系统运作得很好,因为它定义了模块的依赖关系,所以他们是显式的,可以静态地检查。静态声明有成本和收益,一个不错的发展在最近语言bet188足球在一个更微妙的方式静态声明而不是仅仅把语言当作纯粹的静态或动态。.戊酸-长时间癌                                             

链接器替代的优点在于,它需要很少的工作组件的一部分作者,所以适合在不可预见的定制。但它也有它的缺点。在某些环境中,如Java,它可以的。代码不显示替换是如何工作的,所以没有控制代码库的替代机制。.戊酸-长时间癌                                             

缺乏存在的一个重要结果的代码替换不能发生动态,一旦程序被组装和运行,我不能改变数据源。这通常不是一个大问题在生产中,有热交换的数据源的情况下是有用的,但他们是少数的病例。但是动态替换带有测试的价值。想要使用很常见测试双打提供罐头的数据进行测试,这常常意味着我要扔在不同的双打不同的测试用例。.戊酸-长时间癌                                             

这些要求更大的直言不讳的代码库和动态替换测试,通常使我们探索其他方法,允许我们指定的组件是如何接线的明确而不是依赖路径查找。.戊酸-长时间癌                                             


数据源与每个调用参数

如果我们想支持调用gondorff与不同的数据源,然后一个显而易见的方法是通过每次我们叫它作为参数。.戊酸-长时间癌                                             

让我们来看看这个看起来在Java版本第一,从当前的Java版本开始,后提取数据源接口

类应用。.戊酸-长时间癌                                             .戊酸-长时间癌                                             .戊酸-长时间癌                                             

公共字符串emitGondorff(列表
      
       产品){列表
       
        结果= new ArrayList < > ();结果。添加(“\ n
        
         ");(字符串p:产品)的结果。添加(字符串。格式(“"
         "、p、新Gondorff ()。gondorffNumber(p)));结果。添加(“"
         
% s % 4。2 f
");HtmlUtils返回。编码(结果。流()。收集(收藏家。加入(“\ n”)));}

类Gondorff。.戊酸-长时间癌                                             .戊酸-长时间癌                                             .戊酸-长时间癌                                             

公共双gondorffNumber产品(String){返回新CsvDataSource()。salesDataFor(产品,gondorffEpoch(产品),hookerExpiry ())。过滤器(r - > r。获取当前日期()。toString ()。匹配(“.戊酸-长时间癌                                             * 01美元”))。findFirst ()。get()。getQuantity() *数学。π;}私人LocalDate gondorffEpoch(字符串产品){最终长countingBase = new CsvDataSource()。recordCounts(baselineRange(产品));返回deriveEpoch(countingBase);}

通过数据源作为参数,生成的代码是这样的。.戊酸-长时间癌                                             

类应用。.戊酸-长时间癌                                             .戊酸-长时间癌                                             .戊酸-长时间癌                                             

公共字符串emitGondorff(列表
      
       产品){列表
       
        结果= new ArrayList < > ();结果。添加(“\ n
        新的CsvDataSource()
        
         ");(字符串p:产品)的结果。添加(字符串。格式(“"
         "、p、新Gondorff ()。gondorffNumber (p,)));结果。添加(“"
         
% s % 4。2 f
");HtmlUtils返回。编码(结果。流()。收集(收藏家。加入(“\ n”)));}

类Gondorff。.戊酸-长时间癌                                             .戊酸-长时间癌                                             .戊酸-长时间癌                                             

公共双gondorffNumber(字符串产品,数据源数据源){返回数据源.戊酸-长时间癌                                             salesDataFor(产品,gondorffEpoch(产品、数据源),hookerExpiry())。过滤器(r - > r。获取当前日期()。toString ()。匹配(“.戊酸-长时间癌                                             * 01美元”))。findFirst ()。get()。getQuantity() *数学。π;}私人LocalDate gondorffEpoch(String产品,数据源数据源){最终长countingBase =数据源.戊酸-长时间癌                                             recordCounts(baselineRange(产品));返回deriveEpoch(countingBase);}

我能做这188足球比分直播在一些小步骤。.戊酸-长时间癌                                             

  • 使用添加参数gondorffEpoch添加数据源
  • 代替打电话新的CsvDataSource()使用补充道数据源参数
  • 编译和测试
  • 重复做gondorffNumber

现在到JavaScript版本,这里的当前状态。.戊酸-长时间癌                                             

应用程序。es6……

从“进口gondorffNumber。/ gondorff。es6”

函数emitGondorff(产品)(产品){ {函数行返回['”、“$ {产品}”、“$ { gondorffNumber(产品)。toFixed (2)}”、“']。加入(' \ n ');}返回encodeForHtml ('
      
       \ n $ {产品。地图(线)。加入(“\ n”)} \ n
      
”);}

Gondorff。es6……

从“进口{ salesDataFor,recordCounts }。/数据源。es6”

出口的默认函数gondorffNumber(产品){返回salesDataFor(产品,gondorffEpoch(产品),hookerExpiry())。找到(r = > r。日期。匹配(/ 01 /美元))。量*数学。π;}函数gondorffEpoch(产品){ const countingBase = recordCounts(baselineRange(产品);返回deriveEpoch(countingBase);}

在这种情况下我能通过两个函数作为参数

应用程序。es6……

从“进口gondorffNumber。/ gondorff。es6”从“进口*作为数据源。/数据源。es6”

函数emitGondorff(产品)(产品){ {函数行返回['”、“$ {产品}”、“$ {gondorffNumber(产品,数据源。salesDataFor数据源。recordCounts).戊酸-长时间癌                                             toFixed (2)}”、“']。加入(' \ n ');}返回encodeForHtml ('
      
       \ n $ {产品。地图(线)。加入(“\ n”)} \ n
      
”);}

Gondorff。es6……

  从“进口{ salesDataFor,recordCounts }。/数据源。es6”出口的默认函数gondorffNumber(产品,salesDataFor recordCounts){返回salesDataFor(产品,gondorffEpoch(产品、recordCounts)hookerExpiry())。找到(r = > r。日期。匹配(/ 01 /美元))。量*数学。π;}函数gondorffEpoch(产品,recordCounts){ const countingBase = recordCounts(baselineRange(产品);返回deriveEpoch(countingBase);}

与java示例一样,我可以申请添加参数gondorffEpoch首先,编译和测试,然后做同样的事情gondoffNumber为每一个函数。.戊酸-长时间癌                                             

在这种情况下,我倾向于把这两个salesDataForrecordCounts函数到一个数据源对象并传递相反——本质上使用引入参数对象.戊酸-长时间癌                                             我不会做这个在这篇文章中,主要因为它是一个更好的示范操作一流的功能如果我不。但如果gondorff需要从数据源我会使用更多的功能。.戊酸-长时间癌                                             

Paramterizing数据源文件的名字

作为进一步我可以参数化数据源的文件名。java版本我通过添加一个字段为数据源,使用的文件名添加参数它的构造函数。.戊酸-长时间癌                                             

类CsvDataSource……

私人字符串文件名;公共CsvDataSource(字符串文件名){。文件名=文件名;}

类应用…

公共字符串emitGondorff(列表
       
        产品){
        数据源数据源= new CsvDataSource (“销售。csv”);;列表
        
         结果= new ArrayList < > ();结果。添加(“\ n
         数据源
         
          ");(字符串p:产品)的结果。添加(字符串。格式(“"
          "、p、新Gondorff ()。gondorffNumber (p,)));结果。添加(“"
          
% s % 4。2 f
");HtmlUtils返回。编码(结果。流()。收集(收藏家。加入(“\ n”)));}

我需要使用JavaScript版本添加参数功能需要在数据源。.戊酸-长时间癌                                             

数据源。es6……

导出功能salesDataFor(产品,开始,结束,文件名){返回salesData (文件名)。过滤器(r = > (r。产品= = =)& &(新日期(r。开始日期)> =)& &(新日期(r。结束日期)<));}出口函数recordCounts(开始,文件名){返回salesData (文件名)。过滤器(r = >新日期(r。日期)> =开始)。长度}

离开,这将迫使我向gondorff把文件名参数功能,但实际上他们不需要了解。我可以解决这个问题通过创建一个简单的适配器。.戊酸-长时间癌                                             

dataSourceAdapter。es6……

*作为ds从导入”。/数据源。es6”出口默认函数(文件名){返回{salesDataFor(产品、开始、结束){返回ds。salesDataFor(产品,开始,结束,文件名)},recordCounts(开始){返回ds。recordCounts(开始,文件名)}}}

应用程序代码使用这个适配器时通过数据源到gondorff函数。.戊酸-长时间癌                                             

应用程序。es6……

从“进口gondorffNumber。/ gondorff。es6”从“进口*作为数据源。/数据源。es6”
从“进口createDataSource。/ dataSourceAdapter。es6”

函数emitGondorff(产品){功能线(产品){const数据源= createDataSource(的销售。csv”);;返回['”、“$ {产品}”、“$ {gondorffNumber(产品,数据源。salesDataFor数据源。recordCounts)。toFixed (2)}”、“']。加入(' \ n ');}返回encodeForHtml ('
       
        \ n $ {产品。地图(线)。加入(“\ n”)} \ n
       
”);}

权衡,参数化

通过与每个调用数据源gondorff给我我要找的动态替换。作为应用程序开发人员可以使用任何数据源我喜欢,我也可以轻松地测试通过存根数据源每当我需要。.戊酸-长时间癌                                             

但也有缺点,每次使用一个参数调用。首先,我必须通过数据源(或函数)作为参数在gondorff,要么需要每个函数,或者需要调用另一个函数。这可以导致数据源被一个流浪汉的数据里四处走动。.戊酸-长时间癌                                             

更严重的问题是,现在每一次我有一个应用程序模块使用gondorff我必须确保我能创建和配置数据源。这可以很容易混乱的如果我有一个更复杂的配置,用一个通用的组件,需要一些所需的组件,每一个都有自己的设置所需的组件。每次我使用gondorff嵌入知识如何我配置gondorff对象。这是一个复杂的重复代码更难理解和使用。.戊酸-长时间癌                                             

我可以想象这个通过查看依赖关系。之前介绍的数据源作为参数的依赖关系是这样的:

当我通过数据源作为参数看起来像这样。.戊酸-长时间癌                                             

在这些图中,我区分使用依赖性和创建依赖关系。使用依赖意味着客户端模块调用函数上定义供应商。总是会有一个使用gondorff和数据源之间的依赖关系。创建的依赖是一个更加亲密的依赖,因为你通常需要知道更多关于供应商模块配置和创建它。(创建依赖意味着使用依赖性。每次调用)使用一个参数减少了依赖从gondorff从创建到使用,但引入了一个创建依赖于任何应用程序。.戊酸-长时间癌                                             

以及创建depenendency问题,还有另一个问题,因为我真的不想改变数据源在生产代码。每个调用的参数传递给gondorff意味着我不同参数之间的调用,但每当我打电话gondorffNumber我总是传递相同的数据源。失调容易混淆我六个月时间。.戊酸-长时间癌                                             

如果我有相同的数据源的配置,设置它是有意义的一次,是指每次我使用它。但如果我这样做,我不妨gondorff设置一次,并使用一个完全配置gondorff每次我想使用它。.戊酸-长时间癌                                             

因此探索每一次使用一个参数是什么样子,我将利用我的版本控制系统和做一个硬重置到我在这一部分的开始所以我可以探索另一条路径。.戊酸-长时间癌                                             


单一的服务

gondorff和数据源的一个重要性质,它们都可以作为单一的服务对象。服务对象的一部分埃文斯的分类,指的是一个对象的面向一个活动而不是集中在数据实体或值。我经常将服务对象称为“服务”,但他们属于不同的SOA中的服务,因为他们没有网络访问的组件。在功能的世界中,服务往往只是功能,但有时你找到你想要的情况下把一组函数当作一件事。我们认为这与数据源,我们有两个函数,我能想到的一个数据源。.戊酸-长时间癌                                             

我还说“奇异”,在这里,我指的是概念意义只有一个整整一个执行上下文。由于服务通常是无状态的,只有一个是有意义的。如果是单数在执行上下文中,这意味着我们可以引用它在我们的全球计划。我们甚至可能想迫使它是一个单例,也许因为它是昂贵的设置或有并发限制资源操作。可能只是其中一个在整个过程中运行,或可能会出现更多状况,如每个线程使用一个表存储。但无论如何,我们的代码而言,只有其中的一个。.戊酸-长时间癌                                             

如果我们选择使我们gondorff计算器和数据源是单一服务,然后配置它们是有意义的一次,在应用程序的启动,然后把他们后来当使用它们。.戊酸-长时间癌                                             

这引入了一种分离的方式服务处理:分离的配置和使用。有几个方法可以重构代码做这种分离:引入Service Locator模式或依赖注入模式。我将从服务定位器。.戊酸-长时间癌                                             


引入服务定位器

Service Locator模式背后的想法是有一个奇点组件定位服务。定位器是一个注册表的服务。在使用中,客户机使用全球查找注册表,然后要求为一个特定的服务注册表。配置设置与所需的所有服务定位器。.戊酸-长时间癌                                             

188年的第一步足球比分直播介绍创建定位器。这是一个非常简单的结构,一个全球纪录,所以我的JavaScript版本是几个变量和一个简单的初始化。.戊酸-长时间癌                                             

serviceLocator。es6……

出口让salesDataFor;出口让recordCounts;出口让gondorffNumber;导出函数初始化(arg){ salesDataFor:参数。salesDataFor;recordCounts:参数。recordCounts;gondorffNumber =参数。gondorffNumber;}

出口让出口一个变量对其他模块作为只读视图。.戊酸-长时间癌                                             [1]

当然,Java是冗长的。.戊酸-长时间癌                                             

类ServiceLocator……

私有静态ServiceLocator soleInstance;私人数据源的数据源;私人Gondorff Gondorff;公共静态数据源的数据源(){soleInstance返回。数据源;{ }公共静态Gondorff Gondorff()返回soleInstance。gondorff;}公共静态空间初始化(arg ServiceLocator){ soleInstance =参数;}公共ServiceLocator(数据源的数据源,Gondorff Gondorff){。数据源=数据源;这一点。gondorff = gondorff;}

我喜欢在这种情况下静态方法是提供一个接口,以便客户的定位器不需要知道数据存储的地方。但是我喜欢用一个单例实例数据,作为测试,使它更容易替换。.戊酸-长时间癌                                             

在这两种情况下,服务定位器是一组属性。.戊酸-长时间癌                                             

188年足球比分直播的JavaScript使用定位器

定义了定位器后,下一步是开始服务,我从gondorff开始。配置服务定位器,我会写一个小模块配置服务定位器。.戊酸-长时间癌                                             

configureServices。es6……

进口*的定位器。/ serviceLocator。es6 ';从“进口gondorffImpl。/ gondorff。es6 ';出口的默认函数(){定位器。初始化({gondorffNumber: gondorffImpl});}

我需要确保这个函数是进口和在应用程序启动。.戊酸-长时间癌                                             

一些启动文件…

从“进口initializeServices。/ configureServices。es6 ';;

initializeServices ();;

刷新我们的记忆,这是当前应用程序代码(早些时候恢复后)。.戊酸-长时间癌                                             

应用程序。es6……

从“进口gondorffNumber。/ gondorff。es6”

函数emitGondorff(产品)(产品){ {函数行返回['”、“$ {产品}”、“$ { gondorffNumber(产品)。toFixed (2)}”、“']。加入(' \ n ');}返回encodeForHtml ('
       
        \ n $ {产品。地图(线)。加入(“\ n”)} \ n
       
”);}

使用服务定位器相反,所有我需要做的是调整import语句。.戊酸-长时间癌                                             

应用程序。es6……

  从“进口gondorffNumber。/ gondorff。es6”
从“进口{gondorffNumber}。/ serviceLocator。es6 ';;

我可以运行测试只有这一变化,以确保我没有搞砸(这听起来比”发现我怎么乱”)。这改变了我做一个类似的数据源。.戊酸-长时间癌                                             

configureServices。es6……

进口*的定位器。/ serviceLocator。es6 ';从“进口gondorffImpl。/ gondorff。es6 ';;从“进口*作为数据源。/数据源。es6 ';;出口的默认函数(){定位器。初始化({salesDataFor:数据源。salesDataFor,
recordCounts:数据源。recordCounts,gondorffNumber:gondorffImpl });}

Gondorff。es6……

从“进口{ salesDataFor,recordCounts }。/serviceLocator.戊酸-长时间癌                                             es6”

我可以使用相同的188足球比分直播前参数化文件名称,这次的变化只影响服务配置功能。.戊酸-长时间癌                                             

configureServices。es6……

进口*的定位器。/ serviceLocator。es6 ';从“进口gondorffImpl。/ gondorff。es6 ';;从“进口*作为数据源。/数据源。es6 ';;
从“进口createDataSource。/ dataSourceAdapter。es6”出口的默认函数(){const数据源= createDataSource(的销售。csv”);;定位器。初始化({ salesDataFor:数据源。salesDataFor recordCounts:数据源。recordCounts gondorffNumber: gondorffImpl});}

dataSourceAdapter。es6……

*作为ds从导入”。/数据源。es6”出口默认函数(文件名){返回{salesDataFor(产品、开始、结束){返回ds。salesDataFor(产品,开始,结束,文件名)},recordCounts(开始){返回ds。recordCounts(开始,文件名)}}}

Java

java的情况看起来是一样的。我创建了一个配置类填充服务定位器。.戊酸-长时间癌                                             

类ServiceConfigurator……

公开课ServiceConfigurator {公共静态空run(){ ServiceLocator定位= new ServiceLocator(null,新的Gondorff());ServiceLocator。初始化(定位);} }

并确保我有一个电话在应用程序启动。.戊酸-长时间癌                                             

当前的应用程序代码是这样的:

类应用…

公共字符串emitGondorff(列表
       
        产品){列表
        
         结果= new ArrayList < > ();结果。添加(“\ n
         
          ");(字符串p:产品)的结果。添加(字符串。格式(“"
          "、p、新Gondorff ()。gondorffNumber(p)));结果。添加(“"
          
% s % 4。2 f
");HtmlUtils返回。编码(结果。流()。收集(收藏家。加入(“\ n”)));}

我现在使用定位器获取gondorff对象。.戊酸-长时间癌                                             

类应用…

公共字符串emitGondorff(列表
       
        产品){列表
        
         结果= new ArrayList < > ();结果。添加(“\ n
         ServiceLocator。gondorff()
         
          ");(字符串p:产品)的结果。添加(字符串。格式(“"
          "、磷、.戊酸-长时间癌                                             gondorffNumber(p)));结果。添加(“"
          
% s % 4。2 f
");HtmlUtils返回。编码(结果。流()。收集(收藏家。加入(“\ n”)));}

将数据源对象添加到混合,我开始通过将其添加到定位器。.戊酸-长时间癌                                             

类ServiceConfigurator……

公开课ServiceConfigurator{公共静态空run () {ServiceLocator定位= new ServiceLocator (新的CsvDataSource()、新Gondorff());ServiceLocator。初始化(定位);} }

目前gondorff对象看起来像这样:

类Gondorff……

公共双gondorffNumber产品(String){返回新CsvDataSource()。salesDataFor(产品,gondorffEpoch(产品),hookerExpiry ())。过滤器(r - > r。获取当前日期()。toString ()。匹配(“.戊酸-长时间癌                                             * 01美元”))。findFirst ()。get()。getQuantity() *数学。π;}私人LocalDate gondorffEpoch(字符串产品){最终长countingBase = new CsvDataSource()。recordCounts(baselineRange(产品));返回deriveEpoch(countingBase);}

使用服务定位器因此改变

类Gondorff……

公共双gondorffNumber(字符串产品){回报ServiceLocator。数据源().戊酸-长时间癌                                             salesDataFor(产品,gondorffEpoch(产品),hookerExpiry ())。过滤器(r - > r。获取当前日期()。toString ()。匹配(“.戊酸-长时间癌                                             * 01美元”))。findFirst ()。get()。getQuantity() *数学。π;}私人LocalDate gondorffEpoch(字符串产品){最终长countingBase =ServiceLocator。数据源().戊酸-长时间癌                                             recordCounts(baselineRange(产品));返回deriveEpoch(countingBase);}

与JavaScript的情况下,参数化文件名只是改变了服务配置代码。.戊酸-长时间癌                                             

类ServiceConfigurator……

公开课ServiceConfigurator{公共静态空run () {ServiceLocator定位= new ServiceLocator(新CsvDataSource ("销售。csv”"),新Gondorff ());ServiceLocator。初始化(定位);} }

使用服务定位器的后果

使用服务定位器的直接影响是改变我们三个组件之间的依赖性。简单的组件分工后,我们可以看到像这样的依赖关系。.戊酸-长时间癌                                             

引入服务定位器移除所有的创造主模块之间的依赖关系。.戊酸-长时间癌                                             [2].戊酸-长时间癌                                             当然这是忽略了配置服务模块,所有创建依赖关系。.戊酸-长时间癌                                             

我敢肯定你们中的一些人可能已经注意到,做的应用程序定制服务配置函数,这意味着任何自定义是由相同的链接器替换机制,我早些时候说我们需要远离。在某种程度上是这样,但事实显然是独立的服务配置模块给了我更多的灵活性。库提供者可以提供一个范围的数据源的实现,和客户可以编写一个服务配置模块,将选择一个在运行时根据配置参数配置文件等环境变量,或者命令行变量。有一个潜在的188足球比分直播在这里引入参数从一个配置文件,但我将离开一天。.戊酸-长时间癌                                             

但使用服务定位器的一个特定的结果是,我现在可以轻松地替换服务进行测试。我可以把一个测试存根gondorff的数据源是这样的:

测试……

(可以存根数据源,函数(){const data =[{产品:“p”日期:“2015-07-01”,数量:175});const newLocator = { recordCounts:()= > 500年salesDataFor:()= >数据,gondorffNumber:serviceLocator。gondorffNumber };serviceLocator。初始化(newLocator);断言。接近(549。7787年,serviceLocator。gondorffNumber (“p”),0。001);});;

类试验机。.戊酸-长时间癌                                             .戊酸-长时间癌                                             .戊酸-长时间癌                                             

@Test公共空can_stub_data_source () {ServiceLocator抛出异常。初始化(新ServiceLocator(新DataSourceStub(),ServiceLocator。gondorff ()));assertequal(549。7787年,ServiceLocator。gondorff ()。gondorffNumber (“p”),0。001);私人类DataSourceStub实现数据源}{@Override公共流
       
        salesDataFor(String产品,LocalDate开始,LocalDate){返回集合。singletonList(新SalesRecord(“p”LocalDate。(1)2015年,7日,175))。流();}@Override公共长recordCounts (LocalDate开始){返回500;} }
       

分相

当我在这篇文章中,我参观了Kent Beck。后喂他自制的奶酪,我们的话题转到188足球比分直播的话题,他告诉我一个重要的188足球比分直播,他承认十年前,但从来没打过一个像样的书面形式。这188足球比分直播涉及到一个复杂的计算和分裂成两个阶段,第一阶段通过第二阶段的结果和一些中间结果的数据结构。这种模式的一个大规模的例子是使用编译器,将他们的工作分为许多阶段:分词,解析代码生成,数据结构,如令牌溪流和解析树作为中间结果。.戊酸-长时间癌                                             

当我回到家,再次开始这篇文章,我很快认识到,推出这样的服务定位器是一个例子的分相188足球比分直播。我提取的配置服务对象到其自己的阶段使用服务定位器作为中间结果通过配置服务阶段的结果的程序。.戊酸-长时间癌                                             

这样的分裂计算到单独的阶段是一个有用的188足球比分直播,因为它允许我们单独考虑每个阶段的不同需求,有明显的迹象表明的结果(在中间结果),每个阶段和每个阶段可以独立测试通过检查或提供的中间结果。这188足球比分直播效果非常好,当我们把中间结果作为一个不可变的数据结构,给我们带来的好处处理后阶段代码,而无需思考突变行为在任何数据生成的早期阶段。.戊酸-长时间癌                                             

在我写这篇文章时,它几乎和肯特郡的一个月,但我觉得分相的概念是一个强大的一个用于188足球比分直播。像许多伟大的模式有显著性的概念——我觉得这只是把名字几十年来我一直在做的事情。但这样一个名字不是小事,一旦你的名字这样一个经常使用的技术,它使它更容易和别人谈谈,改变我自己的思考:给它更多的核心作用和故意使用比是当它在不知不觉中完成的。.戊酸-长时间癌                                             


依赖注入

使用服务定位器的缺点,组件对象需要知道服务定位器是如何工作的。这不是一个问题,如果gondorff计算器只使用易于理解范围的应用程序的上下文中使用相同的服务定位器机械、但我想应该把它卖给我的财富,耦合是一个问题。即使我所有的买家使用服务定位器,不太可能,他们都使用相同的API。我需要的是一种gondorff配置数据源的方式不需要任何机械以外的内置语言本身。.戊酸-长时间癌                                             

这是导致的需要不同的配置形式,被称为依赖注入。依赖注入是鼓吹很多,特别是在Java世界中,与各种各样的框架来实现它。虽然这些框架可以是有用的,基本思想是非常简单,我将说明它与188足球比分直播这个例子简单实现。.戊酸-长时间癌                                             

Java示例

的核心理念是,您应该能够编写的组件(比如gondorff对象,而不需要知道任何特殊约定或工具配置相关的组件。自然的方法是在Java gondorff对象有一个字段保存数据源。该字段可以通过服务配置一般填充方法填充任何领域——要么setter或在施工期间。自从gondorff对象需要一个数据源做任何有用的东西,我通常的方法是把它放到构造函数。.戊酸-长时间癌                                             

类Gondorff……

私人数据源的数据源;{这个公共Gondorff(数据源的数据源)。数据源=数据源;}
私人的数据源getDataSource (){返回(数据源!= null)?数据源:ServiceLocator。数据源();}公共双gondorffNumber(字符串产品){回报getDataSource ().戊酸-长时间癌                                             salesDataFor(产品,gondorffEpoch(产品),hookerExpiry ())。过滤器(r - > r。获取当前日期()。toString ()。匹配(“.戊酸-长时间癌                                             * 01美元”))。findFirst ()。get()。getQuantity() *数学。π;}私人LocalDate gondorffEpoch(字符串产品){最终长countingBase =getDataSource ().戊酸-长时间癌                                             recordCounts(baselineRange(产品));返回deriveEpoch(countingBase);}

类ServiceConfigurator……

公开课ServiceConfigurator{公共静态空run () {ServiceLocator定位= new ServiceLocator(新CsvDataSource (“销售。csv”),新Gondorff());ServiceLocator。初始化(定位);} }

通过将访问器getDataSource我能做188足球比分直播在较小的步骤。这段代码与配置完成服务定位器工作正常,我能逐渐取代测试设置定位器测试,使用这个新的依赖注入机制。第一个188足球比分直播就添加字段和适用添加参数.戊酸-长时间癌                                             调用者可以使用构造函数使用一个空参数最初我可以一次提供一个数据源,测试后的变化。(当然,因为我们所做的所有服务配置在配置阶段,通常有来电者并不多。我们得到更多的电话在哪里测试的存根。)

类ServiceConfigurator……

公开课ServiceConfigurator {公共静态空run(){数据源的数据源= new CsvDataSource(“销售。csv”);ServiceLocator定位= new ServiceLocator(数据源、新Gondorff(数据源));ServiceLocator。初始化(定位);} }

一旦我做了,我可以删除所有对服务定位器从gondorff对象的引用。.戊酸-长时间癌                                             

类Gondorff……

私人数据源getDataSource () {回报(数据源!= null)?数据源:ServiceLocator。数据源();;返回数据源;}

我也可以内联getDataSource如果我感到如此倾向

JavaScript的例子

因为我避开类在JavaScript的例子中,一种方式以确保gondorff计算器源函数获取数据没有额外的框架是通过他们与每个调用作为参数。.戊酸-长时间癌                                             

Gondorff。es6……

  从“进口{ recordCounts }。/ serviceLocator。es6”出口的默认函数gondorffNumber(产品、salesDataFor recordCounts){返回salesDataFor(产品,gondorffEpoch(产品、recordCounts),hookerExpiry())。找到(r = > r。日期。匹配(/ 01 /美元))。量*数学。π;}函数gondorffEpoch(产品、recordCounts){ const countingBase = recordCounts(baselineRange(产品);返回deriveEpoch(countingBase);}

当然之前我做了这种方法,但是这一次需要确保客户不需要做任何设置每个调用。我可以通过为客户提供部分应用gondorff函数。.戊酸-长时间癌                                             

configureServices。es6……

进口*的定位器。/ serviceLocator。es6 ';从“进口gondorffImpl。/ gondorff。es6 ';从“进口createDataSource。/ dataSourceAdapter。es6 '出口默认函数(){ const数据源= createDataSource(“销售。csv”);定位器。初始化({ salesDataFor:数据源。salesDataFor recordCounts:数据源。recordCounts,gondorffNumber:(产品)= > gondorffImpl(产品,数据源。salesDataFor数据源。recordCounts)});}

后果

如果我们看看在使用阶段的依赖关系,图中是这样的。.戊酸-长时间癌                                             

之间唯一的区别和使用服务定位器是早些时候gondorff之间不再有任何依赖关系和服务定位器,这是使用依赖注入的全部意义。(配置阶段依赖同一组创建依赖关系。)

一旦我把依赖从gondorff服务定位器,我还可以从服务定位器完全删除数据源字段如果没有任何其他类需要一个数据源的服务定位器。我也可以使用依赖注入提供gondorff对象应用程序类,虽然在做更少的价值,由于应用程序类不是共享的,因此不是弱势群体通过使用定位器。常见的服务定位器和依赖注入模式一起使用,与使用服务定位器的初始服务进一步配置已经通过依赖注入。依赖注入容器通常用作服务定位器通过提供一种机制来查找服务。.戊酸-长时间癌                                             


最终的想法

这个188年的关键信息足球比分直播一集是分裂阶段的服务配置使用的服务。你如何使用服务定位器和依赖注入来执行这倒不是个大问题,你在,取决于具体情况。这种情况下很可能让你打包框架来管理这些依赖项,或者如果你的案子很简单也许可以自己滚。.戊酸-长时间癌                                             


分享:
如果你发现了这篇文章有用,请分享它。我很欣赏的反馈和鼓励

关于类似的主题的文章…

…看看以下标记:

188年足球比分直播 API bet188足球 应用程序体系结构

脚注

1:我开发了这些例子使用巴别塔。目前巴别塔一个错误允许您重新分配输出变量。规范ES6说出口变量导出为一个只读视图.戊酸-长时间癌                                             .戊酸-长时间癌                                             

2:有人可能会认为,服务定位器的java版本依赖gondorff和数据源由于他们提到的类型签名。我打折,因为定位不实际调用任何方法的类。我也可以删除那些静态类型依赖某种类型体操,虽然我怀疑治疗比疾病本身更糟糕。.戊酸-长时间癌                                             


确认

皮特·霍奇森和阿克塞尔Rauschmayer给了我宝贵的帮助提高我的JavaScript。本·吴(伍斌)提出了一个有用的插图。Jean-Noel Rouvignac纠正几个错误。.戊酸-长时间癌                                             

重大修改

2015年10月13日:第一次出版