designmönster är designnivålösningar för återkommande problem som vi mjukvaruingenjörer stöter på ofta. Det är inte kod, jag upprepar. Det är som en beskrivning av hur man hanterar dessa problem och utformar en lösning.
användning av dessa mönster anses vara god praxis, eftersom lösningen är ganska beprövad och testad, vilket resulterar i högre läsbarhet för den slutliga koden., Designmönster skapas ganska ofta för och används av OOP-språk, som Java, där de flesta exemplen härifrån kommer att skrivas.
typer av designmönster
det finns cirka 26 mönster som för närvarande upptäcks(jag tror knappast att jag kommer att göra dem alla…).
dessa 26 kan klassificeras i 3 typer:
1. Creational: dessa mönster är utformade för klass instantiation. De kan vara antingen klassskapande mönster eller objektkreationsmönster.
2. Struktur: dessa mönster är utformade med avseende på en klasss struktur och sammansättning., Huvudsyftet med de flesta av dessa mönster är att öka funktionaliteten hos den(de) involverade klassen (er) utan att ändra mycket av dess sammansättning.
3. Beteende: dessa mönster är utformade beroende på hur en klass kommunicerar med andra.
i det här inlägget kommer vi att gå igenom ett grundläggande designmönster för varje klassificerad typ.
typ 1: Creational – Singleton designmönstret
Singleton designmönstret är ett Kreationsmönster, vars mål är att skapa endast en instans av en klass och att tillhandahålla endast en global åtkomstpunkt till det objektet., Ett vanligt exempel på en sådan klass i Java är kalender, där du inte kan göra en instans av den klassen. Den använder också sin egen getInstance()
– metod för att få objektet att användas.
en klass som använder singleton designmönstret kommer att inkludera,
- en privat statisk variabel som håller klassens enda instans.
- en privat konstruktör, så det kan inte instansieras någon annanstans.
- en offentlig statisk metod för att returnera klassens enda instans.,
det finns många olika implementeringar av singleton design. Idag kommer jag att gå igenom implementeringarna av;
1. Ivrig Instantiation
2. Lat Instansiering
3. Thread-safe Instantiation
Eager Beaver
public class EagerSingleton {// create an instance of the class.private static EagerSingleton instance = new EagerSingleton();// private constructor, so it cannot be instantiated outside this class.private EagerSingleton() { }// get the only instance of the object created.public static EagerSingleton getInstance() {return instance;}}
denna typ av instantiation sker under klassbelastning, eftersom instantiationen av variabelinstansen sker utanför vilken metod som helst. Detta utgör en rejäl nackdel om denna klass inte används alls av klientprogrammet. Beredskapsplanen, om den här klassen inte används, är den Lat Instansieringen.,
lata dagar
det är inte mycket skillnad från ovanstående implementering. Huvudskillnaderna är att den statiska variabeln ursprungligen deklareras null och endast instansieras inom getInstance()
– metoden om – och endast om-instansvariabeln förblir null vid tidpunkten för kontrollen.
det här åtgärdar ett problem, men en annan finns fortfarande. Tänk om två olika klienter får tillgång till Singleton-klassen samtidigt, ända till millisekund?, Tja, de kommer att kontrollera om instansen är null på samma gång, och kommer att finna det sant, och så kommer att skapa två instanser av klassen för varje begäran av de två klienterna. För att åtgärda detta, tråd säker instantiation ska genomföras.
(tråd) säkerhet är nyckel
i Java används sökordet synkroniserat på metoder eller objekt för att implementera trådsäkerhet, så att endast en tråd kommer åt en viss resurs på en gång. Klassen instantiation sätts i ett synkroniserat block så att metoden endast kan nås av en klient vid en given tidpunkt.,
overhead för den synkroniserade metoden är hög och minskar prestanda för hela operationen.
till exempel, om instansvariabeln redan har instansierats, körs getInstance()
– metoden, synchronized
– metoden och prestandan sjunker. Detta händer bara för att kontrollera om värdet instance
variabler är null. Om den finner att det är, lämnar den metoden.
för att minska denna overhead används dubbel låsning., Kontrollen används också före metodensynchronized
, och om värdet är null ensam, körs metodensynchronized
.
nu till nästa klassificering.
Type 2: Structural-The Decorator Design Pattern
Jag ska ge dig ett litet scenario för att ge ett bättre sammanhang till varför och var du ska använda Dekoratörsmönstret.
säg att du äger ett kafé, och som alla nybörjare börjar du med bara två typer av vanligt kaffe, husblandningen och mörksteken., I ditt faktureringssystem fanns det en klass för de olika kaffeblandningarna, som ärver dryckets abstrakta klass. Människor faktiskt börjar komma förbi och har din underbara (om än bitter?) Kaffe. Sedan finns det kaffe newbs som, Gud förbjude, vill ha socker eller mjölk. En sådan travesti för kaffe!! ??
nu måste du också ha dessa två tillägg, både till menyn och tyvärr på faktureringssystemet. Ursprungligen kommer din IT-person att göra en underklass för båda kaffe, en inklusive socker, den andra mjölken., Sedan, eftersom kunderna alltid har rätt, säger man dessa fruktade ord:
”kan jag få ett mjölkkaffe, med socker, tack?”
???
Det går ditt faktureringssystem skrattar i ansiktet igen. Tja, tillbaka till ritbordet….
IT-personen lägger sedan till mjölkkaffe med socker som en annan underklass till varje moderkaffeklass. Resten av månaden är smidig segling, folk köar upp för att ha ditt kaffe, du faktiskt tjäna pengar. ??
men vänta, det finns mer!
världen är emot dig igen., En konkurrent öppnar upp över gatan, med inte bara 4 typer av kaffe, men mer än 10 tillägg samt! ?
du köper alla dessa och mer, för att sälja bättre kaffe själv, och kom bara ihåg att du glömde att uppdatera det dragna faktureringssystemet. Du kan absolut inte göra det oändliga antalet underklasser för alla kombinationer av alla tillägg, med de nya kaffeblandningarna också. För att inte tala om storleken på det slutliga systemet.??
tid att faktiskt investera i ett korrekt faktureringssystem., Du hittar ny IT-Personal, som faktiskt vet vad de gör och de säger;
”varför, det blir så mycket enklare och mindre om det använde dekoratörsmönstret.”
vad i hela friden är det?
designmönstret för dekoratören faller i den strukturella kategorin, som behandlar den faktiska strukturen hos en klass, oavsett om den är genom arv, sammansättning eller båda. Målet med denna design är att ändra en objekts funktionalitet vid körning. Detta är en av de många andra designmönster som använder abstrakta klasser och gränssnitt med sammansättning för att få sitt önskade resultat.,
Låt oss ge Matte en chans (ryser?) för att få allt detta i perspektiv;
ta 4 kaffeblandningar och 10 tillägg. Om vi fastnat för generering av underklasser för varje annan kombination av alla tillägg för en typ av kaffe. Det är;
(10-1)2 = 92 = 81 underklasser
vi subtraherar 1 från 10, eftersom du inte kan kombinera ett tillägg med en annan av samma typ, socker med socker låter dumt. Och det är bara en kaffeblandning. Multiplicera det 81 med 4 och du får en jättestor 324 olika underklasser!, Prata om allt som kodar …
men med dekoratörsmönstret kräver endast 16 klasser i detta scenario. Vill du slå vad?
om vi kartlägger vårt scenario enligt klassen diagram ovan får vi 4 klasser för 4 kaffeblandningar, 10 för varje tillägg och 1 för den abstrakta komponenten och 1 mer för den abstrakta dekoratorn. Se! 16!, Ge mig 100 dollar.?? (jk, men det kommer inte att vägras om det ges… bara säger)
som du kan se ovanifrån, precis som de konkreta kaffeblandningarna är underklasser av dryckets abstrakta klass, ärver addon abstract-klassen också sina metoder från den. Tilläggen, som är dess underklasser, ärver i sin tur några nya metoder för att lägga till funktionalitet till basobjektet när det behövs.
låt oss komma till kodning, för att se detta mönster i bruk.,
först för att göra den abstrakta dryckesklassen, att alla olika kaffeblandningar kommer att ärva från:
sedan för att lägga till både de konkreta kaffeblandningsklasserna.
addon abstract-klassen ärver också från dryckes abstract-klassen (mer om detta nedan).
och nu de konkreta implementeringarna av denna abstrakta klass:
som du kan se ovan kan vi skicka någon underklass av dryck till någon underklass av AddOn, och få den extra kostnaden samt den uppdaterade beskrivningen. Och eftersom AddOn-klassen i huvudsak är av Typdryck, kan vi skicka en AddOn till en annan AddOn., På så sätt kan vi lägga till valfritt antal tillägg till en viss kaffeblandning.
nu för att skriva någon kod för att testa detta.
slutresultatet är:
det fungerar! Vi kunde lägga till mer än ett tillägg till en kaffeblandning och framgångsrikt uppdatera sin slutliga kostnad och beskrivning, utan att behöva göra oändliga underklasser för varje tilläggskombination för alla kaffeblandningar.
slutligen, till den sista kategorin.,
typ 3: Behavioral – Kommandodesignmönstret
ett beteendemönster fokuserar på hur klasser och objekt kommunicerar med varandra. Huvudfokus för kommandomönstret är att inkulcera en högre grad av lös koppling mellan berörda parter (läs: klasser).
Uhhhh… Vad är det?
koppling är det sätt som två (eller flera) klasser som interagerar med varandra, ja, interagerar. Det ideala scenariot när dessa klasser interagerar är att de inte är starkt beroende av varandra. Det är lös koppling., Så, en bättre definition för lös koppling skulle vara, klasser som är sammankopplade, vilket gör minsta användning av varandra.
behovet av detta mönster uppstod när förfrågningar behövde skickas utan att medvetet veta vad du ber om eller vem mottagaren är.
i det här mönstret frikopplas anropsklassen från den klass som faktiskt utför en åtgärd. Invokerklassen har endast den callable method execute, som kör det nödvändiga kommandot, när klienten begär det.
Låt oss ta en grundläggande verkliga exempel, beställa en måltid på en fin restaurang., När flödet går, ger du din beställning (kommando) till servitören (invoker), som sedan lämnar över den till kocken(mottagare), så att du kan få mat. Kanske låter enkelt … men lite meh att koda.
tanken är ganska enkel, men kodningen går runt näsan.,
flödet av drift på den tekniska sidan är, du gör ett konkret kommando, som implementerar Kommandogränssnittet, ber mottagaren att slutföra en åtgärd och skicka kommandot till åberoparen. Invokeraren är den person som vet när du ska ge det här kommandot. Kocken är den enda som vet vad man ska göra när man får det specifika kommandot / beställningen., Så, när exekveringsmetoden för åberoparen körs, orsakar den i sin tur kommandoobjektens exekveringsmetod att köras på mottagaren och därigenom slutföra nödvändiga åtgärder.,
vad vi behöver implementera är;
- ett gränssnittskommando
- en klassorder som implementerar Kommandogränssnitt
- en klass servitör (invoker)
- en klass kock (mottagare)
så går kodningen så här:
kock, mottagaren
kommando, gränssnittet
public interface Command {public abstract void execute();}
order, concrete Command
servitör, Invoker
public class Waiter {private Order order;public Waiter(Order ord) {this.order = ord;}public void execute() {this.order.execute();}}
du, klienten
som du kan se ovan, gör klienten en beställning och ställer in mottagaren som kocken., Ordern skickas till servitören, som kommer att veta när man ska utföra ordern (dvs när man ska ge kocken order att laga mat). När invoker exekveras körs orderns exekveringsmetod på mottagaren (dvs kocken får kommandot att antingen laga pasta ? eller baka tårta?).,
snabb recap
i det här inlägget gick vi igenom:
- vad ett designmönster verkligen är,
- de olika typerna av designmönster och varför de är olika
- ett grundläggande eller vanligt designmönster för varje typ
Jag hoppas att detta var till hjälp.
hitta koden repo för inlägget, här.