8.1 Nedbrytning är allt

För tydlighetens skull, en bild från en bra artikel "Decoupling of Object-Oriented Systems", som illustrerar huvudpunkterna som kommer att diskuteras.

Sönderfall

Tycker du fortfarande att det är enkelt att designa en applikationsarkitektur?

8.2 Gränssnitt, implementeringsdöljning

Huvudprinciperna för att minska kopplingen av systemet är principerna för OOP och principen om inkapsling + abstraktion + polymorfism bakom dem.

Det är därför:

  • Moduler ska vara "svarta lådor" för varandra (inkapsling) . Det betyder att en modul inte ska "klättra" in i en annan modul och veta något om dess interna struktur. Objekt i ett delsystem ska inte direkt komma åt objekt i ett annat delsystem.
  • Moduler/delsystem bör interagera med varandra endast genom gränssnitt (det vill säga abstraktioner som inte är beroende av implementeringsdetaljer). Följaktligen måste varje modul ha ett eller flera väldefinierade gränssnitt för att interagera med andra moduler.

Principen om "svart låda" (inkapsling) tillåter oss att överväga strukturen för varje delsystem oberoende av andra delsystem. Modulen, som är en "svart låda", kan relativt fritt bytas ut. Problem kan bara uppstå vid kopplingen mellan olika moduler (eller en modul och en miljö).

Och denna interaktion måste beskrivas i den mest allmänna (abstrakta) formen, det vill säga i form av ett gränssnitt. I det här fallet kommer koden att fungera på samma sätt med alla implementeringar som överensstämmer med gränssnittskontraktet. Det är denna förmåga att arbeta med olika implementeringar (moduler eller objekt) genom ett enhetligt gränssnitt som kallas polymorfism.

Det är därför Servlet är ett gränssnitt : webbbehållaren vet inget om servlets, för det är några objekt som implementerar Servlet-gränssnittet och det är allt. Servlets vet också lite om behållarens struktur. Servlet-gränssnittet är det kontraktet, den standarden, den minsta interaktion som behövs för att få Java-webbapplikationer att ta över världen.

Polymorfism är inte alls att åsidosätta metoder, som man ibland felaktigt tror, ​​utan först och främst utbytbarheten av moduler / objekt med samma gränssnitt eller "ett gränssnitt, många implementeringar". För att implementera polymorfism behövs inte arvsmekanismen alls. Detta är viktigt att förstå eftersom arv i allmänhet bör undvikas när det är möjligt .

Tack vare gränssnitt och polymorfism uppnås just möjligheten att modifiera och utöka koden utan att ändra det som redan skrivits (Open-Closed Principle).

Så länge interaktionen mellan moduler enbart beskrivs i form av gränssnitt och inte är knuten till specifika implementeringar, har du möjlighet att helt "smärtfritt" för systemet ersätta en modul med vilken som helst annan som implementerar samma gränssnitt, samt lägga till en ny och därmed utöka funktionaliteten.

Det är som i LEGO-konstruktören – gränssnittet standardiserar interaktionen och fungerar som ett slags kontaktdon där vilken modul som helst med lämplig kontakt kan anslutas.

Designerns flexibilitet säkerställs av det faktum att vi helt enkelt kan ersätta en modul eller del med en annan med samma kontakter (med samma gränssnitt), samt lägga till så många nya delar som vi vill (samtidigt, befintliga delar inte ändras eller ändras på något sätt).

Gränssnitt låter dig bygga ett enklare system, beakta varje delsystem som en helhet och ignorera dess interna struktur. De tillåter moduler att interagera och samtidigt vet ingenting om den interna strukturen hos varandra, och implementerar därmed principen om minimal kunskap, som är grunden för lös koppling.

Ju mer generella/abstrakta gränssnitten är definierade och ju mindre restriktioner de lägger på interaktion, desto mer flexibelt är systemet. Härifrån följer faktiskt ytterligare en av principerna för SOLID - Interface Segregation Principle , som motsätter sig "tjocka gränssnitt".

Han säger att stora, skrymmande gränssnitt bör delas upp i mindre, mer specifika, så att klienter av små gränssnitt (beroende moduler) bara vet om de metoder de behöver arbeta med.

Denna princip är formulerad enligt följande: "Kunder bör inte vara beroende av metoder (vara medvetna om metoder) som de inte använder" eller "Många specialiserade gränssnitt är bättre än ett universellt".

Det visar sig att svag anslutning endast tillhandahålls när modulernas interaktion och beroenden beskrivs endast med hjälp av gränssnitt, det vill säga abstraktioner, utan att använda kunskap om deras interna struktur och struktur. Och i själva verket är inkapsling implementerad. Dessutom har vi möjlighet att utöka/ändra systemets beteende genom att lägga till och använda olika implementeringar, det vill säga på grund av polymorfism. Ja, vi kom igen till OOP - Encapsulation, Abstraction, Polymorphism.

8.3 Fasad: modulgränssnitt

Här kommer en erfaren programmerare att fråga: om designen inte är på nivån för objekt som själva implementerar motsvarande gränssnitt, utan på nivån av moduler, vad är då implementeringen av modulgränssnittet?

Svar: talar på språket för designmönster, då kan ett speciellt objekt ansvara för implementeringen av modulens gränssnitt - Fasad . Om du anropar metoder på ett objekt som innehåller Gateway-suffixet (till exempel MobileApiGateway), så är det troligen en fasad.

En fasad är ett gränssnittsobjekt som samlar en uppsättning operationer på hög nivå för att arbeta med ett visst delsystem, döljer dess interna struktur och sanna komplexitet bakom det . Ger skydd mot förändringar i delsystemets implementering. Fungerar som en enda ingång - "du sparkar fasaden, och han vet vem som behöver sparkas i detta delsystem för att få vad han behöver."

Du har precis blivit introducerad till ett av de viktigaste designmönstren som gör att du kan använda begreppet gränssnitt när du designar moduler och därigenom frikoppla dem - "Fasad".

Dessutom gör "Facade" det möjligt att arbeta med moduler på samma sätt som med vanliga objekt och tillämpa alla användbara principer och tekniker som används vid design av klasser vid design av moduler.

Fasad: modulgränssnitt

Obs : Även om de flesta programmerare förstår vikten av gränssnitt när de designar klasser (objekt), verkar det som att många upptäcker idén med att använda gränssnitt på modulnivå också.