Hierarkisk nedbrytning
Du bör aldrig börja skriva klasser för din ansökan direkt. Först måste den designas. Designen ska avslutas med en genomtänkt arkitektur. Och för att få den här arkitekturen måste du konsekvent bryta ner systemet.
Nedbrytning måste utföras hierarkiskt - först är systemet uppdelat i stora funktionella moduler / delsystem som beskriver dess funktion i den mest allmänna formen. Därefter analyseras de resulterande modulerna mer i detalj och delas upp i undermoduler eller objekt.
Innan du väljer objekt, dela upp systemet i grundläggande semantiska block, åtminstone mentalt. I små applikationer är detta vanligtvis väldigt enkelt att göra: ett par nivåer av hierarki är tillräckligt, eftersom systemet först delas in i delsystem / paket och paket är indelade i klasser.
![Hierarkisk nedbrytning](https://cdn.codegym.cc/images/article/d5e9aa2b-5783-430f-b5b5-1832bc33c540/800.jpeg)
Denna idé är inte så trivial som den verkar. Till exempel, vad är kärnan i ett så vanligt "arkitektoniskt mönster" som Model-View-Controller (MVC)?
Det handlar om att skilja presentationen från affärslogiken . För det första är vilken användarapplikation som helst uppdelad i två moduler - en är ansvarig för att implementera själva affärslogiken (Model), och den andra är ansvarig för att interagera med användaren (användargränssnitt eller vy).
Sedan visar det sig att modulerna på något sätt måste interagera, för detta lägger de till en Controller, vars uppgift är att hantera interaktionen mellan modulerna. Även i den mobila (klassiska) versionen av MVC läggs Observer-mönstret till det så att vyn kan ta emot händelser från modellen och ändra den visade data i realtid.
Typiska toppnivåmoduler, erhållna som ett resultat av den första uppdelningen av systemet i de största komponenterna, är exakt:
- Företagslogik;
- Användargränssnitt;
- Databas;
- Meddelandesystem;
- Objektbehållare.
Den första uppdelningen delar vanligtvis upp hela applikationen i 2-7 (max 10 delar). Om vi bryter ner det i fler delar, kommer det att finnas en önskan att gruppera dem, och vi kommer återigen att få 2-7 toppnivåmoduler.
Funktionell nedbrytning
Uppdelningen i moduler/delsystem görs bäst utifrån de uppgifter som systemet löser . Huvuduppgiften är uppdelad i sina ingående deluppgifter, som kan lösas/utföras autonomt, oberoende av varandra.
Varje modul bör ansvara för att lösa någon deluppgift och utföra dess motsvarande funktion . Förutom det funktionella syftet kännetecknas modulen också av en uppsättning data som är nödvändig för att den ska kunna utföra sin funktion, det vill säga:
Modul = Funktion + Data som behövs för att köra den.
Om sönderdelningen till moduler görs korrekt, blir interaktionen med andra moduler (ansvariga för andra funktioner) minimal. Det kan vara det, men frånvaron bör inte vara avgörande för din modul.
En modul är inte en godtycklig kod, utan en separat funktionellt meningsfull och komplett programenhet (underprogram) som ger en lösning på en viss uppgift och som helst kan arbeta självständigt eller i en annan miljö och återanvändas. Modulen ska vara ett slags "integritet som kan vara relativt oberoende i beteende och utveckling." (Christopher Alexander)
Sålunda baseras kompetent sönderdelning först och främst på analysen av systemfunktionerna och de data som är nödvändiga för att utföra dessa funktioner. Funktioner i det här fallet är inte klassfunktioner och moduler, eftersom de inte är objekt. Om du bara har ett par klasser i en modul, har du överdrivit det.
Stark och svag anslutning
Det är mycket viktigt att inte överdriva det med modularisering. Om du ger en nybörjare en monolitisk Spring-applikation och ber honom dela upp den i moduler, kommer han att ta ut varje Spring Bean i en separat modul och anser att hans arbete är avslutat. Men det är inte.
Huvudkriteriet för kvaliteten på nedbrytningen är hur modulerna är fokuserade på att lösa sina uppgifter och är oberoende.
Detta är vanligtvis formulerat enligt följande: "Modulerna som erhålls som ett resultat av sönderdelning bör vara maximalt konjugerade internt (hög intern kohesion) och minimalt sammankopplade med varandra (låg extern koppling)."
Hög sammanhållning, hög sammanhållning eller "sammanhållning" inom modulen, indikerar att modulen är fokuserad på att lösa ett smalt problem och inte är engagerad i att utföra heterogena funktioner eller orelaterade ansvarsområden.
Sammanhållning kännetecknar i vilken grad de uppgifter som utförs av modulen är relaterade till varandra.
En konsekvens av High Cohesion är Single Responsibility Principle - den första av de fem SOLID-principerna , enligt vilka alla objekt/moduler endast bör ha ett ansvar och det inte bör finnas mer än ett skäl för att ändra det.
Low Coupling , lös koppling, innebär att modulerna som systemet är uppdelat i ska vara om möjligt oberoende eller löst kopplade till varandra. De ska kunna interagera, men samtidigt veta så lite som möjligt om varandra.
Varje modul behöver inte veta hur den andra modulen fungerar, vilket språk den är skriven på och hur den fungerar. Ofta, för att organisera interaktionen mellan sådana moduler, används en viss behållare, i vilken dessa moduler laddas.
Med rätt design, om du ändrar en modul, behöver du inte redigera andra, eller dessa ändringar kommer att vara minimala. Ju lösare kopplingen är desto lättare är det att skriva/förstå/förlänga/reparera programmet.
Man tror att väldesignade moduler bör ha följande egenskaper:
- Funktionell integritet och fullständighet - varje modul implementerar en funktion, men implementerar den väl och fullständigt, modulen utför självständigt en fullständig uppsättning operationer för att implementera sin funktion.
- En ingång och en utgång - vid ingången tar programmodulen emot en viss uppsättning initiala data, utför meningsfull bearbetning och returnerar en uppsättning resultatdata, det vill säga standard IPO-principen implementeras - input -\u003e process -\u003e produktion.
- Logiskt oberoende - resultatet av programmodulens arbete beror endast på de initiala uppgifterna, men beror inte på andra modulers arbete.
- Svaga informationslänkar till andra moduler - informationsutbytet mellan moduler bör om möjligt minimeras.
Det är mycket svårt för en nybörjare att förstå hur man kan minska anslutningen av moduler ännu mer. Dels kommer denna kunskap med erfarenhet, dels – efter att ha läst smarta böcker. Men det är bäst att analysera arkitekturerna för befintliga applikationer.
Sammansättning istället för arv
Kompetent nedbrytning är en sorts konst och en svår uppgift för de flesta programmerare. Enkelhet är vilseledande här, och misstag är kostsamma.
![](https://cdn.codegym.cc/images/article/d7e785ed-e16d-4354-883c-0ad83327512a/512.jpeg)
Det händer att dedikerade moduler är starkt kopplade till varandra och inte kan utvecklas oberoende. Eller så är det inte klart vilken funktion var och en av dem ansvarar för. Om du stöter på ett liknande problem, är det troligt att partitioneringen i moduler gjordes felaktigt.
Det ska alltid vara tydligt vilken roll varje modul spelar . Det mest tillförlitliga kriteriet att nedbrytningen görs korrekt är om modulerna är oberoende och värdefulla subrutiner som kan användas isolerat från resten av applikationen (och därför kan återanvändas).
Vid nedbrytning av ett system är det önskvärt att kontrollera dess kvalitet genom att ställa dig själv frågorna: "Vilken uppgift utför varje modul?", "Hur lätta är modulerna att testa?", "Är det möjligt att använda modulerna på egen hand." eller i en annan miljö?" påverka andra?"
Du måste försöka hålla modulerna så autonoma som möjligt . Som nämnts tidigare är detta en nyckelparameter för korrekt nedbrytning . Därför måste det utföras på ett sådant sätt att modulerna initialt är svagt beroende av varandra. Om du lyckades, då är du fantastisk.
Om inte, så är inte allt förlorat här heller. Det finns ett antal speciella tekniker och mönster som gör att du ytterligare kan minimera och försvaga länkarna mellan delsystem. Till exempel, i fallet med MVC, användes Observer-mönstret för detta ändamål, men andra lösningar är möjliga.
Man kan säga att tekniker för frikoppling utgör den huvudsakliga "arkitektens verktygslåda". Det är bara nödvändigt att förstå att vi talar om alla delsystem och det är nödvändigt att försvaga kopplingen på alla nivåer i hierarkin , det vill säga inte bara mellan klasser, utan också mellan moduler på varje hierarkisk nivå.