Co to jest „luźne połączenie”? Proszę podać przykłady


172

Nie mogę zrozumieć pojęcia „luźnego sprzężenia”. Przypuszczam, że nie pomaga to, że słowo „luźne” ma zwykle negatywne konotacje, więc zawsze zapominam, że luźne sprzężenie to dobra rzecz.

Czy ktoś może pokazać jakiś kod (lub pseudokod) „przed” i „po”, który ilustruje tę koncepcję?


Nie jestem pewien, jak można to oddzielić od konkretnego języka, ponieważ część koncepcji polega na tym, jak używa się języka, aby jednostki kodu nie były zbyt splątane ze sobą. Zwracam również uwagę, że kilka odpowiedzi odwołuje się do kodu, aby zilustrować tę koncepcję.
Onorio Catenacci

11
Całkowicie się nie zgadzam, aspekty praktyczne mogą być różnie zilustrowane w różnych językach i jest to szczególnie widoczne w językach dynamicznych i kompilowanych. Ale koncepcja jest taka sama.
Owen

Luźne vs ciasne i Sprzężenie vs Odsprzężenie . Tagowanie się nie zawsze jest dobrą rzeczą, czasem dobrze, czasem źle ... Tak jak sami powinniśmy mieć niezależność , tak samo jest z zajęciami.
bonCodigo

Odpowiedzi:


180

Rozważmy prostą aplikację koszyka na zakupy, która używa klasy CartContents do śledzenia pozycji w koszyku oraz klasy Order do przetwarzania zakupu. Zamówienie musi określić całkowitą wartość zawartości koszyka, może to zrobić w następujący sposób:

Przykład ściśle powiązany:

public class CartEntry
{
    public float Price;
    public int Quantity;
}

public class CartContents
{
    public CartEntry[] items;
}

public class Order
{
    private CartContents cart;
    private float salesTax;

    public Order(CartContents cart, float salesTax)
    {
        this.cart = cart;
        this.salesTax = salesTax;
    }

    public float OrderTotal()
    {
        float cartTotal = 0;
        for (int i = 0; i < cart.items.Length; i++)
        {
            cartTotal += cart.items[i].Price * cart.items[i].Quantity;
        }
        cartTotal += cartTotal*salesTax;
        return cartTotal;
    }
}

Zwróć uwagę, jak metoda OrderTotal (a tym samym klasa Order) zależy od szczegółów implementacji klas CartContents i CartEntry. Gdybyśmy spróbowali zmienić tę logikę, aby umożliwić zniżki, prawdopodobnie musielibyśmy zmienić wszystkie 3 klasy. Ponadto, gdybyśmy zaczęli używać kolekcji List do śledzenia przedmiotów, musielibyśmy również zmienić klasę Order.

Oto nieco lepszy sposób na zrobienie tego samego:

Mniej powiązany przykład:

public class CartEntry
{
    public float Price;
    public int Quantity;

    public float GetLineItemTotal()
    {
        return Price * Quantity;
    }
}

public class CartContents
{
    public CartEntry[] items;

    public float GetCartItemsTotal()
    {
        float cartTotal = 0;
        foreach (CartEntry item in items)
        {
            cartTotal += item.GetLineItemTotal();
        }
        return cartTotal;
    }
}

public class Order
{
    private CartContents cart;
    private float salesTax;

    public Order(CartContents cart, float salesTax)
    {
        this.cart = cart;
        this.salesTax = salesTax;
    }

    public float OrderTotal()
    {
        return cart.GetCartItemsTotal() * (1.0f + salesTax);
    }
}

Logika specyficzna dla implementacji pozycji koszyka lub kolekcji koszyka lub zamówienia jest ograniczona tylko do tej klasy. Mogliśmy więc zmienić implementację którejkolwiek z tych klas bez konieczności zmiany innych klas. Moglibyśmy posunąć się jeszcze dalej, ulepszając projekt, wprowadzając interfejsy itp., Ale myślę, że rozumiesz o co chodzi.


Czy mógłbyś rozszerzyć odpowiedź, wprowadzając dalsze oddzielenie poprzez wstrzykiwanie zależności (chociaż konstruktor powinien być idealny)?
BAD_SEED,

4
@Wedge, nie widzę powodu, Orderaby mieć jakąkolwiek wiedzę o Cart. Zakupy na żywo i fakturowanie to dwa różne konteksty. Gdy klient jest gotowy do zapłaty, możesz mieć proces odpowiedzialny za przetłumaczenie wpisów koszyka na coś znaczącego dla Order. W ten sposób Orderklasa byłaby również instancjonowana i używana bez Cart.
plalx

@Wedge Czy mógłbyś zareagować na mój komentarz?
plalx

Dobrze i po prostu wyjaśnione. Zacząłem dostrzegać światło w „.. Zauważ, że metoda OrderTotal (a tym samym klasa Order) zależy od szczegółów implementacji klas CartContents i CartEntry ..”
okey_on

Ten przykład jest jasno przedstawiony. Jednak w dzisiejszych czasach coraz więcej architektów zgadza się, że ten nazbyt obiektowy styl jest złą praktyką. Nie ma sensu ukrywać szczegółów „implementacji” CartEntryi CartContents. Ponieważ mają one doskonale jasne znaczenie, powinny być po prostu modelowane jako martwe dane. w kategoriach bazy danych wszystko, czego potrzeba, to tabelaCREATE TABLE entry (price INTEGER, quantity INTEGER)
Jo So

160

Wiele zintegrowanych produktów (zwłaszcza Apple), takich jak iPody , iPady, jest dobrym przykładem ścisłego połączenia: gdy bateria wyczerpie się, równie dobrze możesz kupić nowe urządzenie, ponieważ bateria jest przylutowana na stałe i nie poluzuje się, co sprawia, że ​​wymiana jest bardzo kosztowny. Luźno spięty odtwarzacz pozwoliłby na bezproblemową wymianę baterii.

To samo dotyczy tworzenia oprogramowania: generalnie (znacznie) lepiej jest mieć luźno powiązany kod, aby ułatwić rozszerzenie i wymianę (oraz ułatwić zrozumienie poszczególnych części). Jednak bardzo rzadko w szczególnych okolicznościach ścisłe połączenie może być korzystne, ponieważ ścisła integracja kilku modułów pozwala na lepszą optymalizację.


Czy luźne lub ciasne połączenie jest dobre? Jak zdecydować o zastosowaniu luźnego lub ciasnego połączenia?
Sreekanth Karumanaghat

2
dziękuję za ten wspaniały przykład. Spędziłem dużo czasu na innych odpowiedziach, ale nie były tego warte
alamin

1
Sreekanth Karumanaghat, czy wolisz wydać mniej pieniędzy tylko na baterię i znowu mieć działającego iPoda, czy wolisz wydać dużo pieniędzy na całego iPoda?
Koshera

1
@SreekanthKarumanaghat Zwykle luźne połączenie jest lepsze, ponieważ promuje modułowość, a tym samym ułatwia utrzymanie kodu. Jeśli ściśle połączysz swoje klasy / biblioteki / kod, każda mała zmiana, którą wprowadzisz w jednej klasie, będzie musiała zostać zaimplementowana we wszystkich klasach, które jej używają.
Kenny Worden

56

Jako przykładu użyję Javy. Powiedzmy, że mamy klasę, która wygląda następująco:

public class ABC
{
   public void doDiskAccess() {...}
}

Kiedy zadzwonię do klasy, będę musiał zrobić coś takiego:

ABC abc = new ABC();

abc. doDiskAccess();

Na razie w porządku. Powiedzmy teraz, że mam inną klasę, która wygląda tak:

public class XYZ
{
   public void doNetworkAccess() {...}
}

Wygląda dokładnie tak samo jak ABC, ale powiedzmy, że działa w sieci zamiast na dysku. A teraz napiszmy taki program:

if(config.isNetwork()) new XYZ().doNetworkAccess();
else new ABC().doDiskAccess();

To działa, ale jest trochę nieporęczne. Mógłbym to uprościć za pomocą takiego interfejsu:

public interface Runnable
{
    public void run();
}

public class ABC implements Runnable
{
   public void run() {...}
}

public class XYZ implements Runnable
{
   public void run() {...}
}

Teraz mój kod może wyglądać tak:

Runnable obj = config.isNetwork() ? new XYZ() : new ABC();

obj.run();

Zobacz, o ile jest to czystsze i łatwiejsze do zrozumienia? Właśnie zrozumieliśmy pierwszą podstawową zasadę luźnego powiązania: abstrakcja. Kluczem jest tutaj zapewnienie, że ABC i XYZ nie zależą od żadnych metod ani zmiennych klas, które je wywołują. Dzięki temu ABC i XYZ mogą być całkowicie niezależnymi API. Innymi słowy, są one „oddzielone” lub „luźno powiązane” z klasami nadrzędnymi.

Ale co, jeśli potrzebujemy komunikacji między nimi? Cóż, wtedy możemy użyć dalszych abstrakcji, takich jak model zdarzeń, aby upewnić się, że kod nadrzędny nigdy nie musi łączyć się z utworzonymi interfejsami API.


2
Ten przykład nadal ma dość wysoki poziom sprzężenia. Teraz zamiast być połączonym z jedną klasą, do wyboru są dwie. W tym przykładzie założono, że zawsze będą dostępne tylko dwie implementacje „Runnable”. W takim przypadku interfejs nie jest nawet wymagany.
Owen

Nazwane metody Run * i Do * to dla mnie ogromny zapach kodu. Równie dobrze możesz zmienić to na IHandleStuff lub IFrobnicateThings. Runnable jest w tym przypadku zdecydowanie zbyt ogólnym terminem.
Wedge

8
Owen, dziecko podchodzi do mojego przyjaciela. Jeśli nie rozumie sprzężenia, jak ma zrozumieć, co kupuje mu wzór fabryczny? Ten przykład kodu stawia go na właściwej drodze. Chodzi o to, aby lepiej zrozumiał problem i uświadomił sobie, jak wyodrębnić tworzenie obiektów.
64BitBob

6
Czy byłoby to bardziej czytelne, gdyby nazwy ABC i XYZ zostały zmienione na takie jak DiskAccessori NetworkAccessor? I dunno no java ...
MusiGenesis

Czy używa również wzorca projektowego fabryki? Ponieważ tworzymy nowe obiekty XYZ lub ABC w zależności od potrzeb.
munmunbb

37

Przepraszamy, ale „luźne połączenie” to nie problem z kodowaniem, ale z projektem. Termin „luźne sprzężenie” jest ściśle powiązany z pożądanym stanem „wysokiej spójności”, będącej przeciwieństwem, ale komplementarnością.

Luźne powiązanie oznacza po prostu, że poszczególne elementy projektu powinny być konstruowane tak, aby zmniejszyć ilość niepotrzebnych informacji, które muszą wiedzieć o innych elementach projektu.

Wysoka spójność jest czymś w rodzaju „ścisłego sprzężenia”, ale wysoka spójność to stan, w którym elementy projektu, które naprawdę muszą o sobie znać, są projektowane tak, aby współpracowały ze sobą czysto i elegancko.

Chodzi o to, że niektóre elementy projektu powinny znać szczegóły dotyczące innych elementów projektu, więc powinny być zaprojektowane w ten sposób, a nie przypadkowo. Inne elementy projektu nie powinny znać szczegółów na temat innych elementów projektu, więc powinny być projektowane w ten sposób, celowo, a nie losowo.

Realizacja tego pozostawiamy jako ćwiczenie dla czytelnika :).


33

Ściśle powiązany kod opiera się na konkretnej implementacji. Jeśli potrzebuję listy ciągów w moim kodzie i deklaruję to w ten sposób (w Javie)

ArrayList<String> myList = new ArrayList<String>();

to jestem zależny od implementacji ArrayList.

Jeśli chcę to zmienić na luźno powiązany kod, ustawiam odwołanie jako typ interfejsu (lub inny abstrakcyjny).

List<String> myList = new ArrayList<String>();

Uniemożliwia mi to wywołanie jakiejkolwiek metody, myListktóra jest specyficzna dla implementacji ArrayList. Jestem ograniczony tylko do metod zdefiniowanych w interfejsie List. Jeśli później zdecyduję, że naprawdę potrzebuję LinkedList, wystarczy mi zmienić kod w jednym miejscu, w którym utworzyłem nową Listę, a nie w 100 miejscach, w których wykonałem wywołania metod ArrayList.

Oczywiście możesz utworzyć wystąpienie ArrayList przy użyciu pierwszej deklaracji i powstrzymać się od używania metod, które nie są częścią interfejsu List, ale użycie drugiej deklaracji sprawia, że ​​kompilator zachowuje uczciwość.


Nie ma to żadnego związku z tym, czym właściwie jest „sprzęganie” . Zobacz stackoverflow.com/questions/39946/coupling-and-cohesion, aby uzyskać dobre odpowiedzi.
Rogério

@Rogerio: Myślę, że to dotyczy. Być może jesteś zdezorientowany innymi definicjami „sprzężenia”, ale powinieneś przeczytać Luźne sprzężenie . „Silne sprzężenie występuje, gdy klasa zależna zawiera wskaźnik bezpośrednio do konkretnej klasy, która zapewnia wymagane zachowanie ... Luźne sprzężenie występuje, gdy klasa zależna zawiera tylko wskaźnik do interfejsu, który może być następnie zaimplementowany przez jedną lub wiele klas konkretnych ”. W moim przykładzie mój kod byłby luźno powiązany z ArrayList za pośrednictwem interfejsu List.
Bill the Lizard

@Bill: Dzięki za link, ale ten artykuł wydaje mi się całkowicie fałszywy. Wydaje się, że „reinterpretuje” pomysły z innej domeny (zachowania organizacyjne), podając definicję „luźnego powiązania”, która jest sprzeczna z oryginalną definicją Larry'ego Constantine'a dotyczącą oprogramowania: en.wikipedia.org/wiki/Coupling_(computer_science) .
Rogério

@Rogerio: Artykuł Constantine'a został napisany w 1974 roku. Jest prawdopodobne, że w ciągu ostatnich 36 lat dokonano wielu reinterpretacji, ale nie sądzę, aby artykuł w Wikipedii był jego źródłem. Na przykład wzorce takie jak Dependency Injection polegają na interfejsach, aby poluzować sprzężenie, tak jak opisuję w mojej odpowiedzi.
Bill the Lizard

2
@Bill: Jasne, praca Constantine'a nad Structured Design jest stara, ale nie widzę powodu, aby ją unieważnić lub radykalnie zinterpretować. Oryginalne koncepcje spójności i sprzężenia leżą u podstaw projektowania obiektowego, podobnie jak dziedziczenie i polimorfizm. DI (lepszym artykułem jest martinfowler.com/articles/injection.html ) to jedynie technika zewnętrznej konfiguracji wtyczek (abstrakcyjne interfejsy z implementacjami wybieranymi w czasie wykonywania za pomocą kodu konfiguracyjnego); inną taką techniką jest wzorzec Service Locator. DI i sprzęgło to niezależne koncepcje.
Rogério

27

Stopień różnicy między odpowiedziami tutaj pokazuje, dlaczego byłoby to trudne do zrozumienia, ale mówiąc najprościej, jak mogę to opisać:

Żeby wiedzieć, że jeśli rzucę ci piłkę, to ty możesz ją złapać, naprawdę nie muszę wiedzieć, ile masz lat. Nie muszę wiedzieć, co jadłeś na śniadanie i naprawdę nie obchodzi mnie, kto był twoim pierwszym zakochanym. Muszę tylko wiedzieć, że możesz złapać. Jeśli to wiem, to nie obchodzi mnie, czy to ty, rzucam piłkę do ciebie lub twojego brata.

W przypadku języków niedynamicznych, takich jak C #, Java itp., Osiągamy to za pośrednictwem interfejsów. Powiedzmy, że mamy następujący interfejs:

public ICatcher
{
   public void Catch();
}

A teraz powiedzmy, że mamy następujące klasy:

public CatcherA : ICatcher
{
   public void Catch()
   {
      console.writeline("You Caught it");
   }

}
public CatcherB : ICatcher
{
   public void Catch()
   {
      console.writeline("Your brother Caught it");
   }

}

Teraz zarówno CatcherA, jak i CatcherB implementują metodę Catch, więc usługa, która wymaga Catcher, może używać jednego z nich i tak naprawdę nie obchodzi, która to jest. Tak więc ściśle powiązana usługa może bezpośrednio zainicjować przechwyconą tzw

public CatchService
{
   private CatcherA catcher = new CatcherA();

   public void CatchService()
   {
      catcher.Catch();
   }

}

Tak więc CatchService może robić dokładnie to, co zaplanował, ale używa CatcherA i zawsze będzie używać CatcherA. Jest mocno zakodowany, więc pozostaje tam, dopóki ktoś nie przyjdzie i go nie przerobi.

Teraz weźmy inną opcję, zwaną iniekcją zależności:

public CatchService
{
   private ICatcher catcher;

   public void CatchService(ICatcher catcher)
   {
      this.catcher = catcher;
      catcher.Catch();
   }
}

Tak więc calss, które inicjują CatchService, mogą wykonać następujące czynności:

CatchService catchService = new CatchService(new CatcherA());

lub

CatchService catchService = new CatchService(new CatcherB());

Oznacza to, że usługa Catch nie jest ściśle powiązana z CatcherA ani CatcherB.

Istnieje kilka innych strategii dla luźnego łączenia usług, takich jak użycie frameworka IoC itp.


1
Ten przykład podoba mi się bardziej niż inne, ponieważ takie rzeczy zdarzają się w świecie biznesu na co dzień. Jeśli Class Person działa, Class Cat działa, Class Dog biegnie, itd. Byłby to ogromny program do wyszczególnienia wszystkich tych zajęć. Jednak użycie interfejsu o nazwie działa, w którym każda klasa robi to samo, sprawia, że ​​program jest znacznie bardziej elastyczny i łatwiejszy w zarządzaniu. :)
sksallaj

Naprawdę dobrze wyjaśniono, żeby wyjaśnić, że w ostatnim przykładzie zrobiłeś to z zastrzykiem zależności, a pierwszy przykład jest również luźno powiązany?
Ali Sajid,

Wow, stara odpowiedź skomentowana :). Więc odpowiedź brzmi nie, pierwszy przykład ma silną zależność od CatcherA, więc są one silnie powiązane
Owen

23

Możesz myśleć o (ścisłym lub luźnym) sprzężeniu jako o ilości wysiłku, jakiej wymagałoby od ciebie oddzielenie określonej klasy od jej zależności od innej. Na przykład, jeśli każda metoda w Twojej klasie miała mały ostatecznie blok na dole, w którym wywołałeś Log4Net w celu zarejestrowania czegoś, powiedziałbyś, że twoja klasa jest ściśle powiązana z Log4Net. Gdyby zamiast tego twoja klasa zawierała prywatną metodę o nazwie LogSomething, która była jedynym miejscem, które wywoływało komponent Log4Net (a inne metody nazywały się zamiast tego LogSomething), to powiedziałbyś, że twoja klasa była luźno powiązana z Log4Net (ponieważ nie zajmie dużo próba wyciągnięcia Log4Net i zastąpienia go czymś innym).


1
Czyli „luźno powiązane” faktycznie oznacza mniej zależności? Mam rację?
user314362,

4
Nie jest tak naprawdę związane z całkowitą liczbą zależności, które ma jedna konkretna klasa. Sprzężenie (ciasne lub luźne) jest właściwie właściwością relacji między dwiema klasami. Możesz więc mieć jedną klasę, która jest luźno powiązana ze 100 innymi klasami lub inną, która jest ściśle powiązana z tylko 2 innymi klasami (na przykład).
MusiGenesis,

2
Zły przykład, powiedziałbym: masz powielanie kodu, a nie ścisłe sprzężenie. Dzięki dobremu narzędziu do refaktoryzacji takie powielanie można wyeliminować w ciągu kilku sekund.
Rogério

@Rogerio: to chyba nie najlepszy przykład. Ja bardziej lubię 64BitBobodpowiedź.
MusiGenesis

12

Definicja

Zasadniczo sprzężenie to stopień, w jakim dany obiekt lub zestaw obiektów polega na innym obiekcie lub innym zestawie obiektów, aby wykonać swoje zadanie.

Wysokie sprzęgło

Pomyśl o samochodzie. Aby silnik mógł się uruchomić, należy włożyć kluczyk do stacyjki, przekręcić, musi być benzyna, musi pojawić się iskra, tłoki muszą zapalić, a silnik musi ożyć. Można powiedzieć, że silnik samochodu jest silnie sprzężony z kilkoma innymi obiektami. To jest wysokie sprzężenie, ale nie jest to złe.

Luźne powiązanie

Pomyśl o kontrolce użytkownika dla strony internetowej, która jest odpowiedzialna za umożliwienie użytkownikom publikowania, edytowania i przeglądania pewnego rodzaju informacji. Pojedyncza kontrolka może być wykorzystana do umożliwienia użytkownikowi opublikowania nowej informacji lub edycji nowej informacji. Kontrolka powinna mieć możliwość współdzielenia między dwiema różnymi ścieżkami - nową i edytuj. Jeśli formant jest napisany w taki sposób, że potrzebuje jakiegoś rodzaju danych ze stron, które będą go zawierać, można powiedzieć, że jest zbyt silnie powiązany. Formant nie powinien potrzebować niczego ze swojej strony kontenera.


7

To dość ogólna koncepcja, więc przykłady kodu nie dadzą pełnego obrazu.

Pewien facet tutaj, w pracy, powiedział mi: „wzory są jak fraktale, można je zobaczyć, gdy się bardzo blisko przybliża, a kiedy przybliża się do poziomu architektury”.

Przeczytanie krótkiej strony Wikipedii może dać ci poczucie tej ogólności:

http://en.wikipedia.org/wiki/Loose_coupling

Jeśli chodzi o konkretny przykład kodu ...

Oto jedno luźne połączenie, z którym ostatnio pracowałem, z rzeczy Microsoft.Practices.CompositeUI.

    [ServiceDependency]
    public ICustomizableGridService CustomizableGridService
    {
        protected get { return _customizableGridService; }
        set { _customizableGridService = value; }
    }

Ten kod deklaruje, że ta klasa ma zależność od CustomizableGridService. Zamiast bezpośrednio odwoływać się do dokładnej implementacji usługi, po prostu stwierdza, że ​​wymaga ona NIEKTÓREJ implementacji tej usługi. Następnie w czasie wykonywania system rozwiązuje tę zależność.

Jeśli nie jest to jasne, możesz przeczytać bardziej szczegółowe wyjaśnienie tutaj:

http://en.wikipedia.org/wiki/Dependency_injection

Wyobraź sobie, że ABCCustomizableGridService to implementacja, którą zamierzam tutaj podłączyć.

Jeśli zdecyduję się, mogę to wyciągnąć i zastąpić XYZCustomizableGridService lub StubCustomizableGridService bez żadnych zmian w klasie z tą zależnością.

Gdybym bezpośrednio odwołał się do ABCCustomizableGridService, musiałbym wprowadzić zmiany w tym / tych odwołaniach, aby zamienić inną implementację usługi.


6

Sprzężenie ma związek z zależnościami między systemami, które mogą być modułami kodu (funkcjami, plikami lub klasami), narzędziami w potoku, procesami serwer-klient i tak dalej. Im mniej ogólne są zależności, tym bardziej stają się „ściśle powiązane”, ponieważ zmiana jednego systemu wymagała zmiany innych systemów, które na nim polegają. Idealną sytuacją jest „luźne sprzężenie”, w którym można zmienić jeden system, a systemy od niego zależne będą dalej działać bez modyfikacji.

Ogólnym sposobem uzyskania luźnego połączenia są dobrze zdefiniowane interfejsy. Jeśli interakcja między dwoma systemami jest dobrze zdefiniowana i przestrzegana po obu stronach, łatwiej będzie zmodyfikować jeden system, zapewniając jednocześnie, że konwencje nie zostaną złamane. W praktyce często zdarza się, że nie tworzy się dobrze zdefiniowanego interfejsu, co powoduje niechlujną konstrukcję i ścisłe połączenie.

Kilka przykładów:

  • Aplikacja zależy od biblioteki. Przy ścisłym sprzężeniu aplikacja psuje się w nowszych wersjach lib. Google dla nazwy „DLL Hell”.

  • Aplikacja kliencka odczytuje dane z serwera. Przy ścisłym sprzężeniu zmiany na serwerze wymagają poprawek po stronie klienta.

  • Dwie klasy współdziałają w hierarchii zorientowanej obiektowo. Przy ścisłym sprzężeniu zmiany w jednej klasie wymagają aktualizacji drugiej w celu dopasowania.

  • W rurze komunikuje się wiele narzędzi wiersza polecenia. Jeśli są one ściśle powiązane, zmiany w wersji jednego narzędzia wiersza polecenia spowodują błędy w narzędziach odczytujących jego dane wyjściowe.


5

Sprzężenie odnosi się do tego, jak ściśle różne klasy są ze sobą połączone. Ściśle powiązane klasy zawierają dużą liczbę interakcji i zależności.

Klasy luźno powiązane są przeciwieństwem, ponieważ ich wzajemne zależności są ograniczone do minimum i zamiast tego polegają na dobrze zdefiniowanych wzajemnych interfejsach publicznych.

Legos, zabawki, które SNAP razem są uważane za luźno połączone, ponieważ można po prostu połączyć razem elementy i zbudować dowolny system. Jednak układanka ma elementy, które są MOCNIE połączone. Nie można wyjąć elementu z jednej układanki (systemu) i włożyć go do innej układanki, ponieważ system (układanka) jest bardzo zależny od bardzo konkretnych elementów, które zostały zbudowane specjalnie dla tego konkretnego „projektu”. Klocki Lego są zbudowane w bardziej ogólny sposób, dzięki czemu można ich używać w Twoim domu Lego lub w moim Lego Alien Man.

Źródła: https://megocode3.wordpress.com/2008/02/14/coupling-and-cohesion/


4

Dwa komponenty są silnie sprzężone, gdy zależą od konkretnej implementacji.

Załóżmy, że mam ten kod gdzieś w metodzie w mojej klasie:

this.some_object = new SomeObject();

Teraz moja klasa zależy od SomeObject i są one silnie powiązane. Z drugiej strony, powiedzmy, że mam metodę InjectSomeObject:

void InjectSomeObject(ISomeObject so) { // note we require an interface, not concrete implementation
  this.some_object = so;
}

Wtedy pierwszy przykład może po prostu użyć wstrzykniętego SomeObject. Jest to przydatne podczas testowania. Podczas normalnej pracy możesz używać ciężkich, wykorzystujących bazy danych, korzystających z sieci klas itp., Podczas gdy testy przechodzą lekką, próbną implementację. Z ściśle powiązanym kodem nie możesz tego zrobić.

Możesz ułatwić niektóre części tej pracy, używając kontenerów iniekcji zależności. Więcej o DI można przeczytać w Wikipedii: http://en.wikipedia.org/wiki/Dependency_injection .

Czasami łatwo jest posunąć się za daleko. W pewnym momencie musisz uczynić rzeczy konkretnymi, inaczej twój program będzie mniej czytelny i zrozumiały. Więc używaj tej techniki głównie na granicy komponentów i wiedz, co robisz. Upewnij się, że korzystasz z luźnego połączenia. Jeśli nie, prawdopodobnie nie potrzebujesz go w tym miejscu. DI może uczynić twój program bardziej złożonym. Upewnij się, że dokonałeś dobrego kompromisu. Innymi słowy, utrzymuj dobrą równowagę. Jak zawsze przy projektowaniu systemów. Powodzenia!


3

Rozważ aplikację systemu Windows z FormA i FormB. FormA jest formą podstawową i wyświetla FormB. Wyobraź sobie, że FormB musi przekazać dane z powrotem do swojego rodzica.

Jeśli zrobiłeś to:

class FormA 
{
    FormB fb = new FormB( this );

    ...
    fb.Show();
}

class FormB 
{
    FormA parent;

    public FormB( FormA parent )
    {
        this.parent = parent;
    }     
}

FormB jest ściśle powiązany z FormA. FormB nie może mieć innego elementu nadrzędnego niż typ FormA.

Jeśli, z drugiej strony, miałeś FormB opublikować zdarzenie i zasubskrybowałeś FormA to zdarzenie, to FormB może przekazać dane z powrotem przez to zdarzenie do dowolnego subskrybenta, który ma to zdarzenie. W takim przypadku FormB nawet nie wie, że odzywa się do swojego rodzica; dzięki luźnemu powiązaniu wydarzenie zapewnia po prostu rozmowę z abonentami. Każdy typ może teraz być rodzicem FormA.

rp


3

W informatyce jest jeszcze jedno znaczenie „luźnego sprzężenia”, o którym nikt inny tu nie pisał, więc ... Proszę bardzo - mam nadzieję, że oddasz mi kilka głosów, aby nie stracić tego na dnie stosu! Z pewnością temat mojej odpowiedzi należy do jakiejkolwiek wyczerpującej odpowiedzi na pytanie ... To znaczy:

Termin „Loose Coupling” po raz pierwszy pojawił się w informatyce jako termin używany jako przymiotnik dotyczący architektury procesora w konfiguracji wieloprocesorowej. Jego odpowiednikiem jest „ścisłe sprzężenie”. Loose Coupling występuje, gdy procesory nie współdzielą wielu wspólnych zasobów, a Tight Coupling - wtedy, gdy to robią.

Termin „system” może być tutaj mylący, dlatego prosimy o uważne przeanalizowanie sytuacji.

Zwykle, ale nie zawsze, wiele procesorów w konfiguracji sprzętowej, w której istnieją one w jednym systemie (jak w pojedynczych komputerach PC), byłoby ściśle powiązanych. Z wyjątkiem niektórych systemów o bardzo wysokiej wydajności, które mają podsystemy, które faktycznie współdzielą pamięć główną między „systemami”, wszystkie podzielne systemy są luźno powiązane.

Pojęcia ściśle powiązane i luźno powiązane zostały wprowadzone wcześniej wynalezieniem wielowątkowych i wielordzeniowych procesorów, więc terminy te mogą wymagać pewnych towarzyszących im elementów, aby w pełni przedstawić dzisiejszą sytuację. I rzeczywiście, dzisiaj można bardzo dobrze mieć system, który obejmuje oba typy w jeden ogólny system. Jeśli chodzi o obecne systemy oprogramowania, istnieją dwie wspólne architektury, po jednej z każdej odmiany, które są wystarczająco powszechne i powinny być znane.

Po pierwsze, ponieważ o to właśnie chodziło, kilka przykładów systemów luźno powiązanych:

  • VaxClusters
  • Klastry Linux

Z drugiej strony, niektóre ściśle powiązane przykłady:

  • Semetrical-Multi-Processing (SMP) Systemy operacyjne - np. Fedora 9
  • Wielowątkowe procesory
  • Procesory wielordzeniowe

We współczesnych komputerach przykłady działania obu systemów w jednym systemie nie są rzadkością. Weźmy na przykład nowoczesne dwu- lub czterordzeniowe procesory Pentium z Fedorą 9 - są to ściśle powiązane systemy obliczeniowe. Następnie połącz kilka z nich w luźno powiązany klaster Linuksa, a teraz masz zarówno luźne, jak i ściśle powiązane obliczenia! Och, czy nowoczesny sprzęt nie jest wspaniały!


3

Mówiąc prostym językiem, luźno powiązane oznacza, że ​​nie zależy to od innego zdarzenia. Wykonuje się niezależnie.


1
To nie do końca prawda - jak wspomniano w innych przykładach, nie chodzi tak bardzo o to, czy istnieje związek między jednostkami (modułami, klasami), ale o tym, czy można je łatwo zamienić na inne klasy implementujące tę samą funkcjonalność. np. ViewModel może działać z różnymi modelami, ale na pewno nie będzie działać bez jednego
Hayden Crocker

eh, niezbyt pomocne
brgs

2

Kilka długich odpowiedzi. Zasada jest jednak bardzo prosta. Przesyłam oświadczenie otwierające z wikipedii :

„Luźne powiązanie” opisuje odporną relację między dwoma lub więcej systemami lub organizacjami z pewnego rodzaju relacjami wymiany.

Każdy koniec transakcji jasno określa swoje wymagania i przyjmuje niewiele założeń co do drugiego końca ”.


1
Gdyby to było takie proste, nie mielibyśmy tej dyskusji.
brgs

2

Proponuję bardzo prosty Test sprzęgania kodu :

  1. Fragment A kodu jest ściśle powiązany z Fragmentem B kodu, jeśli istnieje jakakolwiek możliwa modyfikacja Części B, która wymusiłaby zmiany w Części A w celu zachowania poprawności.

  2. Fragment A kodu nie jest ściśle powiązany z Częścią B kodu, jeśli nie ma możliwości modyfikacji Części B, która spowodowałaby konieczność zmiany Części A.

Pomoże ci to sprawdzić, ile sprzężeń występuje między fragmentami twojego kodu. aby znaleźć uzasadnienie, zobacz ten post na blogu: http://marekdec.wordpress.com/2012/11/14/loose-coupling-tight-coupling-decoupling-what-is-that-all-about/


2

Podczas tworzenia obiektu klasy przy użyciu new słowa kluczowego w innej klasie, faktycznie robisz ścisłe połączenie (zła praktyka) zamiast tego powinieneś używać luźnego sprzężenia, co jest dobrą praktyką

--- A.java ---

package interface_package.loose_coupling;

public class A {

void display(InterfaceClass obji)
{
    obji.display();
    System.out.println(obji.getVar());
}
}

--- B.java ---

package interface_package.loose_coupling;

public class B implements InterfaceClass{

private String var="variable Interface";

public String getVar() {
    return var;
}

public void setVar(String var) {
    this.var = var;
}

@Override
public void display() {
    // TODO Auto-generated method stub
    System.out.println("Display Method Called");
}
}

--- InterfaceClass ---

package interface_package.loose_coupling;

public interface InterfaceClass {

void display();
String getVar();
}

--- MainClass ---

package interface_package.loose_coupling;

public class MainClass {

public static void main(String[] args) {
    // TODO Auto-generated method stub

    A obja=new A();
    B objb=new B();
    obja.display(objb);     //Calling display of A class with object of B class 

}
}

Wyjaśnienie:

W powyższym przykładzie mamy dwie klasy A i B.

Klasa B implementuje Interface, czyli InterfaceClass.

InterfaceClass definiuje Contract for B, ponieważ InterfaceClass ma abstrakcyjne metody klasy B, do których może uzyskać dostęp każda inna klasa, na przykład A.

W klasie A mamy metodę display, która może poza obiektem klasy implementującej InterfaceClass (w naszym przypadku jest to klasa B). A na tym obiekcie metoda klasy A wywołuje metody display () i getVar () klasy B

W MainClass stworzyliśmy obiekt klasy A i B. Wywołanie metody wyświetlania A poprzez przekazanie obiektu klasy B tj. Objb. Metoda wyświetlania A zostanie wywołana z obiektem klasy B.

Teraz mówię o luźnym sprzężeniu. Załóżmy, że w przyszłości będziesz musiał zmienić nazwę klasy B na ABC, wtedy nie będziesz musiał zmieniać jej nazwy w metodzie wyświetlania klasy B, po prostu utwórz obiekt nowy (klasa ABC) i przekaż go do metody wyświetlania w MailClass. Nie musisz niczego zmieniać w klasie A.

ref: http://p3lang.com/2013/06/loose-coupling-example-using-interface/


0

Możesz przeczytać więcej o ogólnej koncepcji „luźnego sprzężenia” .

Krótko mówiąc, jest to opis relacji między dwiema klasami, gdzie każda klasa wie najmniej o sobie i każda klasa może potencjalnie nadal dobrze działać bez względu na to, czy druga jest obecna, czy nie i bez zależności od konkretnej implementacji drugiej klasa.


-8

Ogólnie rzecz biorąc, luźne powiązanie to 2 podmioty pracujące niezależnie od siebie przy tym samym obciążeniu pracą. Więc gdybyś miał 2 serwery internetowe korzystające z tej samej bazy danych zaplecza, powiedziałbyś, że te serwery internetowe są luźno powiązane. Przykładem ścisłego połączenia może być posiadanie 2 procesorów na jednym serwerze WWW ... te procesory są ściśle powiązane.

Mam nadzieję, że to trochę pomocne.


1
Rozumiem, do czego zmierzasz, ale nadal nie jest to dobry sposób na wyjaśnienie, że ściśle powiązane jest to, że dwie „rzeczy” są od siebie współzależne, w porównaniu do luźno powiązanych, gdy dwie „rzeczy” istnieją niezależnie - gdzie jedna „rzecz” może być zmienionym bez zmian w innej „rzeczy” ...
Bryan Rehbein,

Steve, masz DOKŁADNĄ rację - nie zasłużyłeś na ŻADNE głosy negatywne. Po prostu twoi odbiorcy nie byli gotowi na twoją terminologię, a zbyt wielu na tej stronie ma zwyczaj głosowania przeciwko temu, czego nie rozumieją lub co jest nieznane. -shrug-
Richard T

Myślę, że Steve przegapił punkt. Luźne powiązanie nie zawsze oznacza, że ​​2 aktorów pracuje niezależnie nad tym samym obciążeniem.
Rob
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.