Programmering

Introduksjon til designmønstre, del 1: Designmønsterhistorie og klassifisering

Det er utarbeidet en rekke strategier for å forenkle og redusere kostnadene ved utforming av programvare, spesielt innen vedlikehold. Lære å identifisere og jobbe med gjenbrukbare programvarekomponenter (noen ganger referert til som programvareintegrerte kretser) er en strategi. Å bruke designmønstre er en annen.

Denne artikkelen lanserer en tredelt serie om designmønstre. I denne delen introduserer jeg det konseptuelle rammeverket for designmønstre og går gjennom en demonstrasjon av evaluering av et designmønster for en bestemt brukssak. Jeg vil også diskutere historien om designmønstre og antimønstre. Til slutt vil jeg klassifisere og oppsummere de mest brukte programvaredesignmønstrene som har blitt oppdaget og dokumentert gjennom de siste par tiårene.

Hva er et designmønster?

Å designe gjenbrukbar objektorientert programvare som modellerer et eksisterende system er virkelig utfordrende. En programvareutvikler må faktorisere systemets enheter i klasser hvis offentlige grensesnitt ikke er for kompliserte, etablere forhold mellom klasser, avsløre arvshierarkier og mer. Fordi den fleste programvare forblir i bruk lenge etter at den ble skrevet, må programvareutviklere også ta opp gjeldende applikasjonskrav mens de holder koden og infrastrukturen fleksibel nok til å møte fremtidige behov.

Erfarne objektorienterte utviklere har oppdaget at mønster for programvaredesign letter koding av stabile og robuste programvaresystemer. Å bruke disse designmønstrene i stedet for å stadig utvikle nye løsninger fra bunnen av er effektivt, og det reduserer noe av risikoen for feil. Hvert designmønster identifiserer et tilbakevendende designproblem i en spesifikk applikasjonskontekst, og tilbyr deretter en generalisert, gjenbrukbar løsning som kan brukes i forskjellige applikasjonsscenarier.

"EN design mønster beskriver klassene og samhandlende objektene som brukes til å løse et generelt designproblem i en bestemt sammenheng. "

Noen utviklere definerer en design mønster som en klassekodet enhet (for eksempel en koblet liste eller bitvektor), mens andre sier at et designmønster er i hele applikasjonen eller delsystemet. Mitt syn er at en design mønster beskriver klassene og samhandlende objektene som brukes til å løse et generelt designproblem i en bestemt sammenheng. Mer formelt er et designmønster spesifisert som en beskrivelse som består av fire grunnleggende elementer:

  1. EN Navn som beskriver designmønsteret og gir oss et ordforråd for å diskutere det
  2. EN problem som identifiserer designproblemet som må løses sammen med konteksten som problemet oppstår i
  3. EN løsning til problemet, som (i en kontekst av programvaredesignmønster) skal identifisere klassene og objektene som bidrar til designet sammen med deres forhold og andre faktorer
  4. En forklaring på konsekvenser av å bruke designmønsteret

For å identifisere riktig designmønster du skal bruke, må du først tydelig identifisere problemet du prøver å løse; det er der problem element i designmønsterbeskrivelsen er nyttig. Å velge ett designmønster fremfor et annet innebærer vanligvis avveininger som kan påvirke applikasjonens eller systemets fleksibilitet og fremtidige vedlikehold. Derfor er det viktig å forstå konsekvenser å bruke et gitt designmønster før du begynner å implementere det.

Evaluering av et designmønster

Vurder oppgaven med å designe et komplekst brukergrensesnitt ved hjelp av knapper, tekstfelt og andre komponenter som ikke er containere. Komposittdesignmønsteret ser på containere som komponenter, som lar oss hekke containere og deres komponenter (containere og ikke-containere) i andre containere, og gjøre det rekursivt. Hvis vi valgte å ikke bruke komposittmønsteret, måtte vi lage mange spesialiserte ikke-container-komponenter (en enkelt komponent som for eksempel kombinerer et passordtekstfelt og en påloggingsknapp), noe som er vanskeligere å oppnå.

Etter å ha tenkt gjennom dette forstår vi problemet vi prøver å løse, og løsningen som tilbys av det sammensatte mønsteret. Men hva er konsekvensene av å bruke dette mønsteret?

Å bruke kompositt betyr at klassehierarkiene dine vil blande beholder- og ikke-beholderkomponenter. Enklere kunder vil behandle container- og ikke-container-komponenter jevnt. Og det blir lettere å introdusere nye typer komponenter i brukergrensesnittet. Kompositt kan også føre til altfor generalisert design, noe som gjør det vanskeligere å begrense hvilke typer komponenter som kan legges til en container. Siden du ikke vil kunne stole på kompilatoren for å håndheve typebegrensninger, må du bruke kjøretidsjekk.

Hva er galt med kjøretidsjekk?

Runtime type kontroller involverer hvis uttalelser og tilfelle av operatør, noe som fører til sprø kode. Hvis du glemmer å oppdatere en kjøretidsjekk etter hvert som applikasjonskravene dine utvikler seg, kan du senere introdusere feil.

Det er også mulig å velge et passende designmønster og bruke det feil. De Dobbeltkontrollert låsing mønster er et klassisk eksempel. Dobbeltsjekket låsing reduserer låseanskaffelsesomkostninger ved først å teste et låsekriterium uten å faktisk skaffe seg låsen, og deretter bare anskaffe låsen hvis sjekken indikerer at låsing er nødvendig. Mens det så bra ut på papir, hadde dobbeltsjekket låsing i JDK 1.4 noen skjulte kompleksiteter. Da JDK 5 utvidet semantikken til flyktige nøkkelord, utviklere var endelig i stand til å høste fordelene av det dobbeltkontrollerte låsemønsteret.

Mer om dobbeltsjekket låsing

Se "Dobbeltsjekket låsing: Smart, men ødelagt" og "Kan dobbeltsjekket låsing løses?" (Brian Goetz, JavaWorld) for å lære mer om hvorfor dette mønsteret ikke fungerte i JDK 1.4 og tidligere. For mer om spesifisering av DCL i JDK 5 og senere, se "The 'Double-Checked Locking is Broken' Declaration" (University of Maryland Department of Computer Science, David Bacon, et al.).

Antimønstre

Når et designmønster ofte brukes, men er ineffektivt og / eller kontraproduktivt, er designmønsteret kjent som et anti-mønster. Man kan hevde at dobbeltkontrollert låsing som brukt i JDK 1.4 og tidligere var et antimønster. Jeg vil si at det bare var en dårlig idé i den sammenheng. For at en dårlig ide skal utvikle seg til et antimønster, må følgende betingelser være oppfylt (se Ressurser).

  • Et gjentatt handlingsmønster, prosess eller struktur som i utgangspunktet ser ut til å være gunstig, men til slutt gir mer dårlige konsekvenser enn gunstige resultater.
  • Det finnes en alternativ løsning som er tydelig dokumentert, bevist i praksis og repeterbar.

Mens dobbeltkontrollert låsing i JDK 1.4 oppfylte det første kravet til et antimønster, oppfylte det ikke det andre: selv om du kunne bruke synkronisert for å løse problemet med lat initialisering i et flertrådet miljø, ville det ha beseiret årsaken til å bruke dobbeltkontrollert låsing i utgangspunktet.

Lås opp antimønstre

Å anerkjenne antimønstre er en forutsetning for å unngå dem. Les Obi Ezechukwus tredelte serie for en introduksjon til tre antimønstre som er kjent for å forårsake blindgang:

  • Ingen voldgift
  • Aggregat av arbeidere
  • Inkrementell låsing

Design mønster historie

Designmønstre dateres tilbake til slutten av 1970-tallet med utgivelsen av Et mønsterspråk: Byer, bygninger, konstruksjon av arkitekt Christopher Alexander og noen få andre. Denne boka introduserte designmønstre i en arkitektonisk sammenheng, og presenterte 253 mønstre som samlet dannet det forfatterne kalte en mønster språk.

Ironien i designmønstre

Selv om designmønstre som brukes til programvaredesign sporer begynnelsen til Et mønsterspråk, ble dette arkitektoniske arbeidet påvirket av det fremvoksende språket for å beskrive dataprogrammering og design.

Begrepet mønsterspråk dukket opp senere i Donald Normans og Stephen Drapers Brukersentrert systemdesign, som ble utgitt i 1986. Denne boken foreslo anvendelse av mønsterspråk på interaksjonsdesign, som er praksis for å designe interaktive digitale produkter, miljøer, systemer og tjenester for menneskelig bruk.

I mellomtiden hadde Kent Beck og Ward Cunningham begynt å studere mønstre og deres anvendelse på programvaredesign. I 1987 brukte de en serie designmønstre for å hjelpe Tektronixs Semiconductor Test Systems Group, som hadde problemer med å fullføre et designprosjekt. Beck og Cunningham fulgte Alexanders råd for brukersentrert design (la representanter for prosjektets brukere bestemme designresultatet), samtidig som de ga dem noen designmønstre for å gjøre jobben enklere.

Erich Gamma innså også viktigheten av gjentatte designmønstre mens han jobbet med doktorgradsavhandlingen. Han mente at designmønstre kunne lette oppgaven med å skrive gjenbrukbar objektorientert programvare, og tenkte på hvordan man kunne dokumentere og kommunisere dem effektivt. Før den europeiske konferansen om objektorientert programmering i 1991 begynte Gamma og Richard Helm å katalogisere mønstre.

På en OOPSLA-workshop holdt i 1991 fikk Gamma og Helm selskap av Ralph Johnson og John Vlissides. Dette Gang of Four (GoF), som de senere ble kjent, fortsatte med å skrive den populære Designmønstre: Elementer av gjenbrukbar objektorientert programvare, som dokumenterer 23 designmønstre i tre kategorier.

Den moderne utviklingen av designmønstre

Designmønstre har fortsatt å utvikle seg siden den originale GoF-boka, spesielt ettersom programvareutviklere har konfrontert nye utfordringer knyttet til endrede maskinvare- og applikasjonskrav.

I 1994 ble en amerikansk-basert ideell organisasjon kjent som Hillside Group innviet Mønsterspråk for programmer, en gruppe årlige konferanser som har som mål å utvikle og foredle kunsten med designmønstre for programvare. Disse pågående konferansene har gitt mange eksempler på domenespesifikke designmønstre. For eksempel designmønstre i samtidig sammenheng.

Christopher Alexander ved OOPSLA

OOPSLA 96s hovedtale ble holdt av arkitekten Christopher Alexander, og Alexander reflekterte over arbeidet hans og hvordan det objektorienterte programmeringssamfunnet hadde truffet og savnet merket i å vedta og tilpasse ideene sine om mønster språk til programvare. Du kan lese Alexanders adresse i sin helhet: "The Origins of Pattern Theory: The Future of theory, And The Generation of a Living World."

I 1998 slapp Mark Grand Mønstre i Java. Denne boka inkluderte designmønstre som ikke ble funnet i GoF-boka, inkludert samtidige mønstre. Grand brukte også Unified Modeling Language (UML) for å beskrive designmønstre og deres løsninger. Bokens eksempler ble uttrykt og beskrevet på Java-språket.

Programvare design mønstre etter klassifisering

Moderne programvaredesignmønstre er i stor grad klassifisert i fire kategorier basert på deres bruk: skapelses-, strukturell, atferdsmessig og samtidig. Jeg vil diskutere hver kategori og deretter liste opp og beskrive noen av de fremtredende mønstrene for hver enkelt.

Andre typer designmønstre

Hvis du tenker at det er flere typer mønstre, har du rett. En senere artikkel i denne serien vil diskutere flere designmønstertyper: interaksjon, arkitektoniske, organisatoriske og kommunikasjons- / presentasjonsmønstre.

Skapelsesmønstre

EN skapelsesmønster abstraherer prosessen med instantiering, skiller hvordan objekter blir opprettet, komponert og representert fra koden som er avhengig av dem. Klasseskapningsmønstre bruke arv til å variere klassene som er instantiert, og objekt skapelsesmønstre delegere instantiering til andre objekter.

  • Abstrakt fabrikk: Dette mønsteret gir et grensesnitt for å kapsle inn en gruppe individuelle fabrikker som har et felles tema uten å spesifisere deres konkrete klasser.
  • Bygger: Skiller konstruksjonen av et komplekst objekt fra dets representasjon, slik at den samme konstruksjonsprosessen kan skape forskjellige representasjoner. Å abstrakte trinnene til objektkonstruksjon tillater forskjellige implementeringer av trinnene å konstruere forskjellige representasjoner av objektene.
  • Fabrikkmetode: Definerer et grensesnitt for å lage et objekt, men lar underklasser bestemme hvilken klasse som skal instansieres. Dette mønsteret lar en klasse utsette instantiering til underklasser. Avhengighetsinjeksjon er et relatert mønster. (Se ressurser.)
  • Lat initialisering: Dette mønsteret gir oss en måte å forsinke oppretting av objekter, databaseoppslag eller en annen kostbar prosess til første gang resultatet er nødvendig.
  • Multiton: Utvider singleton-konseptet for å administrere et kart over navngitte klasseinstanser som nøkkelverdipar, og gir et globalt tilgangspunkt til dem.
  • Objektbasseng: Hold et sett med initialiserte objekter klare til bruk, i stedet for å bli tildelt og ødelagt på forespørsel. Hensikten er å unngå kostbar ressursinnsamling og gjenvinning ved å gjenvinne objekter som ikke lenger er i bruk.
  • Prototype: Spesifiserer hvilke objekter som skal opprettes ved hjelp av en prototypisk forekomst, og deretter oppretter du nye objekter ved å kopiere denne prototypen. Den prototypiske forekomsten er klonet for å generere nye objekter.
  • Ressursinnhenting er initialisering: Dette mønsteret sikrer at ressursene automatisk og riktig initialiseres og gjenvinnes ved å knytte dem til levetiden til egnede objekter. Ressurser anskaffes under initialisering av objektet, når det ikke er noen sjanse for at de blir brukt før de er tilgjengelige, og frigjøres med ødeleggelse av de samme objektene, noe som garantert vil finne sted selv i tilfelle feil.
  • Singleton: Sikrer at en klasse bare har en forekomst og gir et globalt tilgangspunkt til denne forekomsten.
$config[zx-auto] not found$config[zx-overlay] not found