2012-05-09 12 views
7

Po studiach pojawia się kod, w którym muszę zmniejszyć sprzężenie. Ale nie rozumiem w pełni wszystkich koncepcji i chciałbym prostego przykładu, aby mi pomóc. Aby zacząć, mam klasę osoby z jednym polem, nazwą. Mam w tej klasie metodę łączenia jakiegoś tekstu.Redukcja sprzężenia prosty przykład potrzebny dla początkujących

Wiem, że jest to głupi przykład i większość ludzi nigdy nie rozważałby zmniejszenia sprzężenia w sytuacjach tak prostych jak ten, ale chcę tylko prostego przykładu, aby pomóc mi w pełni zrozumieć kod i koncepcje razem.

W kodzie za głównym oknem umieszczam pole tekstowe i przycisk. Po załadowaniu okna pokazuje aktualną wartość pola person x name. Po kliknięciu przycisku wywoływana jest metoda x.PersonAddText. Obecnie ten przykład ma sprzężenie obliczone na 8. Z 3 dla zdarzenia kliknięcia przycisku i 3 dla zdarzenia załadowanego przez okno.

Czy jest jakiś sposób, używając tego przykładu, możemy obniżyć to do mniej niż dla jednego lub obu z nich.

Poniżej jest cały mój kod:

Osoba Klasa:

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) 
} 

mojego kodu Behind:

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; 
    } 

mój prosty 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> 

Mam wielka trudność unde Opanuj tutoriale w Internecie wyjaśniając to. Z tego co widzę są 3 sposoby, aby to zrobić (byłoby miło, jeśli to możliwe, aby mieć mojego kodu powyżej przerobiona na przykład wszystkich trzech):

  • lokalizatora usług
  • Dependency Injection
  • Inversion kontroli (MKOl)

article wyjaśniając rzeczy Czytałem jest doskonała, ale przykłady nie są istotne dla mnie, jest on przy użyciu VB i ASP.NET z ciągów połączenia bazy danych. Jest to całkowicie sprzeczne z tym, czego potrzebuję i nie chcę myśleć o tym, jak przetłumaczyć kod, ucząc się tych pojęć i zastanawiając się, jak zastosować je do czegoś istotnego. Chociaż przykład jest dobry, to po prostu za dużo, a ja naprawdę doceniam każdą dodatkową pomoc.

Edytuj historię: poprawiono pisownię. Dodano następujące informacje w celu wyjaśnienia mojego pytania:

Rozumiem teorię leżącą u podstaw sprzężenia i kohezji i dlaczego należy ją zredukować, a zwiększyć drugą. Ale nigdy nie napisaliśmy żadnych przykładów na studiach. Poza tym, chociaż nie jest to na studiach, rozumiem interfejsy. Jednak nie rozumiem, jak z nich korzystać, aby zmniejszyć sprzężenie.

Dodano link do the article I refrenced above.

Edit 2: Do tej pory, co ja teraz mam jest następujący:

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

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

Jak mogę teraz użyć tego w kodzie MainWindow tyłu?Domyślam należy wymienić

Person x = new Person(); 

z

IPerson x = new Person(); 

Czy to poprawne, a jeśli tak, to czy jest coś jeszcze muszę zrobić. Powodem, dla którego pytam, jest to, że nadal nie widzę żadnej redukcji w liczbach sprzężenia kodu zgłaszanych przez studio graficzne (infact, zwiększa to o 1 w głównym kodzie okna).

+0

Co _exactly_ nie rozumiesz? Z jaką koncepcją masz problemy? – Oded

+0

Wiem, co oznacza coupeling i spójność w teorii. Ale nie rozumiem, jak to zakodować. Zwłaszcza, że ​​nigdy nie obejmowaliśmy interfejsów na studiach (tak, wiem, świetna uczelnia). Rozumiem także interfejsy, ale nie wiem, jak je wykorzystać, aby zmniejszyć coupeling. –

Odpowiedz

3

Edit

Cieszę moja odpowiedź pomogła trochę, pozwól mi go zaktualizować nieco dalej. Aby użyć pytanie jako bezpośrednia odpowiedź, wszystko, czego potrzeba, aby zmienić to oświadczenie pola od:

Person x = new Person(); 

do

IPerson x = new Person(); 

Twój kod z opóźnieniem teraz wiem, właściwości i metody, które są określone w twój interfejs i jest o wiele mniej sprzężony, ponieważ możesz później wymienić new Person() dla new Student(). Dopóki obiekt implementuje interfejs. Twój kod dostępu powinien teraz działać bez żadnych koniecznych zmian.

uwaga Side

Polecam rozważa leniwy ładowania x osoby, a także przy użyciu właściwości z bardziej rozpoznawalnej nazwy. N.B. to nie odpowiada na twoje pytanie, ale to tylko sugestia. :)

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

De-sprzęgło jest, gdy dwa lub więcej bloki kodu nie powinna zależeć od siebie. Inwersja kontroli ma miejsce, gdy sprzężenie obiektów jest wiązane w czasie wykonywania, co pozwala na znacznie większą elastyczność, a zatem mniej sprzężeń obiektów i ich instancji. Odwrócenie kontroli najlepiej stosować w połączeniu z interfejsami. Interfejsy definiują, że ClassA zrobi MethodX i będzie mieć PropertyY. Nasz główny obiekt nie dba o to, który obiekt jest zwracany w czasie wykonywania, ponieważ log, ponieważ może on wypełnić interfejs, jest szczęśliwy.

W swojej powyższym przykładzie, będziemy chcieli, aby interfejs swoją klasę osoby, może coś tak:

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

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

Następnie, w ramach głównych wywołań metod, zamiast bezpośrednio za pomocą Person obiekt, będzie używać wystąpienie obiektu implementującego interfejs IPerson. "Zahaczenie" interfejsu i obiektu można osiągnąć za pomocą różnych bibliotek, które pomogą w ustawieniu twoich zależności. Z mojego doświadczenia wynika, że ​​użyłem StructureMap i Microsoft's Enterprise Library. Mogą być trochę fiddly do set-up, ale raz są, będziesz w stanie zrobić coś tak ...

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

wiem to ins't pełną odpowiedź, ale mam nadzieję, że to” Pomogę ci trochę.:)

+0

Jak dotąd ta odpowiedź wydaje się najłatwiejsza do zrozumienia. Obecnie gram z nim i skontaktuję się z Tobą. Jedyny problem, jaki mam, to to, że linki, które dostarczyłeś, dostają się dość szybko i na tym prostym przykładzie, zrozumienie jest ważniejsze. Udzieliłbym ci odpowiedzi, jeśli możesz napisać kod bez rozszerzeń zamiast MainMethod_InInterfaceLayer().Być może zmieniając to, co powinienem zrobić w kodzie MainWindow, jeśli to możliwe. Dzięki za pomoc do tej pory. –

+1

Zaktualizowałem swoją odpowiedź. Mamy nadzieję, że w połączeniu z odpowiedzią @ Odeda, powinno być to, czego szukasz. – Richard

+0

Dzięki Richard. Właśnie wprowadzam twoją odpowiedź teraz i wyjaśnienie stojące za nią jest bardzo miłe. Zakładam, że mój przykład jest zbyt prosty, aby zredukować znak Visual Studios Coupling z 3 do 2 na wymienionych metodach. Jak wyjaśniono w moich edycjach, redukcja nie ma miejsca, gdy robię tak, jak opisałeś. Dziękuję za pomoc w tej sprawie. Będę też grał z twoją notatką boczną, ale mam wrażenie, że jest to trochę niezwiązane (choć akceptuję sugestię). Chciałbym poznać zalety leniwego ładowania w tym przypadku i jaką linię nauczania nauczyłem cię, by sugerować, jeśli masz czas. –

2

Powiedzmy, że masz interfejs IPerson i kilka realizacji (Person, Student, Teacher etc) i masz jakiś kod, który po prostu musi działać na IPerson.

uwzględniając:

IPerson x = new Person(); 

w kodzie za mocno pary to do klasy Person.

Inwersja kontroli wchodzi w grę dzięki zależnościom pochodzącym z zewnątrz, zamiast tworzenia wewnątrz klasy.

Zwykle uzyskuje się to za pomocą zastrzyku zależności - do klasy jest przekazywana klasa IPerson, a nie klasa bezpośrednio ją tworząca. Możesz to zrobić, przekazując instancję do konstruktora (iniektor konstruktora) lub jako właściwość (wtrysk właściwości).

Lokalizator usług jest innym sposobem uzyskania zależności bez twardego kodowania ich - wzorzec "lokalizuje" instancję dla typu/interfejsu.

Przykładem jak wstrzyknąć zależność do klasy:

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

Najpierw pozwól mi podziękować za odpowiedź. Korzystając z kodu w odpowiedzi na Richardsa, udało mi się stworzyć interfejs do tej pory. Problem, który mam teraz, polega na użyciu tego zamiast kodu w kodzie. Czy to właśnie pokazujesz mi powyżej? Nie jest to dla mnie jasne, ale jeśli potrafisz wyjaśnić za pomocą dodatkowego kodu, udzielę ci odpowiedzi. Dzięki jeszcze raz. –

+1

@FrancisRodgers - Dodano przykład, w jaki sposób klasa 'MyClass' jest oddzielona od jakiejkolwiek implementacji' IPersona', więc _any_ implementer może być użyty. Pokazuje również przypadek wtrysku konstruktora. – Oded

0

Załóżmy, że mamy klasy osoba, która może ładować się z bazą danych, przebrać i wysłać wiadomość e-mail, gdy on lub ona umarła.

To, co można tu zobaczyć, to fakt, że ta osoba faktycznie robi trzy rzeczy (przynajmniej!).

Wie, jak rozmawiać z bazą danych, aby załadować się. Wie, jak wysyłać wiadomości e-mail. Może się zmienić.

Każdy kod, teoretycznie, jeśli nie praktykuje, powinien być odpowiedzialny tylko za jedno. Aby zmniejszyć sprzężenie między tymi komponentami, musisz je rozdzielić. Odłącz je, aby mogły działać niezależnie.

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

W tym wymyślonym przykładzie osoba nie ma pojęcia, jak załadować coś z bazy danych. Ignorując fakt, że ładowanie jest czymś, co należy rozdzielić. Wysyłanie poczty również zostało odłączone, ponieważ mówisz Person.HaveDiedWillMail, że chcesz użyć konkretnego programu pocztowego (IMailer mailer).

Wstrzyknięcie zależne, pojemniki serwisowe i taka technologia automatycznie odnajdują komponenty, których potrzebuje/chce użytkownik, aby funkcjonować, sortując dodatkową warstwę nad odsprzęganiem, aby ułatwić łączenie ze sobą wszystkich oddzielnych części.

+0

Nie zgadzam się, że obiekt (model) powinien ładować się z bazy danych, nawet w luźny sposób. Model powinien * wykonać jedną rzecz *: reprezentować prawdziwy obiekt. Ładowanie z bazy danych jest zadaniem innego obiektu, który * zrobi jedno *: ładuje dane z bazy danych. W rzeczywistości dwie warstwy pomiędzy modelem a bazą danych byłyby świetne. –

+0

Zgadzam się również z tobą. Ale nie staram się wyjaśniać wszystkiego tutaj, a nawet tego, co jest rozsądne ... ale oddzielać. – Jaapjan