2014-03-12 38 views
0

我要尋找一個PHP實現或設計模式是這樣的(只是一個很基本骨架爲例):依賴解析設計模式

namespace Contract { 
    interface Application {...} 
    interface EntryPoint {...} 
} 

namespace RestApi { 

    class Module { 
     /** @return Contract\EntryPoint */ 
     public function getEntryPoint(Contract\Application $application){...} 
    } 

    class EntryPoint implements Contract\EntryPoint {...} 
} 

namespace BusinessLogic { 

    class Module { 
     /** @return Contract\Application */ 
     public function getApplication(){...} 
    } 

    class Application implements Contract\Application {...} 
} 

$dependencyResolver = new DependencyResolver(); 
$dependencyResolver->parse(new RestApi\Module()); 
$dependencyResolver->parse(new BusinessLogic\Module()); 
$dependencyResolver->invoke(function (Contract\EntryPoint $entryPoint){ 
    $entrypoint->handleRequest(); 
}); 

我想鬆耦合我用我的應用程序的每個模塊,所以我打算設計接口,也許抽象類與驗證,爲每個模塊類型做一個明確界面。我還沒有找到解決這個問題的方法,通常情況下,ppl會給IoC容器注入東西,它不會告訴任何關於兩個模塊之間通用接口的東西...

您是否知道解決此問題的設計模式,或一個de因素標準的PHP實現/框架呢?

注意:IoC容器不是解決方案,我想用定義良好的接口注入依賴關係,而不是將它們從DI容器或服務定位器中拉出來......我不希望我的模塊知道任何有關他們是如何得到他們的依賴...

編輯:

我更新了我的問題,因爲我不認爲我的代碼是爲大家明顯。 A創建一個類圖來緩解它的理解:

class diagram of multiple modules in one of the environments

好了,所以此圖只包含一個環境的描述。我們通常使用不同的環境,例如:測試,開發,生產等......這些環境中的每一個都包含不同的模塊,例如通過測試和開發,我們通常關閉電子郵件發送,所以其中一個模塊包含一個模擬PHP郵件程序那些環境......正如你看到的跨模塊依賴關係,類依賴於合同,而不是直接相互關聯。通過這種方式,模塊的代碼是鬆耦合...

我打算描述這種依賴關係(例如,具有註釋)和注射他們在某種程度上以自動化方式。這是一項艱鉅的任務,因爲通過PHP,我只需要加載處理請求所需的類。例如:

loading only the necessary dependencies

所以我必須以某種方式使用延遲加載,比如我可以注入的工廠,但我不喜歡這個主意,我想注入依賴本身,而不是工廠..

請注意契約接口並不知道其實現的任何內容,因此我必須以某種方式發佈每個模塊的這些實現,並從其他模塊中找到它們......大多數DI容器實現可以解決該問題,但我不想將工廠或DI容器注入到我的模塊中。我希望他們能夠只依賴於合同和其它...

馬修那不勒斯建議每個環境中只使用不同的配置單IoC容器,但我不認爲這將如何解決我的問題。 DI容器將實例化每個具有跨模塊依賴性的類,因此我將把每個模塊的整個配置移到一個巨大的主配置文件中。即使通過一個簡單的項目,我也會有大約20個課程(語言,用戶,識別因素,用戶識別因素,聯繫人,用戶聯繫人,角色,用戶角色,權限,角色權限,文章,評論等)。 ...)和至少3個模塊(表示層,業務邏輯層,數據訪問層)。因此,主配置將包含來自不同模塊的至少60個類的實例化...這將很難維護,我幾乎可以肯定,它會導致大量的代碼重複由不同環境的配置...可能也許拆分配置文件可以減輕痛苦,但我想我不會知道這個解決方案的優點和缺點,直到我開始在複雜的項目中使用它。這種方法的另一個問題是,如何在不將IoC容器本身注入到具有跨模塊相關性的每個類中的情況下如何實現延遲加載?我認爲我需要證明或示例代碼,這種方法真的很好地通過這個問題域...

目前我在想的東西類似require.js與AMD的JavaScript模塊,但注入的IoC容器作爲以及延遲加載。如果你想加載你不想在你的代碼中使用的依賴,那麼你必須在模塊內部使用require("moduleName"),以防萬一真的有必要。 Ofc認爲require只是container.get("moduleName")的糖語法......目前我看不出如何解決這個問題。我認爲我的每個模塊都應該有一個或多個DI容器,稱之爲模塊容器。這些模塊容器可以處理跨模塊依賴關係。通過延遲加載,模塊容器將從主容器中拉下每個類的跨模塊依賴關係,這將自動註冊每個模塊容器以及由它們實例化的每個類。在這種情況下,如果我無法在不注入工廠或DI容器或服務定位器的情況下解決延遲負載,那麼只有模塊容器纔會知道主容器... Ofc這是最後的手段,我認爲我可以以某種方式執行延遲加載而無需將主容器注入模塊容器中。或者至少,我可以用糖語法莫名其妙地做到這一點,例如:

class ModuleContainer { 
    public function setCrossDomainDependency(Contract\Dep $crossDomainDependency){ 
     //... 
    } 

    /** @return Contract\Dep */ 
    public function getCrossDomainDependency(){ 
     //... 
    } 
} 

我的目的使用相同的ModuleContainer實例,而不管有MainContainer的。我會檢查反射api,也許我可以在運行時以某種方式覆蓋getCrossDomainDependency。如果不是,那麼我認爲唯一的解決方案注入工廠或主要容器......不過,這是我的解決方案...

你會如何解決這個問題?

+1

我認爲你正在創建不存在的問題。我在一個有很多模塊的大型應用程序上工作,每個模塊都有它的配置。在Symfony世界中也是如此:每個Bundle都有自己的配置,句點。 ZF2也一樣。擁有多個容器只是分離事物的另一種方式,但除了維護和開發更復雜之外,我沒有發現任何差異。 –

+0

也許像我一樣手動實現DI容器比較複雜,但是我有代碼自動完成和完全重構支持,所以維護我的容器非常容易... 全局容器和這些本地那些;我可以通過這種方式控制模塊的外部依賴關係。所以我可以很容易地爲每個模塊編寫集成測試... 我目前有一個測試代碼。我會用一個小項目來測試它,然後我將它推送到github並在這裏分享鏈接... – inf3rno

回答

0

一個IoC容器是正好是的解決方案。

我要具有良好定義的接口注入的依賴,而不是從DI容器或服務定位器

拉他們那麼不使用容器作爲一個服務定位器。這正是DI容器的意義所在。

下面是使用PHP-DI你的例子:

$containerBuilder = new ContainerBuilder(); 
$container = containerBuilder->build(); 

// Configure RestApi\EntryPoint to use for Contract\EntryPoint 
$container->set(Contract\EntryPoint::class, \DI\link(RestApi\EntryPoint::class)); 

// Configure BusinessLogic\Application to use for Contract\Application 
$container->set(Contract\Application::class, \DI\link(BusinessLogic\Application::class)); 

// Run 
$entryPoint = $container->get(Contract\EntryPoint::class); 
$entryPoint->handleRequest(); 

當然這裏我舉的例子是不完美的。我建議將配置部分與執行部分分開。我會把配置放在一個配置文件中(look here for how)。

重要:是的,在那個例子中,我從容器中取東西。這是例外。這在應用程序中應該只發生一次或兩次。

您必須在應用程序根目錄(即應用程序的入口點)某處使用容器來構造對象圖(或依賴關係圖)。

我不是說你每次需要依賴時都要在容器上調用get()。我建議您使用依賴注入,並且僅在應用程序的入口點調用get()


與工廠定義的例子:

$container->set(Contract\EntryPoint::class, \DI\factory(function (Container $c) { 
    return new RestApi\EntryPoint($c->get('some.parameter')); 
})); 
+0

難道你不覺得使用接口名稱作爲令人厭惡的字符串嗎? – inf3rno

+0

是的,我確實:)我不想讓這個例子複雜化,但是我使用了PHP5.5,PHP-DI是爲其構建的(參見主頁http://php-di.org/)。我將用':: class'更新我的例子 –

+0

我想我無法避免我的代碼中的'invoke'部分,所以我的版本只是一個DI容器的sntax糖。我會考慮的。 – inf3rno