188足球比分直播重构访问外部服务的代码

当我编写处理外部服务的代码时,我发现将访问代码分离成单独的对象是很有价值的。在这里,我将展示如何将一些凝固的代码重构成这种分离的一种常见模式。

2015年2月17日

发现 类似物品通过查看这些标签: 对象协作设计bet188足球· 干净代码· 188足球比分直播· 应用程序体系结构

软件系统的一个特点是它们不能独立生活。为了做一些有用的事,他们通常需要和软件的其他部分交流,不同的人写的,我们不认识,也不知道或不关心我们正在编写的软件的人。

当我们在编写能够进行这种外部协作的软件时,我认为应用好的模块化和封装特别有用。我看到了一些常见的模式,并且发现它们在这方面很有价值。

在本文中,我将举一个简单的例子,并通过重构来介绍我正在寻找的模块化类型。188足球比分直播


起始代码

示例代码的工作是从JSON文件中读取一些关于视频的数据,用YouTube上的数据丰富它,计算一些简单的进一步数据,然后返回JSON中的数据。

这是起始代码。

类VideoService…

def video_list@video_list=json.parse(file.read('videos.json'))id=@video_list.map v v['youtubeid']client=googleauthorizer.new(token_key:'api youtube',应用程序名称:“网关YouTube示例”,application_version:'0.1').api_client youtube=client.discovered_api('youtube','v3')请求api_方法:youtube.videos.list,参数:id:ids.join(“,”),部分:摘录,内容细节,统计,}}响应=json.parse(client.execute!(请求).body)id.eaeach do id video=@video_list.find 124v id==v['youtube id']youtube you录制=resresres'items'].find;v;id==v['id']v id视频['view']=youtube录制['统计']'viewcount']to“u i days availu availavailable=date.todadaday-date.todadadadadadadada今天-date.date.parse-date.parse(youtube录制['snipp'hlyviews']=视频['视图']*365.0/天可用/12结束返回json.dump(@video_list)结束

这个例子的语言是ruby

这里我要说的第一件事是这个例子中没有太多的代码。如果整个代码库只是这个脚本,那么您就不必太担心模块化了。我需要一个小例子,但如果我们观察一个真实的系统,任何读者的眼睛都会变得呆滞。所以我不得不让你把这段代码想象成一个数万行的系统中的典型代码。

通过googleauthorizer对象介导对YouTube API的访问,为了本文的目的,我将作为一个外部API来处理。它处理连接到谷歌服务(如YouTube)的混乱细节,尤其处理授权问题。如果你想了解它是如何工作的,看一看我最近写的一篇关于访问谷歌API的文章.

这个代码是怎么回事?您可能无法理解此代码所做的一切,但是你应该能够看到它混合了不同的关注点,我建议在下面的代码示例上色。为了做出任何改变,你必须理解如何访问YouTube的API, YouTube如何构建其数据,和一些域逻辑。

类VideoService…

def video_list@video_list=json.parse(file.read('videos.json'))id=@video_list.map v v['youtubeid']client=googleauthorizer.new(token_key:'api youtube',应用程序名称:“网关YouTube示例”,application_version:'0.1').api_client youtube=client.discovered_api('youtube','v3')请求api_方法:youtube.videos.list,参数:id:ids.join(“,”),部分:摘录,内容细节,统计,}}响应=json.parse(client.execute!(要求)身体id.each do id video=@video_list.find v id==v['youtubeid']youtube记录=响应[项目]。查找v id==v['id']
video['views']=youtube_record['statistics']['viewCount']。
      days_available=今天。-日期.parse(youtube_record['snippet'][发布日期])
video['monthlyviews']=video['views']*365.0/天可用/12结束返回json.dump(@video_list)结束

对于像我这样的软件专家来说,讨论“关注点分离”是很常见的,这基本上意味着不同的主题应该在不同的模块中。我理解这一点的主要原因是:在一个模块化良好的程序中,每个模块应该是关于一个主题的,所以我可以对任何我不需要理解的东西保持无知。如果YouTube的数据格式发生变化,我不需要理解应用程序的域逻辑来重新排列访问代码。即使我正在做一个改变,从YouTube上获取一些新数据并将其用于某些领域逻辑,我应该能够将我的任务分成这些部分,分别处理每个部分,最小化我需要在头脑中不断旋转的代码行数。

我的重188足球比分直播构任务是将这些问题分解成单独的模块。完成后,视频服务中唯一的代码应该是未着色的代码——协调这些其他职责的代码。


测试代码

重构的第一步总是相同的。188足球比分直播你需要有信心,你不会无意中弄坏任何东西。188足球比分直播重构就是将一大组小步骤串在一起,所有这些都是行为保护。小步走,我们增加了不搞砸的机会。但我对自己的了解足以让我搞砸哪怕是最简单的改变,所以为了获得自信,我需要做一些测试来弥补我的错误。

但是这样的代码不容易测试。最好编写一个对计算的每月视图字段断言的测试。毕竟,如果还有什么问题,这将给出一个错误的答案。但问题是我正在访问YouTube的实时数据,人们有看录像的习惯。YouTube上的View Count字段将定期更改,使我的测试变红非确定性

所以我的第一个任务就是去掉那块薄片。我可以通过介绍双倍试验,一个看起来像YouTube的对象,但却以确定性的方式响应。不幸的是,在这里我遇到了遗留代码的困境。

遗留代码困境:当我们更改代码时,我们应该有适当的测试。要进行测试,我们经常要更改代码。

--迈克尔·费瑟

考虑到我必须在没有测试的情况下进行更改,我需要做我能想到的最小和最简单的改变,这将使我能在一个接缝后面与youtube进行交互,在那里我可以引入一个双重测试。所以我的第一步是提取方法将与YouTube的交互与常规程序的其他部分分离成自己的方法。

类VideoService…

def video_list@video_list=json.parse(file.read('videos.json'))id=@video_list.map v v['youtubeid']response=拨打YouTube IDi d.each do i d video=@video_list.find v i d==v['youtube i d']youtube录制=respons['items'].find;v i d==v['i d'];v i d视频['view']=youtube录制['统计']'viewcount']到“u i days-avavail=date.todadadadadadadadadadate.parse(youtube录制['snipp'snipp'][publishedt']'publishedt'])视频['monthlylyview''monthlyview'=视频[“视图”]*365.0/days_available/12 end return json.dump(@video_list)end def call_youtube ids client=googleauthorizer.new(token_key:'api youtube',应用程序名称:“网关YouTube示例”,application_version:'0.1').api_client youtube=client.discovered_api('youtube','v3')请求api_方法:youtube.videos.list,参数:id:ids.join(“,”),部分:摘录,内容细节,统计,}}返回json.parse(client.execute!(请求).body)结束

这样做有两件事。首先,它很好地将google api操作代码提取到自己的方法中(主要是)将其与任何其他类型的代码隔离开来。这本身是值得的。其次,更紧急的是,它建立了一个接缝,我可以用它来替代测试行为。Ruby内置的小型测试库允许我轻松地在一个对象上截取单个方法。

class videoserviceTester<minitest::test def setup vs=videoservice.new vs.stub(:call outube,stub_calu youtube)do@video s=json.parse(v s.video_list)@µs=@vidvideos.det'wgdbvix9ifa'==v['youtubeid'];@evo=@vidvidvidvids.det'zisghs0w44y'==v['youtubeid']end end def stub_calu youtuyoutube json.parse(file.read('test/data/youtube视频列表.json'))file.read end def test 124;'v在del中断言TA 5880,@礹s[“月视图”],1个断言在三角洲20,@evo[月视图],1结束根据需要进一步测试…

通过分离YouTube电话,把它割破,我可以使这个测试具有确定性。至少在今天,为了让它明天工作,我需要对电话做同样的事情今天的日期.

类VideoServiceTester…

DEF设置日期.stub(:今天,新日期(2015)2,2))vs=videoservice.new vs.stub(:调用youtube,stub_call_youtube)do@video s=json.parse(v s.video_list)@_s=@video s.detect v'wgdbvix9ifa'==v['youtubeid']@evo=@video s.detect v'zisghs0w44y'==v['youtubeid']end end end

将远程调用分离为连接对象

通过将代码放入不同的函数来分离关注点是分离的第一个层次。但是当关注点与域逻辑和处理外部数据提供者一样不同时,我更喜欢把分离程度提高到不同的班级。

图1:开始时,视频服务课程包含四个职责

因此,我的第一步是创建一个新类并使用移动法.

类VideoService…

def调用youtube id youtubeconnection.new.list视频id end

类youtube连接…

def list_videos ids client=googleauthorizer.new(token_key:'api youtube',应用程序名称:“网关YouTube示例”,application_version:'0.1').api_client youtube=client.discovered_api('youtube','v3')请求api_方法:youtube.videos.list,参数:id:ids.join(“,”),部分:摘录,内容细节,统计,}}返回json.parse(client.execute!(请求).body)结束

有了它,我还可以更改存根,这样它将返回一个双精度测试,而不是简单地将方法存根化。

类VideoServiceTester…

def设置日期.stub(:今天,新日期(2015)2,2))YouTube连接.Stub(:新,你的管子接头。新的)做@video s=json.parse(videoservice.new.video_list)@_s=@video s.detect v wgdbvix9ifa'==v['youtubeid']@evo=@video s.detect v zisghs0w44y'==v['youtubeid']end end end

类youtubeconnectionstub…

def list_videos ids json.parse(file.read('test/data/youtube video list.json'))结束

在进行重构时,188足球比分直播我必须小心,我闪亮的新测试不会捕捉到我在存根后面犯的任何错误,所以我必须手动确保生产代码仍然有效。(是的,既然你问了,我做这个的时候犯了一个错误(把争论抛在一边列出视频)。有一个原因我需要测试这么多。)

通过一个单独的类获得的更大的关注分离也为测试提供了更好的接口——我可以将所有需要存根的东西打包成一个单独的对象创建,如果我们在测试期间需要对同一个服务对象进行多个调用,这就特别方便。

当对YouTube的调用转移到连接对象时,视频服务的方法已经不值得再使用了,所以我把它放在内联函数.

类VideoService…

def video_list@video_list=json.parse(file.read('videos.json'))id=@video_list.map v v['youtubeid']response=youtubeconnection.new.list_视频IDi d.each do i d video=@video_list.find v i d==v['youtube i d']youtube youtube录制=响应['items'].find;v i d==v['i d'];v i d视频['view']=youtube录制['统计']'viewcount']到“u i days-avail=date.todadadadadadadadadadadadadate.parse(youtube录制['snipp'snipp'][publishedt']'publishedt']'publishedt''publishedet'=视频[“视图”]*365.0/d天可用/12结束返回json.dump(@video_list)结束def调用youtube id youtubeconnection.new.list视频id end

我不喜欢我的存根必须解析JSON字符串。总的来说,我喜欢将连接对象作为卑鄙之物,因为他们做的任何行为都没有经过测试。所以我更喜欢把解析过程放到调用者中。

类VideoService…

def video_list@video_list=json.parse(file.read('videos.json'))id=@video_list.map v v['youtubeid']响应=JSON.PARSE(youtubeconnection.new.list_videos(id))id.eaDo id video=@video_list.find v id==v['youtube id']youtube录制=响应['items']find;v id==v['id']v id视频['view']=youtube录制['statists'][viewcount']到“u天可用”=date.todaday-date.todadada今天-date.parse(youtube录制['u录制['snipp''snipp'snippet'['“发布日期”])视频['monthlyviews']=video['views']*365.0/天可用/12结束返回json.dump(@video_list)结束

类youtube连接…

def list_videos ids client=googleauthorizer.new(token_key:'api youtube',应用程序名称:“网关YouTube示例”,application_version:'0.1').api_client youtube=client.discovered_api('youtube','v3')请求api_方法:youtube.videos.list,参数:id:ids.join(“,”),部分:摘录,内容细节,统计,}}返回JSON.PARSE(客户端执行!(请求).body)结束

类youtubeconnectionstub…

def列出视频IDJSON.PARSE(file.read('test/data/youtube video list.json'))结束

图2:第一个主要步骤将YouTube连接代码分为连接对象。


将YouTube数据结构分离为网关

现在我已经基本上可以连接到YouTube了,我可以研究通过YouTube数据结构进行深入研究的代码。这里的问题是,为了获取视图计数数据,需要知道许多代码,你必须研究结果的“统计”部分,但要获得发布日期,您需要深入研究“代码片段”部分。这种挖掘与来自远程数据源的数据很常见,它的组织方式对他们来说很有意义,不适合我。这是完全合理的行为,他们不了解我的需求,我一个人做这件事已经够麻烦的了。

我发现思考这个问题的一个好方法是埃里克·埃文斯的有界语境.YouTube根据上下文组织数据,而我想根据另一个组织我的。合并两个有界上下文的代码会变得复杂,因为它将两个单独的词汇表混合在一起。我需要用埃里克所说的反腐败层,他们之间有明确的界限。他对反腐层的描绘是中国的长城,像这样的墙一样,我们需要能够让一些东西在它们之间传递的通道。在软件方面,网关允许我通过墙从YouTube绑定的上下文中获取所需的数据。但是,应该以一种在我的上下文中有意义的方式来表达网关,而不是它们的上下文。

在这里,简单的,例子,这意味着网关对象可以给我发布的日期和视图计数,而客户机不需要知道它是如何存储在YouTube数据结构中的。网关对象从YouTube的上下文转换为我的上下文。

我首先创建一个网关对象,用从连接中得到的响应进行初始化。

类VideoService…

def video_list@video_list=json.parse(file.read('videos.json'))id=@video_list.map v v['youtubeid']youtube=youtubegateway.new(youtubeconnection.new.list_videos(id))。id.each do id video=@video_list.find v id==v['youtube id']youtube_录制=youtube.record(id)条video['views']=youtube_record['statistics']'viewcount']to_i days_available=date.today-date.parse(youtube_record['snippet']['publishedt'])video['monthlyviews']=video['views']*365.0/days_available/12 end返回json.dump(@video_list)end

给你上大号吧…

def initialize responsejson@data=json.parse(responsejson)end def record id@data['items'],find v id==v['id']end

我创造了最简单的行为即使我最终不打算使用网关的记录方法,事实上,除非我停下来喝杯茶,否则我想它不会持续半个小时。

现在,我将把视图的挖掘逻辑从服务转移到网关中,创建一个单独的网关项类来表示每个视频记录。

类VideoService…

def video_list@video_list=json.parse(file.read('videos.json'))id=@video_list.map v v['youtube id']youtube=youtubegateway.new(youtubeconnection.new.list_videos(ids))id.each do id video=@video_list.find v id==v['youtube id']youtube record=youtube.record(id)video['views']=youtube.item(id)['views']days_available=date.today-date.parse(youtube_record['snippet']['publishedt'])video['monthlyviews']=video['views']*365.0/days_available/12 end返回json.dump(@video_list)end

给你上大号吧…

def item id'views'=>记录(id)['statistics'][viewCount']结束

我对出版日期也这么做

类VideoService…

def video_list@video_list=json.parse(file.read('videos.json'))id=@video_list.map v v['youtube id']youtube=youtubegateway.new(youtubeconnection.new.list_videos(id))id.each do id video=@video_list.find v id==v['youtube id']youtube_record=youtube.record(id)video['views']=youtube.item(id)['views']days_available=date.today-youtube.item(id)[“已发布”]video['monthlyviews']=video['views']*365.0/天可用/12结束返回json.dump(@video_list)结束

给你上大号吧…

def item id'views'=>记录(id)['statistics'][viewCount']。'published'=>日期。分析(记录(ID)['snippet'][发布日期])结束

因为我在用钥匙查到的网关记录,我想在内部数据结构中更好地反映这种用法,我可以用哈希替换列表

给你上大号吧…

定义初始化响应@data=json.parse(responsejson)['items'].map i[i['id'],I]至_H结束def item id'views'=>@数据[ID][统计信息][查看计数]。到\i,'已发布'=>日期。分析(@数据[ID][“代码段”][发布日期])结束def record id@data['items'],查找v id==v['id']结束

图4:将数据处理分离为网关对象

这样做了,我已经完成了我想要做的关键分离。YouTube连接对象处理对YouTube的调用,返回给YouTube网关对象的数据结构。服务代码现在完全是关于我希望如何查看数据,而不是数据如何存储在不同的服务中。

类VideoService…

def video_list@video_list=json.parse(file.read('videos.json'))id=@video_list.map v v['youtube id']youtubegatteway.new(youtubeconnection.new.list_v id视频(id))id.eaDo id;v id视频@v id视频_list.fin;v;id==v['youtube id'];视频['view']=youtube.youtube.ite(id)['youtube.ite(id)['views']da天(id_available=日期。今天-Youtube.item(id)['published']video['monthlyviews']=video['views']*365.0/天可用/12结束返回json.dump(@video_list)结束

将域逻辑分离为域对象

尽管YouTube的所有互动现在都被划分成不同的对象,视频服务仍然混合其域逻辑(如何计算每月视图),从编排本地存储的数据与服务中的数据之间的关系开始。如果我为视频引入域对象,我可以把它分开。

我的第一步是简单地将视频数据的散列包装在一个对象中。

类视频…

def initialize ahash@data=ahash end def[]key@data[key]end def[]=key,value@data[key]=value end def to_h@data end

类VideoService…

def video_list@video_list=json.parse(file.read('videos.json')).地图H视频.新(H)id=@video_list.map \\124\124\124\\id v id视频@v id视频_list.fin \124;v;id==v['youtube id']\\124\124;\\124;\124\\\\\\\\\\']=视频['视图']*365.0/天可用/12结束返回json.dump(@video_list.地图V V.到结束

要将计算逻辑移动到新的视频对象中,我首先需要让它成为正确的移动形状-我可以通过将它全部分割成一个视频服务的方法来完成,视频域对象和YouTube网关项作为参数。第一步是使用 提取变量 在网关项上。

类VideoService…

def video_list@video_list=json.parse(file.read('videos.json')).map h;video.new(h)ids=@video list.map v['youtube id']youtubegateway.new(youtubeConnection.new.list_video(id))id.eaeado id;v id视频@v id视频_list.find v id==v['youtube id''youtube id'youtubebettegat      YouTube项目=youtube.item(id)视频[“视图”]=YouTube项目['视图']天可用=今天。-YouTube项目['published']video['monthlyviews']=video['views']*365.0/days_available/12 end return json.dump(@video_list.map v.to_u h)end

完成后,我可以很容易地将计算逻辑提取到它自己的方法中。

类VideoService…

def video_list@video_list=json.parse(file.read('videos.json')).map h;video.new(h)ids=@video list.map v['youtube id']youtubegateway.new(youtubeConnection.new.list_video(id))id.eaeado id;v id视频@v id视频_list.find v id==v['youtube id''youtube id'youtubebettegat]youtube_item=youtube.item(id)丰富视频,YouTube项目结束返回json.dump(@video_list.map v v.to_u h)结束def enrich视频,YouTube项目video['views']=youtube_item['views']days_available=date.today-youtube_item['published']video['monthlyviews']=video['views']*365.0/days_available/12 end

然后很容易申请移动法移动到视频中。

类VideoService…

def video_list@video_list=json.parse(file.read('videos.json')).map h;video.new(h)ids=@video list.map v['youtube id']youtubegateway.new(youtubeConnection.new.list_video(id))id.eaeado id;v id视频@v id视频_list.find v id==v['youtube id''youtube id'youtubebettegat]youtube_item=youtube.item(id)video.enrich_with youtube youtube项目结束返回json.dump(@video_list.map v v.to_u h)结束

类视频…

def用youtube youtube项目丰富
    @数据['views']=youtube_item['views']days_available=date.today-youtube_item['published']@数据['monthlyviews']=@data['views']*365.0/天可用/12结束

这样做了,我可以删除视频哈希的更新。

类视频…

DEF[] =键,value@data[key]=值结束

现在我有了合适的物品,我可以在服务方法中使用ID简化编排。我先用将临时变量内联YouTube项目然后用对视频对象的方法调用替换对枚举索引的引用。

类VideoService…

def video_list@video_list=json.parse(file.read('videos.json')).map h;video.new(h)ids=@video list.map v['youtube id']youtubegateway.new(youtubeConnection.new.list_video(id))id.eaeado id;v id视频@v id视频_list.find v id==v['youtube id''youtube id'youtubebettegatyoutube_item=youtube.item(id)视频。用youtube丰富(youtube.item(视频.youtube-id)结束返回json.dump(@video_list.map v v.to_u h)结束

类视频…

优土贝特@数据[‘youtubeid’]结束

这允许我直接使用对象进行枚举。

类VideoService…

def video_list@video_list=json.parse(file.read('videos.json')).map h video.new(h)ids=@video_list.map v v['youtubeid']youtube=youtubegateway.new(youtubeconnection.new.list_videos(ids))。@视频v.enrich_with youtube(youtube.item(v.youtube id))返回json.dump(@video_list.map v v.to_u h)结束

并删除视频哈希的访问器

类视频…

def[]key@data[key]结束

类VideoService…

def video_list@video_list=json.parse(file.read('videos.json')).map h video.new(h)ids=@video_list.map v|优土贝特}youtube=youtubegateway.new(youtubeconnection.new.list_videos(ids))@video_list.each v v.enrich_with_youtube(youtube.item(v.youtube_id))return json.dump(@video_list.map v v.to_end

我可以用字段替换视频对象的内部哈希,但我认为它不值得这样做,因为它主要装载了一个散列,其最终输出是一个JSonified散列。安嵌入式文档是一种完全合理的域对象形式。


对最终目标的思考

图5:通过重构创建的对象及其依赖关系188足球比分直播

类VideoService…

def video_list@video_list=json.parse(file.read('videos.json').map h;video.new(h)id=@video list.map v;v.youtube id;youtubegateway.new(youtubeconne.new.list _v id视频(id))@v id视频_list.ea;v;v.enrich _youtuYouYouYouYouTube(youtube.youtube.ite项(v.YouYouYouYouYouYouYouYouYouYouYouYouYouYouTube(YouYouYouYouYouYouTube打开.dump(@video_list.map_v_v.to_u h)结束
类Youtube连接def list_videos ids client=googleauthorizer.new(token_key:'api youtube',应用程序名称:“网关YouTube示例”,application_version:'0.1').api_client youtube=client.discovered_api('youtube','v3')请求api_方法:youtube.videos.list,参数:id:ids.join(“,”),部分:摘录,内容细节,统计,}}返回client.execute!(请求)。正文结束
Youtubegateway类def initialize responsejson@data=json.parse(responsejson)['items'].映射i[i['id']i].to_h end def item id'views'=>@data[id]['statistics']['viewCount']to_i,'published'=>date.parse(@data[id]['snippet'][发布日期])endend
视频类def initialize ahash@data=ahash end def to_h@data end def youtube_id@data['youtube id']end def enrich_with_youtube youtube_item@data['views']=youtube_item['views']days_available=date.today-youtube_item['published']@data['monthlyviews']=@data['views']*365.0/days_available/12 end end

那么我取得了什么成就呢?188足球比分直播重构通常会减少代码大小,但在这种情况下,它几乎翻了一番,从26行到54行。其他一切都是平等的,代码越少越好。但在这里,我认为通过分离关注点而获得的更好的模块化通常值得增加规模。这也是教育学(即玩具)例子可能会掩盖这一点。26行代码不太好理解,但如果我们谈论的是用这种风格写的2600行,然后,模块化变得非常值得任何代码大小的增加。通常,当您使用更大的代码库执行此类操作时,任何此类增加都要小得多,因为您发现了通过消除重复来减少代码大小的更多机会。

你会注意到我已经完成了四种对象:协调器,域对象,网关,和连接。这是一种共同的责任安排,尽管不同的案例在依赖关系的布局上看到了合理的变化。由于特殊需要,职责和依赖关系的最佳安排有所不同。需要频繁更改的代码应该与很少更改的代码分开,或者仅仅因为不同的原因而改变。广泛重用的代码不应依赖于仅用于特定情况的代码。这些驱动因素因地制宜,并指出依赖模式。

一个常见的变化是逆转域对象和网关之间的依赖关系-将网关转变为映射器.这允许域对象独立于它的填充方式,以映射器了解域对象并获得对其内部的一些访问权为代价。如果域对象在许多上下文中使用,那么这可能是一个有价值的安排。

另一个改变可能是将调用连接的代码从协调器转移到网关。这简化了协调器,但使网关更加复杂。这是否是一个好主意取决于协调员是否变得过于复杂,或者,如果许多协调员在设置连接时使用同一个网关导致代码重复。

我也认为我可能会把连接的一些行为转移到呼叫者身上,尤其是当调用者是网关对象时。网关知道它需要什么数据,所以应该提供调用参数中的部件列表。但只有当我们有其他客户打电话给我们时,这才是真正的问题。利斯特视频,所以我倾向于等到那一天。

有件事我觉得很重要,不管你案件的细节是什么,是为相关对象的角色制定一致的命名策略。我有时听到人们说你不应该在代码中输入模式名,但我不同意。通常模式名有助于传达不同元素所扮演的角色,所以拒绝这个机会是愚蠢的。当然,在团队中,您的代码将显示常见的模式,并且命名应该反映出这一点。我使用“网关”来创建网关EAA的P模式。我在这里使用了“连接”来显示到外部系统的原始链接,并打算在我以后的写作中使用这一惯例。这个命名约定并不普遍,虽然你使用我的命名约定会让我感到骄傲,重要的一点不是应该使用哪种命名约定,而是应该选择一些约定。

当我把一个方法分解成一组这样的对象时,关于测试的结果有一个自然的问题。我对视频服务中的原始方法进行了单元测试,我现在应该为三个新类编写测试吗?我的倾向是,如果现有的测试足够覆盖了行为,不需要马上添加。当我们添加更多行为时,然后我们应该增加更多的测试,如果将此行为添加到新对象中,那么新的测试将集中在这些对象上。这可能意味着,目前针对视频服务的一些测试将看起来不合适,应该移动。但所有这些都是在未来,应该在未来处理的。

在测试中,我会特别关注的是我在YouTube连接上添加的存根的使用。像这样的树桩很容易失控,然后,它们实际上可以降低更改的速度,因为简单的生产代码更改会导致更新许多测试。这里的本质是注意测试代码中的重复,并像处理生产代码中的重复一样认真地处理它。

这种对组织测试加倍的思考自然会导致更广泛的服务对象组装问题。现在,我已经将一个行为从一个服务对象拆分为三个服务对象和一个域实体(使用埃文斯分类法)关于如何实例化服务对象有一个自然的问题,配置好的,并组装好。目前,视频服务直接为其家属做这项工作,但这很容易与大型系统脱节。要处理这种复杂性,通常使用诸如服务定位器和依赖注入.我现在不想谈这个,但这可能是后续文章的主题。

此示例使用对象,在很大程度上是因为我对面向对象的样式比功能样式更熟悉。但我希望基本的职责分工是一样的,但是边界是由函数(或者名称空间)而不是类和方法设置的。其他一些细节会改变,视频对象将是一个数据结构,丰富它将创建新的数据结构,而不是就地修改。以一种实用的方式来看待这个问题将是一篇有趣的文章。

最后,我想重新强调重构的一个重要的一般要点。188足球比分直播188足球比分直播重构不是对代码库进行任何重构时应该使用的术语。它特别指应用一系列非常小的行为来保存更改的方法。我们在这里看到了几个例子,我故意引入了一些代码,我知道很快就会删除这些代码,所以我可以采取一些小措施来保护自己的行为。这就是重构的本质,188足球比分直播正如我最近在tweet上所说:

这里的重点是通过采取小步骤,你最终会更快,因为你不会破坏任何东西,从而避免调试。大多数人认为这与直觉相反,当肯特·贝克第一次向我展示他是如何重构的时候,我当然做到了。但我很快发现它有多有效。


分享:
如果你觉得这篇文章有用,请分享。我感谢你的反馈和鼓励

有关类似主题的文章…

…查看以下标签:

对象协作设计bet188足球 干净代码 188足球比分直播 应用程序体系结构

重要修改

2015年2月17日:首次发表