Hierarkisk nedbrydning

Du bør aldrig begynde at skrive klasser til din ansøgning med det samme. Først skal det designes. Designet skal ende med en tankevækkende arkitektur. Og for at få denne arkitektur skal du konsekvent dekomponere systemet.

Dekomponering skal udføres hierarkisk - for det første er systemet opdelt i store funktionelle moduler / undersystemer, der beskriver dets drift i den mest generelle form. Derefter analyseres de resulterende moduler mere detaljeret og opdeles i undermoduler eller objekter.

Inden du vælger objekter, skal du opdele systemet i grundlæggende semantiske blokke, i det mindste mentalt. I små applikationer er dette normalt meget nemt at gøre: et par niveauer af hierarki er ganske nok, da systemet først er opdelt i undersystemer / pakker, og pakker er opdelt i klasser.

Hierarkisk nedbrydning

Denne idé er ikke så triviel, som den ser ud til. For eksempel, hvad er essensen af ​​et så almindeligt "arkitektonisk mønster" som Model-View-Controller (MVC)?

Det handler om at adskille præsentationen fra forretningslogikken . For det første er enhver brugerapplikation opdelt i to moduler - den ene er ansvarlig for at implementere selve forretningslogikken (Model), og den anden er ansvarlig for at interagere med brugeren (brugergrænseflade eller visning).

Så viser det sig, at modulerne på en eller anden måde skal interagere, til dette tilføjer de en Controller, hvis opgave er at styre samspillet mellem modulerne. Også i den mobile (klassiske) version af MVC er Observer-mønsteret tilføjet til det, så View kan modtage hændelser fra modellen og ændre de viste data i realtid.

Typiske topniveau moduler, opnået som et resultat af den første opdeling af systemet i de største komponenter, er netop:

  • Forretningslogik;
  • Brugergrænseflade;
  • Database;
  • Messaging system;
  • Objektbeholder.

Den første opdeling opdeler normalt hele applikationen i 2-7 (maks. 10 dele). Hvis vi deler det op i flere dele, så vil der være et ønske om at gruppere dem, og vi får igen 2-7 top-level moduler.

Funktionel nedbrydning

Opdelingen i moduler/delsystemer sker bedst ud fra de opgaver, som systemet løser . Hovedopgaven er opdelt i dens konstituerende delopgaver, som kan løses/udføres selvstændigt uafhængigt af hinanden.

Hvert modul bør være ansvarlig for at løse en delopgave og udføre dens tilsvarende funktion . Ud over det funktionelle formål er modulet også kendetegnet ved et sæt data, der er nødvendigt for, at det kan udføre sin funktion, dvs.

Modul = Funktion + Data nødvendige for at udføre det.

Hvis dekomponeringen til moduler er udført korrekt, så vil interaktionen med andre moduler (ansvarlige for andre funktioner) være minimal. Det kan det være, men dets fravær bør ikke være kritisk for dit modul.

Et modul er ikke et vilkårligt stykke kode, men en separat funktionelt meningsfuld og komplet programenhed (underprogram), der giver en løsning på en bestemt opgave og ideelt set kan arbejde selvstændigt eller i et andet miljø og genbruges. Modulet skal være en slags "integritet, der er i stand til relativ uafhængighed i adfærd og udvikling." (Christopher Alexander)

Derfor er kompetent nedbrydning først og fremmest baseret på analysen af ​​systemfunktionerne og de data, der er nødvendige for at udføre disse funktioner. Funktioner i dette tilfælde er ikke klassefunktioner og moduler, fordi de ikke er objekter. Hvis du kun har et par klasser i et modul, så har du overdrevet det.

Stærk og svag forbindelse

Det er meget vigtigt ikke at overdrive det med modularisering. Hvis du giver en nybegynder en monolitisk Spring-applikation og beder ham om at dele den op i moduler, vil han tage hver Spring Bean ud i et separat modul og overveje, at hans arbejde er færdigt. Men det er det ikke.

Hovedkriteriet for kvaliteten af ​​nedbrydningen er, hvordan modulerne er fokuserede på at løse deres opgaver og er uafhængige.

Dette er normalt formuleret som følger: "Modulerne opnået som et resultat af nedbrydning bør være maksimalt konjugeret internt (høj intern kohæsion) og minimalt forbundet med hinanden (lav ekstern kobling)."

Høj sammenhængskraft, høj sammenhængskraft eller "sammenhæng" inden for modulet, indikerer, at modulet er fokuseret på at løse ét snævert problem og ikke er engageret i at udføre heterogene funktioner eller uafhængige ansvarsområder.

Samhørighed kendetegner, i hvilken grad de opgaver, modulet udfører, er relateret til hinanden.

En konsekvens af High Cohesion er Single Responsibility Principle - det første af de fem SOLID principper , ifølge hvilket ethvert objekt/modul kun skal have ét ansvar, og der ikke bør være mere end én grund til at ændre det.

Low Coupling , løs kobling, betyder, at de moduler, som systemet er opdelt i, om muligt skal være uafhængige eller løst koblede til hinanden. De skal kunne interagere, men samtidig vide så lidt som muligt om hinanden.

Hvert modul behøver ikke at vide, hvordan det andet modul fungerer, hvilket sprog det er skrevet på, og hvordan det fungerer. For at organisere interaktionen mellem sådanne moduler bruges ofte en bestemt beholder, som disse moduler indlæses i.

Med korrekt design, hvis du ændrer et modul, behøver du ikke at redigere andre, eller disse ændringer vil være minimale. Jo løsere koblingen er, jo lettere er det at skrive/forstå/forlænge/reparere programmet.

Det menes, at veldesignede moduler skal have følgende egenskaber:

  • Funktionel integritet og fuldstændighed - hvert modul implementerer en funktion, men implementerer den godt og fuldstændigt, modulet udfører selvstændigt et komplet sæt operationer for at implementere dets funktion.
  • Et input og et output - ved input modtager programmodulet et bestemt sæt indledende data, udfører meningsfuld behandling og returnerer et sæt resultatdata, det vil sige, at standard IPO-princippet er implementeret - input -\u003e proces -\u003e produktion.
  • Logisk uafhængighed - resultatet af programmodulets arbejde afhænger kun af de indledende data, men afhænger ikke af arbejdet i andre moduler.
  • Svage informationsforbindelser med andre moduler - udvekslingen af ​​information mellem moduler bør minimeres, hvis det er muligt.

Det er meget svært for en nybegynder at forstå, hvordan man kan reducere tilslutningen af ​​moduler endnu mere. Dels kommer denne viden med erfaring, dels - efter at have læst smarte bøger. Men det er bedst at analysere arkitekturen af ​​eksisterende applikationer.

Sammensætning i stedet for arv

Kompetent nedbrydning er en slags kunst og en vanskelig opgave for de fleste programmører. Enkelhed er vildledende her, og fejl er dyre.

Det sker, at dedikerede moduler er stærkt koblet med hinanden og ikke kan udvikles uafhængigt. Eller det er ikke klart, hvilken funktion hver af dem er ansvarlig for. Hvis du støder på et lignende problem, er opdelingen i moduler højst sandsynligt udført forkert.

Det skal altid være klart, hvilken rolle hvert modul spiller . Det mest pålidelige kriterium for, at nedbrydningen er udført korrekt, er, hvis modulerne er uafhængige og værdifulde underrutiner, der kan bruges isoleret fra resten af ​​applikationen (og derfor kan genbruges).

Ved nedbrydning af et system er det ønskeligt at kontrollere dets kvalitet ved at stille dig selv spørgsmålene: "Hvilken opgave udfører hvert modul?", "Hvor nemme er modulerne at teste?", "Er det muligt at bruge modulerne alene" eller i et andet miljø?" påvirke andre?"

Du skal prøve at holde modulerne så autonome som muligt . Som nævnt før er dette en nøgleparameter for korrekt nedbrydning . Derfor skal det udføres på en sådan måde, at modulerne i starten er svagt afhængige af hinanden. Hvis du lykkedes, så er du fantastisk.

Hvis ikke, så er alt heller ikke tabt her. Der er en række specielle teknikker og mønstre, som giver dig mulighed for yderligere at minimere og svække forbindelserne mellem undersystemer. For eksempel, i tilfælde af MVC, blev Observer-mønsteret brugt til dette formål, men andre løsninger er mulige.

Det kan siges, at teknikker til afkobling udgør hoved "arkitektens værktøjskasse". Det er kun nødvendigt at forstå, at vi taler om alle undersystemer, og det er nødvendigt at svække forbindelsen på alle niveauer i hierarkiet , det vil sige ikke kun mellem klasser, men også mellem moduler på hvert hierarkisk niveau.