2012-05-09 44 views
7

剛剛走出大學,我遇到了一些需要減少耦合的代碼。但我不完全理解所有的概念,並希望有一個簡單的例子來幫助我。爲了讓你開始,我有一個單一的領域,名稱的人類。我在該類中有一個方法來連接一些文本。減少初學者需要的耦合簡單例子

我知道這是一個愚蠢的例子,大多數人永遠不會考慮在這種簡單的情況下減少耦合,但我只想要一個簡單的例子來幫助我完全理解代碼和概念。

在主窗口後面的代碼中,我放置了一個文本框和一個按鈕。當窗口加載時,它顯示人物x名稱字段的當前值。當按鈕被點擊時,調用x.PersonAddText方法。目前這個例子的耦合計算爲8,按鈕點擊事件爲3,窗口加載事件爲3。

有沒有什麼辦法,使用這個例子,我們可以把它降低到小於這個值,以減少它們中的任何一個或兩個。

下面是我的代碼:

我的Person類:

public class Person 
{ 
    //Fields 
    private string name; 

    //Properties 
    public string Name 
    { 
     get { return name; } 
     set { name = value; } 
    } 

    //Constructors 
    public Person() 
    { 
     name = "joe"; 
    } 

    //Methods 
    public string PersonAddText(string text) 
    { 
     return name += " - " + text; 
    } 

    //Interfaces (or additional code below here please to aid understanding) 
} 

我的代碼背後:

Person x = new Person(); 

    public MainWindow() 
    { 
     InitializeComponent(); 
    } 

    private void Window_Loaded(object sender, RoutedEventArgs e) 
    { 
     txtname.Text = x.Name; 
    } 

    private void button1_Click(object sender, RoutedEventArgs e) 
    { 
     txtname.Text = x.PersonAddText(txtname.Text); 
     txtname.Text = x.Name; 
    } 

我簡單的XAML:

<Grid> 
    <TextBox Name="txtname" Margin="12,12,12,0" Height="23" VerticalAlignment="Top" /> 
    <Button Content="Add Text" Margin="12,41,12,0" Name="button1" VerticalAlignment="Top" Click="button1_Click" /> 
</Grid> 

我有很難找到通過互聯網上的教程來解釋這一點。從我看到有3種方式來做到這一點(這將是很好,如果可能的話,有我上面的代碼轉換爲三個的例子):

  • 服務定位器
  • 依賴注入
  • 反演控制反轉(IoC)

article解釋的東西,我讀的是優秀的,但因爲他是用VB和ASP.Net與數據庫連接字符串的例子是與我無關。這與我所需要的完全相反,我不想考慮如何翻譯代碼,同時學習概念,並考慮如何將它應用於相關的事物。雖然這個例子很好,但它太多了,我會很感激任何額外的幫助。

編輯歷史記錄:更正拼寫。增加了以下內容來澄清我的問題:

我理解耦合和共同作用背後的理論,以及爲什麼你應該減少一個,增加另一個。但我們從來沒有在大學編寫任何示例代碼。此外,雖然沒有在大學報道,我確實瞭解接口。但是,我不明白如何使用它們來減少耦合。

添加了鏈接到the article I refrenced above

編輯2:到目前爲止,我現在已經得到如下:

public interface IPerson 
{ 
    string Name { get; set; } 
    string PersonAddText(string text); 
} 

public class Person : IPerson 
{ 
    //The code from the person class above 
} 

我現在該如何在主窗口代碼中使用此背後?我猜我應該

IPerson x = new Person(); 

更換

Person x = new Person(); 

這是正確的,如果是這樣,還有什麼我需要做的。我問的原因是因爲我還沒有看到visual studio報告的代碼耦合數據有所減少(事實上,它在後面的主窗口代碼中增加了1)。

+0

什麼_exactly_你不明白嗎?你遇到什麼困難? – Oded

+0

我知道理論上的coupeling和cohesion意味着什麼。但我不明白如何編碼。特別是因爲我們從來沒有涉及大學的接口(是的,我知道,偉大的大學)。我也理解接口,但我不知道如何使用它們來減少coupe。 –

回答

3

編輯

我很高興我的回答幫助了一下,讓我進一步小幅更新。要使用你的問題作爲一個直接的答案,你需要改變的是你的字段聲明來自:

Person x = new Person(); 

IPerson x = new Person(); 

代碼隱藏現在知道,在指定的屬性和方法您的界面,並且耦合度較低,因爲您可以稍後將new Person()替換爲new Student()。只要對象實現了接口。 您的代碼隱藏現在應該沒有任何必要的更改。

旁註

我會建議考慮延遲加載的x人,並且還使用屬性具有更多的可識別名稱。注:這不能回答你的問題,但這只是一個建議。 :)

private IPerson _CurrentPerson = null; 
private IPerson CurrentPerson 
{ 
    get 
    { 
     if (this._CurrentPerson == null) 
     { 
      this._CurrentPerson = new Person(); 
     } 
     return this._CurrentPerson 
    } 
    set 
    { 
     this._CurrentPerson = value; 
    } 
} 

去耦是當兩個或更多個,代碼塊不應該依賴於彼此。控制反轉是指在運行時綁定對象的耦合,因此允許更多的靈活性,從而減少對象及其實例的耦合。控制反轉最好與接口一起使用。接口定義ClassA將做MethodX並且具有PropertyY。我們的主要目標不關心在運行時返回哪個對象,因爲它可以實現一個接口,它很高興。

在你上面的例子中,你將要對接您的Person類,也許像這樣:

public interface IPerson 
{ 
    string Name { get; set; } 
    string PersonAddText(string text); 
} 

public class Person : IPerson 
{ 
    // your code here 
} 

然後,你的主要方法調用中,而是明確使用Person對象,你會使用實現接口IPerson的對象的實例。接口和對象的「掛鉤」可以通過各種不同的庫來實現,這將有助於設置您的依賴關係。根據我的經驗,我使用了StructureMapMicrosoft's Enterprise Library。他們可以是一個有點繁瑣,以建立,但一旦他們,你就可以做一些事情是這樣的...

public void MainMethod_InInterfaceLayer() 
{ 
    // container is an instance of UnityContainer 
    Person newPerson = container.Resolve<IPerson>(); 
} 

我知道這ins't一個完整的答案,但希望它」我會幫助一下。:)

+0

到目前爲止,這個答案似乎是最容易理解的。我目前正在玩它,並會回覆你。我遇到的唯一問題是您提供的鏈接很快就進入了很多部門,對於這個簡單的例子,理解更重要。如果您可以編寫無需擴展代碼而不是MainMethod_InInterfaceLayer()的代碼,我會獎勵您答案。如果可能的話,可以通過改變我應該在MainWindow代碼中做什麼。感謝您一直以來的幫助。 –

+1

我已經更新了我的答案。希望結合@ Oded的回答,它應該是你正在尋找的。 – Richard

+0

謝謝理查德。我現在只是實現你的答案,背後的解釋是非常好的。我將假設我的例子太簡單了,它不能將Visual Studio Coupling的標記從3減少到2。正如我在編輯中所解釋的,當我像你所描述的那樣做時,不會發生任何減少。感謝您的幫助。我也會和你的旁註一起玩,但我有一種感覺,這有點不相關(儘管我提出了這個建議)。我想知道在這種情況下懶惰加載的好處,以及如果有時間的話,哪些教導會讓你提出建議。 –

2

假設你有一個IPerson的界面和多種實現(PersonStudentTeacher等),你有一些代碼,只需要在一個IPerson操作。

有:

IPerson x = new Person(); 

在後面的代碼強烈夫婦它的Person類。

通過讓依賴來自外部而不是創建類內部,控制的反轉就起作用了。

這通常是通過使用依賴注入 - IPerson被傳入類而不是直接創建它的類來實現的。您可以通過將實例傳遞給構造函數(構造函數注入)或作爲屬性(屬性注入)來實現。

服務定位器是另一種獲取依賴關係而不用硬編碼的方式 - 模式「定位」類型/接口的實例。

的如何注入依賴一類的一個例子:

public class MyClass 
{ 
    IPerson person; 

    public MyClass(IPerson p) 
    { 
    person = p; // injected an instance of IPerson to MyClass 
    } 

    // can now use person in any method of the class, or pass it around: 

    public void MyMethod() 
    { 
    string name = person.Name; 
    } 
} 
+0

首先讓我感謝你的答案。使用richards答案中的代碼,我至今能夠創建一個接口。我現在遇到的問題是如何使用該代碼而不是代碼背後的代碼。或者是你在上面展示給我的東西。它對我來說不是很清楚,但如果你能用更多的代碼來澄清,我會給你答案。再次感謝。 –

+1

@FrancisRodgers - 增加了一個例子,說明'MyClass'類如何與'IPerson'的任何實現分離,所以可以使用_any_實現者。還顯示了構造函數注入的一個例子。 – Oded

0

假設你有一個Person類,它可以從數據庫中加載自身,換衣服和發送電子郵件時,他或她就死了。

class Person 
{ 
    ... 
    void LoadUsingId(int id); 
    void HaveDiedWillMail(); 
    void SetFirstName(string name); 
    ... 
} 

你可以在這裏看到的是,這個人實際上是在做三件事情(至少!)。

它知道如何與數據庫交談來加載自己。 它知道如何發送電子郵件。 它可以改變自己。

任何代碼,理論上如果不是練習,應該只負責一件事。爲了減少這些組件之間的耦合,您必須將它們分開。將它們分開,以便它們可以獨立工作。

class Person 
{ 
    ... 
    void LoadUsingId(PersonRepository person); 
    void HaveDiedWillMail(IMailer mailer); 
    void SetFirstName(string name); 
    ... 
} 

在這個人爲的例子中,Person不知道如何從數據庫中加載某些東西。忽略-loading-這個事實,你也應該解耦。由於您告訴Person.HaveDiedWillMail您想使用特定的郵件程序(IMailer mailer),因此郵件發送也已解耦。

依賴注入,服務容器和此類技術自動查找您的人員需要/想要的組件以便運行,在去耦之上排序一個額外的層,以便更容易地將所有分離的部件連接在一起。

+0

我不同意一個對象(模型)應該從數據庫加載它,即使是鬆散耦合的方式。模型應該*做一件事情*:代表一個真實的對象。從數據庫加載是另一個對象的工作,它將*做一件事*:從數據庫加載數據。實際上,模型和數據庫之間的兩層將會很好。 –

+0

我也同意你的看法。但我不想在這裏解釋所有的東西,甚至不是明智的......但是脫鉤。 – Jaapjan