2017-05-05 60 views
9

我想了解真正定義了緊耦合的相關功能。我有readposts關於這個問題,但有一點仍然不正確的坐我一個number使用抽象接口VS緊密耦合

據我所知,類應該被注入到使用他們的接口,而其具體實現其他類。我也明白,如果一個類遵循一個接口,那麼任何使用注入接口的類都可以調用接口中定義的公共函數,並期望類似的功能。

interface iFormatter() 
{ 
    public function format(array $order): array 
} 

public class OrderFormatter implements iFormatter 
{ 
    public function format(array $order): array 
    { 
     // ... 

     return $formattedArray; 
    } 
} 

public class OrderGenerator implements iGenerator 
{ 
    private $formatter; 

    public function __construct(iFormatter $formatter) 
    { 
     $this->formatter = $formatter; 
    } 

    public function generate() 
    { 
      // ... 

      return $this->formatter->format($order); 
    } 
} 

所以我覺得在格式化的變化,這將被定義爲鬆耦合的情況;

$example = new OrderGenerator(new CarOrderFormatter); 
$example->generate(); 

$example = new OrderGenerator(new VanOrderFormatter); 
$example->generate(); 

雖然我不太清楚當你將責任從彼此抽象出來但這些類仍然彼此緊密聯繫在一起。像

$example = new CarOrderGenerator(new CarOrderFormatter); 
$example->generate(); 

$example = new VanOrderGenerator(new VanOrderFormatter); 
$example->generate(); 

事情是,你可以在不同的格式傳遞給這些發電機,但肯定更經常會如果從generate功能在XXXOrderGenerator具體類期待某些數據format函數內發生而不是一些錯誤?

所以我相信有抽象以上,雖然接口已經使用我不知道如果這仍是緊耦合的,或者技術上鬆散耦合最後一個例子把責任落實到自己的類?或者,如果我錯過了點......完全是說,當一個類有其他的依賴或依賴於內部或其他的細節要緊密結合

回答

6

類。

在你給OrderGenerator和OrderFormatter不緊密耦合的,因爲沒有一個是依賴於另一個第一示例的情況下:它們都依賴於iFormatter。另一種說法是OrderFormatter對OrderGenerator一無所知,OrderGenerator對OrderFormatter一無所知,它只知道傳遞給它的對象實現了函數'format'。當您將CarOrderFormatter或VanOrderFormatter傳遞給OrderGenerator而沒有不利結果時,您已將其突出顯示。

在將CarOrderFormatter傳遞給CarOrderGenerator的第二個示例中,您提到肯定會發生錯誤,您將VanOrderFormatter傳遞給CarOrderGenerator。如果CarOrderGenerator中的代碼依賴於CarOrderFormatter的實現細節,那麼即使iFormatter接口是CarOrderGenerator所看到的,這些類也是緊密耦合的。這段代碼會令人困惑,因爲CarOrderGenerator用它的'契約'說它不關心格式化器通過了什麼,但顯然它確實通過了。因此,如果CarOrderGenerator明確表示您只能將CarOrderFormatter傳遞給其構造函數,那麼在代碼的清晰度方面會更好。抽象(也就是接口的使用)並不是一件壞事,需要考慮如何定義接口。例如說,而不是隻是一個iFormatter接口,而不是你有iCarOrderFormatter和iVanOrderFormatter兩者的定義相同,但CarOrderGenerator需要iCarOrderFormatter和VanOrderGenerator需要iVanOrderFormatter。在您的例子可以變成:

$example = new CarOrderGenerator(new CarOrderFormatter 
$example->generate(); 
$example = new CarOrderGenerator(new SportsCarOrderFormatter) 
$example->generate(); 

$example = new VanOrderGenerator(new PassengerVanOrderFormatter 
$example->generate(); 
$example = new VanOrderGenerator(new CargoVanOrderFormatter) 
$example->generate(); 

實現的關鍵是該接口被引入到創造什麼可以傳遞給發電機沒有它造成的問題的靈活性。

+0

謝謝,這件事對我來說很重要 – myol

1

你是正確的,如果碰上汽車/貨車混凝土的iGenerator和iFormatter你很可能將有一個不好的時候混合的依賴關係。但事情是,你不應該遇到這種情況,如果你應該回到繪圖板,因爲你做錯了轉彎。

我不知道,如果你的例子是人爲的還是緊密依託(或沒有),以您的問題域。無論哪種情況,這個問題都是糟糕的設計。你的界面應該代表一個單一的責任或目的。爲此,iOrderGenerator和iFormatter的目的是什麼?你的例子所有iOrderGenerator都會包裝iFormatter。我可以重新設計iOrderGenerator,因爲它沒有任何用處。問題。解決了。

好了,我們不要採取簡單的出路,說iOrderGenerator也有一個目的。那麼這個目的應該與iFormatter不同。如果您的接口實現跨越其單一目的的邊界或者複製另一個接口的目的,那麼您的設計可能會很糟糕。舉例來說,讓我們說iFormatter的目的是根據訂單類型(麪包車或汽車)以不同的方式打印訂單。然後iOrderGenerator負責創建訂單(任何類型)。但我可能有不止一種方式來創建訂單。 CsvOrderGenerator或XmlOrderGenerator或BulkOrderGenerator(但不包含CarOrderGen或VanOrderGen)。

Nit挑選你的例子,我們可以指出設計問題,解釋當你試圖演示一個好的代碼概念時,你最終會遇到一個不舒服的情況。

你iFormatter是一個糟糕的接口,因爲它返回一個模糊值(列)。任何使用返回值的東西都需要深入瞭解具體如何工作以瞭解陣列中包含或未包含哪些項目。即我怎麼會知道沒有讀取代碼或查看結果(調試/打印)有一個orderId鍵值。相反,iFormatter應該返回一個固定的良好描述類型,即CarFormat或VanFormat,其中每一個都可能實現一個提供訂單號和價格的常見訪問器的iFormat。

iOrderGenerator也許應該產生iOrder。其中可能有一個方法getFormatter:iFormatter。

這些更改可能會清理設計,併爲每個具體情況指明更明確的目的,這些目標不會將您引導至初始狀態。

+0

謝謝你關於模糊返回值的觀點,這改變了我對如何完全構造類的看法。我覺得你也應該得到你的解釋的賞金。 – myol