版權(quán)說(shuō)明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡(jiǎn)介
程序設(shè)計(jì)開(kāi)閉原則出處:/en-us/magazine/cc546578.aspx引言本文是新開(kāi)設(shè)的MSDN軟件設(shè)計(jì)基礎(chǔ)專欄的第一篇文章。我的目的是以不局限于某種特定工具或者某個(gè)(軟件工程)周期方法(lifecyclemethodology)的方式來(lái)討論設(shè)計(jì)的模式和原則。換言之,我計(jì)劃討論一些可以引導(dǎo)你使用任何技術(shù),或者在任何項(xiàng)目中更好地進(jìn)行設(shè)計(jì)的基礎(chǔ)知識(shí)。我喜歡以討論開(kāi)閉原則和其他由RobertC.Martin在其著作《敏捷軟件開(kāi)發(fā),原則,模式和實(shí)踐》中所倡導(dǎo)的相關(guān)主題作為開(kāi)始。不要因?yàn)樵跇?biāo)題中出現(xiàn)“敏捷”一詞就把書(shū)合上了,因?yàn)檫@本書(shū)實(shí)際上完全是關(guān)于如何竭力進(jìn)行優(yōu)良軟件設(shè)計(jì)的。問(wèn)下你自己:有多少次你是從零開(kāi)始去寫(xiě)一個(gè)全新的應(yīng)用程序?又有多少次你是通過(guò)將新功能添加到現(xiàn)有代碼庫(kù)(codebase)中來(lái)作為開(kāi)始?恐怕大多數(shù)的情況下,你是花費(fèi)了更多的時(shí)間將新功能添加到現(xiàn)有代碼庫(kù)中吧。然后再問(wèn)自己另一個(gè)問(wèn)題:寫(xiě)全新的代碼容易還是對(duì)現(xiàn)有代碼進(jìn)行修改容易?通常對(duì)我來(lái)說(shuō)寫(xiě)全新的方法和類要比深入舊代碼中,找出我想要修改的部分容易得多。修改舊有代碼增添了破壞已有功能的風(fēng)險(xiǎn)。對(duì)于新代碼來(lái)說(shuō),你通常只需要測(cè)試下新實(shí)現(xiàn)的功能就可以了。而當(dāng)你修改舊有代碼時(shí),你不得不既要測(cè)試你更改的部分,還要進(jìn)行一系列的兼容測(cè)試,以保證你沒(méi)有破壞任何的舊有代碼。所以,你通?;诂F(xiàn)有的代碼庫(kù)進(jìn)行工作,可是寫(xiě)全新的代碼又比修改舊的代碼容易得多。你難道不想像寫(xiě)全新代碼一樣多產(chǎn)、輕松地去對(duì)現(xiàn)有的代碼庫(kù)進(jìn)行擴(kuò)展么?這就是開(kāi)閉原則一展身手的地方了。我來(lái)解釋一下開(kāi)閉原則,它的意思是:軟件實(shí)體應(yīng)該對(duì)于擴(kuò)展是開(kāi)放的,而對(duì)于修改是關(guān)閉的。從字面上看這好像是矛盾的,實(shí)際并非如此。它的全部含義就是你應(yīng)該這樣去構(gòu)建一個(gè)應(yīng)用程序:可以在對(duì)現(xiàn)有代碼做最小修改的同時(shí)添加新的功能。我曾經(jīng)認(rèn)為開(kāi)閉原則僅僅是意味著使用插件(plugins),但并不是這么簡(jiǎn)單。你應(yīng)該避免一個(gè)小小的改動(dòng)就波及了你應(yīng)用程序中的多個(gè)類。這樣會(huì)使程序更加脆弱,更傾向于產(chǎn)生向下兼容的問(wèn)題,并使擴(kuò)展付出更高的代價(jià)。為了隔離變化,你會(huì)想要以一種一旦寫(xiě)好了就再也不需要修改的方式去寫(xiě)類和方法。然而你如何構(gòu)建代碼以實(shí)現(xiàn)隔離變化呢?我想說(shuō)的第一步就是遵循單一責(zé)任原則。單一責(zé)任原則在遵循開(kāi)閉原則的過(guò)程中,我期望能夠?qū)懗鲆粋€(gè)類或者方法,在以后我回過(guò)頭讀它的時(shí)候,會(huì)很舒服地看到它能完成它的工作并且我也不需要再修改它。你永遠(yuǎn)也達(dá)不到真正的開(kāi)閉天堂,但是通過(guò)嚴(yán)格地遵循與之相關(guān)的單一責(zé)任原則:一個(gè)類應(yīng)該有并且只有一個(gè)更改的理由,你可以非??拷亟咏?。寫(xiě)那些永遠(yuǎn)也不需要進(jìn)行修改的類的最簡(jiǎn)單方法就是寫(xiě)一些只能做一件事情的類。通過(guò)這種方式,一個(gè)類只有在它所確切負(fù)責(zé)的那件事更改時(shí)它才需要更改。代碼1演示了沒(méi)有遵循單一責(zé)任原則的一個(gè)例子。我真的懷疑你正在像這樣設(shè)計(jì)一個(gè)系統(tǒng),但是最好記得為什么我們不應(yīng)該這樣去構(gòu)建代碼。代碼1.這個(gè)類負(fù)責(zé)了太多的事publicclassOrderProcessingModule{publicvoidProcess(OrderStatusMessageorderStatusMessage){//從配置文件中讀取連接字符串stringconnectionString=ConfigurationManager.ConnectionStrings["Main"].ConnectionString;
Orderorder=null;using(SqlConnectionconnection=newSqlConnection(connectionString)){//從數(shù)據(jù)庫(kù)中獲取一些數(shù)據(jù)order=fetchData(orderStatusMessage,connection);}//向來(lái)自于OrderStatusMessage的訂單提交變更updateTheOrder(order);//國(guó)際訂單有一些特定的規(guī)則if(order.IsInternational){processInternationalOrder(order);}//對(duì)于大批量訂單我們需要特別處理elseif(order.LineItems.Count>10){processLargeDomesticOrder(order);}//小的國(guó)內(nèi)訂單也需要區(qū)別處理else{processRegularDomesticOrder(order);}//如果訂單準(zhǔn)備好了就發(fā)貨if(order.IsReadyToShip()){ShippingGatewaygateway=new
ShippingGateway();//將訂單對(duì)象提交運(yùn)送ShipmentMessagemessage=createShipmentMessageForOrder(order);gateway.SendShipment(message);}}OrderProcessingModule真是太忙了。它要進(jìn)行數(shù)據(jù)訪問(wèn)、獲取配置文件信息、為訂單處理執(zhí)行業(yè)務(wù)規(guī)則(可能本身就非常復(fù)雜),并且將完成的訂單轉(zhuǎn)移出貨。通常的情況是,如果你通過(guò)這種方式創(chuàng)建了OrderProcessingModule,你將會(huì)經(jīng)常深入到這段代碼中進(jìn)行修改。而許多系統(tǒng)需求的變化也會(huì)造成OrderProcessingModule的代碼產(chǎn)生非常多的變更,讓系統(tǒng)變得岌岌可危并使變更花費(fèi)很大代價(jià)。除了這種一大塊代碼的方式,你應(yīng)該遵循單一責(zé)任原則,將整個(gè)OrderProcessingModule分成一系列相關(guān)類的子系統(tǒng),每一個(gè)類完成它自己特定的職責(zé)。舉個(gè)例子,你可以將所有數(shù)據(jù)訪問(wèn)的功能放到一個(gè)新類中,管它叫OrderDataService,而把Order的業(yè)務(wù)邏輯放到另一個(gè)類中(我會(huì)在下一節(jié)進(jìn)行更詳細(xì)的講述)。根據(jù)開(kāi)閉原則,通過(guò)將業(yè)務(wù)邏輯和數(shù)據(jù)訪問(wèn)的職責(zé)劃分到不同的類中,你將可以獨(dú)立地改變它們中的一個(gè)而不會(huì)影響到另一個(gè)。數(shù)據(jù)庫(kù)物理部署的變化可能將使你把數(shù)據(jù)訪問(wèn)部分完全更換掉(對(duì)擴(kuò)展開(kāi)放),然而訂單邏輯類依然沒(méi)有任何改動(dòng)(對(duì)變更關(guān)閉)。單一責(zé)任原則的要點(diǎn)不僅僅是寫(xiě)一些更小的類和方法。它的要點(diǎn)是每一個(gè)類應(yīng)該實(shí)現(xiàn)一系列緊密相關(guān)的功能。遵循單一責(zé)任原則的最簡(jiǎn)單辦法就是不斷地問(wèn)自己是不是這個(gè)類的每一個(gè)方法和操作都與這個(gè)類的名稱直接相關(guān)。如果你找到了一些方法與這個(gè)類的名稱不相稱,你可以考慮將這些方法移到另一個(gè)類中。責(zé)任鏈模式
業(yè)務(wù)規(guī)則在代碼庫(kù)(Codebase)的生命周期中相對(duì)于系統(tǒng)的任何其他部分可能面臨更多的變化。在OrderProcessingModule類中,基于接收的訂單的類型,對(duì)于訂單的處理有不少的分支邏輯:if(order.IsInternational){processInternationalOrder(order);}elseif(order.LineItems.Count>10){processLargeDomesticOrder(order);}else{processRegularDomesticOrder(order);}一個(gè)真正的訂單處理系統(tǒng)很有可能在業(yè)務(wù)增長(zhǎng)的時(shí)候包含更多類型的訂單一并且要考慮很多的特殊情況,比如對(duì)于政府或者受到優(yōu)待的客戶,以及每周一次的特別供應(yīng)。對(duì)你而言,如果能夠書(shū)寫(xiě)并且測(cè)試一些新的訂單處理邏輯而不用冒著破壞現(xiàn)有業(yè)務(wù)規(guī)則的風(fēng)險(xiǎn)將會(huì)是一件非常有利的事情。最后,通過(guò)代碼2所示的責(zé)任鏈模式,對(duì)于這個(gè)訂單處理的例子你可以更進(jìn)一步地運(yùn)用開(kāi)閉原則。我所做的第一件事就是把所有的分支判斷由OrderProcessingModule中轉(zhuǎn)移到一個(gè)獨(dú)立的類中,這個(gè)類實(shí)現(xiàn)IOrderHandler接口:publicinterfaceIOrderHandler{voidProcessOrder(Orderorder);boolCanProcess(Orderorder);}代碼2.引入責(zé)任鏈publicclassOrderProcessingModule{privateIOrderHandler]]_handlers;publicOrderProcessingModule(){
_handlers=newlOrderHandler[]{newInternationalOrderHandler(),newSmallDomesticOrderHandler(),newLargeDomesticOrderHandler(),};}publicvoidProcess(OrderStatusMessageorderStatusMessage,Orderorder){//對(duì)來(lái)自O(shè)rderStatusMessage的訂單提交變更updateTheOrder(order);//找出知道如何處理這個(gè)訂單的第一個(gè)IOrderHandlerIOrderHandlerhandler=Array.Find(_handlers,h=>h.CanProcess(order));handler.ProcessOrder(order);}privatevoidupdateTheOrder(Orderorder){}}
然后我可以對(duì)于每種類型的訂單寫(xiě)一個(gè)獨(dú)立的lOrderHandler實(shí)現(xiàn),包含著像這樣的基本邏輯,“我知道如何處理這個(gè)訂單,讓我來(lái)處理它”?,F(xiàn)在對(duì)于每種類型的訂單處理邏輯都分隔到了獨(dú)立的處理類中(HandlerClass),對(duì)于某種類型的訂單你可以更改業(yè)務(wù)規(guī)則而不用擔(dān)心會(huì)破化其他類型訂單的規(guī)則。更好的是,你可以添加全新類型的訂單處理程序而只需要對(duì)現(xiàn)有代碼做細(xì)小的改動(dòng)。舉個(gè)例子,比如說(shuō),以后某個(gè)時(shí)候,我需要在系統(tǒng)中為政府的訂單添加支持。通過(guò)責(zé)任鏈模式,我可以添加一個(gè)全新的類,叫做GovernmentOrderHandler,這個(gè)類實(shí)現(xiàn)lOrderHandler接口。一旦我對(duì)GovernmentOrderHanlder按期望的方式所進(jìn)行的工作感到滿意,通過(guò)修改OrderProcessingModule類構(gòu)造函數(shù)的一行代碼,我就可以添加這個(gè)新的政府訂單處理規(guī)則:publicOrderProcessingModule(){_handlers=newIOrderHandler[]{newInternationalOrderHandler(),newSmallDomesticOrderHandler(),newLargeDomesticOrderHandler(),newGovernmentOrderHandler。, //新添加的處理規(guī)則};}通過(guò)在訂單處理規(guī)則上遵循開(kāi)閉原則,我使得在系統(tǒng)中添加新類型的訂單處理邏輯容易得多。我能夠用比在一個(gè)類中實(shí)現(xiàn)各種類型訂單處理所要面臨的小得多的影響其它類型訂單的風(fēng)險(xiǎn)來(lái)完成政府訂單規(guī)則的添加。雙重分發(fā)如果以后上面的步驟變得更加復(fù)雜該怎么辦呢?如果僅僅依靠多態(tài)無(wú)法滿足未來(lái)可能出現(xiàn)的所有變化呢?我們可以使用稱為雙重分發(fā)的模式將變化推入子類中,通過(guò)這種方式,我們不需要破壞現(xiàn)有的接口定義。舉個(gè)例子,比如說(shuō)我們正在構(gòu)建一個(gè)復(fù)雜的桌面應(yīng)用程序,它能一次顯示某種主面板中的一屏(screen)。每次我在程序中打開(kāi)一個(gè)新屏,我需要做很多的事情。我可能需要更改可用的菜單,檢查那些已經(jīng)打開(kāi)的屏幕的狀態(tài),做一些定制整個(gè)屏幕顯示的事,并且,yeah,以某種方式顯示新屏。典型地,我會(huì)使用某種ModelViewPresenter(MVP)模式的變體作為我的桌面應(yīng)用程序的構(gòu)架,并且我通常會(huì)使用程序控制器(ApplicationController)模式去協(xié)調(diào)應(yīng)用程序中各種不同MVP組(譯注:因?yàn)镸VP由三個(gè)部分組成,所以將每三個(gè)部件分為一細(xì)。通過(guò)在MVP中使用一個(gè)程序控制器(了解MVP的更多信息,可以參考Jean-PaulBoodhoo在MSDN雜志設(shè)計(jì)模式專欄中關(guān)于MVP模式的文章,/en-us/magazine/cc188690.aspx),激活屏幕可能會(huì)包含下面三個(gè)基本的部分:每一屏(Screen)都有一個(gè)提供器(Presenter),每個(gè)提供器知道關(guān)于一個(gè)特定屏幕的所有事情。應(yīng)用程序的主窗體有一個(gè)ApplicationShell。ApplicationShell負(fù)責(zé)以其自己的某種方式顯示位于面板(Panel)或者Tab控件(TabControl)中的獨(dú)立視圖(view)。ApplilcationShell也將包含所有的菜單。應(yīng)用程序控制器(ApplicationController)在程序
中扮演交警的角色。它知道ApplicationShell以及在應(yīng)用程序中傳輸?shù)拿恳粋€(gè)提供器。應(yīng)用程序控制器控制屏幕激活和反激活的生命周期。如果我所需要做得只不過(guò)簡(jiǎn)單地在激活時(shí)顯示ApplicationShell中的視圖,代碼可能如同代碼3所示。對(duì)于簡(jiǎn)單的應(yīng)用程序來(lái)說(shuō)這完全是可行的,但是如果程序變得更加復(fù)雜會(huì)怎樣呢?如果在下一個(gè)發(fā)布版本中,我有新的需求,在某些屏幕激活的時(shí)候向主Shell中添加菜單項(xiàng)?如果對(duì)于某些而非全部的視圖,我想要在靠著主屏幕左邊際的新面板中顯示額外的控件?代碼3.一個(gè)簡(jiǎn)單的基于視圖的應(yīng)用程序publicinterfacelApplicationShell{voidDisplayMainView(objectview);}publicinterfaceIPresenter{//僅僅提供對(duì)于內(nèi)部Windows窗體用戶控件或者窗體的訪問(wèn)objectView{get;}}publicclassApplicationController{privateIApplicationShell_shell;publicApplicationController(IApplicationShellshell){_shell=shell;}
publicvoidActivateScreen(IPresenterpresenter){teardownCurrentScreen();//設(shè)置新屏幕_shell.DisplayMainView(presenter.View);}privatevoidteardownCurrentScreen(){//移除現(xiàn)存屏幕}}我還想讓構(gòu)架支持嵌入(pluggable),以便于通過(guò)簡(jiǎn)單的嵌入新的提供器就可以在程序中添加新屏幕,所以現(xiàn)有提供器的抽象應(yīng)該對(duì)于這些新菜單以及左邊面板的構(gòu)造函數(shù)有所了解。然后我還必須更改ApplicationShell或者程序控制器,以對(duì)新菜單項(xiàng)以及左邊面板中額外的控件做出響應(yīng)。代碼4顯示了一種可能的解決方案。我向IPrensenter接口中添加了新的屬性用于對(duì)新的菜單項(xiàng)以及任何有可能添加到新的左側(cè)面板中的控件進(jìn)行建模。我同樣為這些新的概念向IApplicationShell添加了一些新的成員。然后我在ApplicationController.ActivateScreen(IPresenter)方法中添加了些新代碼代碼4.試圖擴(kuò)展IPresenterpublicclassMenuCommand{//...}publicinterfaceIApplicationShell{voidDisplayMainView(objectview);
//新行為voidAddMenuCommands(MenuCommand[]commands);voidDisplayInExplorerPane(objectpaneView);}publicinterfaceIPresenter{objectView{get;}//新屬性MenuCommand[]Commands{get;}object[]ExplorerViews{get;}}publicclassApplicationController{privateIApplicationShell_shell;publicApplicationController(IApplicationShellshell){_shell=shell;}publicvoidActivateScreen(IPresenterpresenter){teardownCurrentScreen();//設(shè)置新屏幕
_shell.DisplayMainView(presenter.View);//新代碼_shell.AddMenuCommands(presenter.Commands);foreach(varexplorerViewinpresenter.ExplorerViews){_shell.DisplayInExplorerPane(explorerView);}}privatevoidteardownCurrentScreen(){//移除現(xiàn)有屏幕}}那么,這個(gè)解決方案遵守了開(kāi)閉原則么?一點(diǎn)也沒(méi)有。首先,我必須修改IPresenter接口。因?yàn)樗且粋€(gè)接口,我必須在代碼庫(kù)中修改IPresenter接口的每一個(gè)實(shí)現(xiàn),并且為這些新的方法添加一些空的實(shí)現(xiàn),僅僅為了我的代碼可以再一次編譯通過(guò)。這通常是一個(gè)無(wú)法忍受的改變,尤其是當(dāng)你不能直接控制這些IPresenter實(shí)現(xiàn)中的任何一個(gè)的時(shí)候。關(guān)于這部分我們后面再說(shuō)。我同樣需要修改ApplicationController類,以使得它知道主ApplicationShell中的屏幕所可能需要的所有新的定制化類型。最后,我需要修改ApplicationShell以使它支持這些新的Shell定制。變化很小,但是同樣,我很有可能不久以后想要再次添加更多的屏幕定制。
在一個(gè)真正的應(yīng)用程序中,ApplicationControll類可能會(huì)變得足夠復(fù)雜,而不必承擔(dān)額外配置Applicationshell的責(zé)任。我們將這些職責(zé)置于每個(gè)提供器中可能會(huì)更好一些。通過(guò)使用一個(gè)名為Presenter的抽象類,而不是使用一個(gè)接口將會(huì)減少修改每個(gè)IPresenter接口的實(shí)現(xiàn)的痛苦。像代碼5這樣,我可以僅僅向抽象類中添加一些默認(rèn)的實(shí)現(xiàn)。并且在添加新的行為時(shí)我不需要修改任何現(xiàn)有的Presenter實(shí)現(xiàn)。代碼5.使用抽象的PresenterpublicabstractclassBasePresenter{publicabstractobjectView{get;}//Commands的默認(rèn)實(shí)現(xiàn)publicvirtualMenuCommand[]Commands{get{returnnewMenuCommand[0];}}//默認(rèn)的ExplorerViewspublicvirtualobject[]ExplorerViews{get{returnnewobject[0];}}}最后,還有一種更靠近開(kāi)閉原則的方式需要說(shuō)明。除了在IPresenter和BasePresenter中添加Get選擇器,我可以使用雙重分發(fā)模式。
幾天前在實(shí)際生活中我意外地得到了雙重分發(fā)模式的一個(gè)演示。我的團(tuán)隊(duì)剛剛轉(zhuǎn)移到一個(gè)新的辦公室中,我們一直在解決網(wǎng)絡(luò)上的問(wèn)題。我們的網(wǎng)絡(luò)負(fù)責(zé)人上周給我打了個(gè)電話并且告訴我我的同事應(yīng)該如何做以連接到VPN。他喋喋不休地向我講述一大堆我不懂的網(wǎng)絡(luò)術(shù)語(yǔ),所以我最終把電話給了我的同事,讓他們直接對(duì)話?,F(xiàn)在我們也為程序控制器做同樣的事情。并非讓程序控制器去詢問(wèn)每個(gè)提供器哪些需要被顯示在ApplicationShell中,提供器可以簡(jiǎn)單地忽略中間人并且告訴ApplicationShell對(duì)于每一屏應(yīng)該顯示些什么(查看代碼6)。publicinterfaceIPresenter{voidSetupView(IApplicationShellshell);}publicclassApplicationController{privateIApplicationShell_shell;publicApplicationController(IApplicationShellshell){_shell=shell;}publicvoidActivateScreen(IPresenterpresenter){teardownCurrentScreen();//使用雙重分發(fā)設(shè)置新屏幕presenter.SetupView(_shell);}privatevoidteardownCurrentScreen(){
//移除現(xiàn)有屏幕起初不管我如何做,我都將不得不為了新的定制菜單以及左欄面板中的控件而去修改Applicationshell,但如果我使用雙重分發(fā)策略,對(duì)于新的變更,程序控制器和提供器都只需要做非常少的修改。創(chuàng)建額外的屏幕概念(screenconcepts)我不再需要修改程序控制器和提供器類。對(duì)于新的Shell概念(screenconcepts),這個(gè)構(gòu)架是開(kāi)放的可擴(kuò)展的,而程序控制器和單獨(dú)的提供器類對(duì)于修改是關(guān)閉的。Liskov替換原則如果我前面所說(shuō)的,使用開(kāi)閉原則最通常的做法就是使用多態(tài)去用一個(gè)全新的類替換程序中現(xiàn)存的一部分。就拿最早的例子來(lái)說(shuō),你有一個(gè)稱為BusinessProcess的類,它的工作是,嗯,執(zhí)行業(yè)務(wù)處理。在這個(gè)過(guò)程中,它需要從數(shù)據(jù)源中訪問(wèn)數(shù)據(jù):publicclassBusinessProcess{privateIDataSource_source;publicBusinessProcess(IDataSourcesource){_source=source;}}publicinterfaceIDataSource{EntityFindEntity(longkey);}如果你可以通過(guò)實(shí)現(xiàn)IDataSource對(duì)這個(gè)系統(tǒng)進(jìn)行擴(kuò)展并且不對(duì)BusinessProcess類做任何的修改,那么這個(gè)設(shè)計(jì)就遵循了開(kāi)閉原則。你可能起初通過(guò)一個(gè)簡(jiǎn)單的基于XML文件的機(jī)制,然后轉(zhuǎn)而使用數(shù)據(jù)庫(kù)進(jìn)行存儲(chǔ),隨后添加某種類型的緩存--但是你還是不想修改
BusinessProcess類。所有這些都是可能的,只要你能夠遵循一個(gè)相關(guān)的原則:Liskov替代原則。粗略地說(shuō),如果你可以在任何接受抽象的地方使用那個(gè)抽象的任何實(shí)現(xiàn),就是在遵循Liskov替換原則°BusinessProcess應(yīng)該可以使用IDataSource的任何實(shí)現(xiàn)而不需要進(jìn)行修改。BusinessProcess不應(yīng)該知道IDataSource中除了進(jìn)行通信的的公共接口以外的任何內(nèi)部事務(wù)。為了深入這個(gè)觀點(diǎn),代碼7演示了一個(gè)沒(méi)有遵循Liskov替換原則的例子。這個(gè)版本的BusinessProcess類型對(duì)于獲取FileSource有著特定的邏輯,同時(shí)依賴一些針對(duì)于DatabaseSource類的特定錯(cuò)誤處理邏輯。你應(yīng)該創(chuàng)建IDataSource的實(shí)現(xiàn)以便他們可以處理所有特定的底層需求。通過(guò)這樣做可以使BusinessProcess類像代碼8這樣書(shū)寫(xiě):代碼7,沒(méi)有對(duì)IDataSource進(jìn)行抽象的BusinessProcess類publicclassBusinessProcess{p
溫馨提示
- 1. 本站所有資源如無(wú)特殊說(shuō)明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒(méi)有圖紙預(yù)覽就沒(méi)有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫(kù)網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。
最新文檔
- 保險(xiǎn)業(yè)務(wù)合規(guī)操作與風(fēng)險(xiǎn)防范手冊(cè)
- 遼寧汽貿(mào)人事制度改模板
- 2026年劇本殺運(yùn)營(yíng)公司門(mén)店投訴處理流程管理制度
- 2025年智能交通行業(yè)數(shù)據(jù)報(bào)告
- 2026年影視娛樂(lè)流媒體內(nèi)容創(chuàng)新報(bào)告及未來(lái)五至十年商業(yè)模式報(bào)告
- 長(zhǎng)沙銀行運(yùn)營(yíng)面試題目及答案
- 全音像記錄制度
- 保護(hù)患者隱私權(quán)制度
- 二手車交易制度
- 業(yè)務(wù)招待制度
- 化工廠班組安全培訓(xùn)課件
- 2025四川成都農(nóng)商銀行招聘10人筆試備考題庫(kù)及答案解析
- 營(yíng)業(yè)執(zhí)照借用協(xié)議合同
- 2025年秋蘇教版(新教材)初中生物八年級(jí)上冊(cè)期末知識(shí)點(diǎn)復(fù)習(xí)卷及答案(共三套)
- 2025年小升初學(xué)校家長(zhǎng)面試題庫(kù)及答案
- 2025年法考客觀題真題回憶版(含答案)
- 2025年危化品泄漏應(yīng)急培訓(xùn)教案
- 2025年江南大學(xué)招聘真題(行政管理崗)
- 2024-2025學(xué)年江蘇省南通市海門(mén)區(qū)高二上學(xué)期期末調(diào)研地理試題(解析版)
- GB/T 13350-2008絕熱用玻璃棉及其制品
- 《語(yǔ)言的演變》-完整版課件
評(píng)論
0/150
提交評(píng)論