ontwerppatronen zijn ontwerppatronen voor terugkerende problemen die wij software engineers vaak tegenkomen. Het is geen code-Ik herhaal, CODE CODE. Het is als een beschrijving van hoe deze problemen aan te pakken en een oplossing te ontwerpen.
het gebruik van deze patronen wordt beschouwd als een goede praktijk, omdat het ontwerp van de oplossing behoorlijk beproefd is, wat resulteert in een betere leesbaarheid van de uiteindelijke code., Design patronen worden vaak gemaakt voor en gebruikt door OOP talen, zoals Java, waarin de meeste van de voorbeelden van nu af aan zullen worden geschreven.
soorten ontwerppatronen
Er zijn momenteel ongeveer 26 patronen ontdekt (Ik denk niet dat ik ze allemaal zal doen…).
deze 26 kunnen worden ingedeeld in 3 typen:
1. Creational: deze patronen zijn ontworpen voor klasse instantiation. Ze kunnen ofwel klassencreatiepatronen of objectcreatiepatronen zijn.
2. Structureel: deze patronen zijn ontworpen met betrekking tot de structuur en samenstelling van een klasse., Het belangrijkste doel van de meeste van deze patronen is om de functionaliteit van de betrokken klasse(sen) te verhogen, zonder veel van de samenstelling te veranderen.
3. Gedragspatronen: deze patronen zijn ontworpen afhankelijk van hoe een klasse communiceert met anderen.
in dit bericht gaan we door een basisontwerp patroon voor elk geclassificeerd type.
Type 1: Creational-The Singleton Design Pattern
Het Singleton Design Pattern is een Creational pattern, met als doel slechts één instantie van een klasse te creëren en slechts één globaal toegangspunt tot dat object te bieden., Een veel gebruikt voorbeeld van een dergelijke klasse in Java is kalender, waar je niet een instantie van die klasse te maken. Het gebruikt ook zijn eigengetInstance()
methode om het te gebruiken object te verkrijgen.
Een klasse die gebruik maakt van het singleton ontwerppatroon zal
- een private statische variabele bevatten, die de enige instantie van de klasse bevat.
- een private constructor, dus het kan nergens anders worden geïnstalleerd.
- een publieke statische methode, om de enkele instantie van de klasse terug te geven.,
Er zijn veel verschillende implementaties van singleton design. Vandaag ga ik door de implementaties van;
1. Eager Instantiation
2. Luie instantiatie
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;}}
Dit type instantiation gebeurt tijdens het laden van de klasse, omdat de instantiation van de variabele instantie buiten elke methode plaatsvindt. Dit vormt een groot nadeel als deze klasse helemaal niet wordt gebruikt door de client applicatie. Het noodplan, als deze klasse niet wordt gebruikt, is de luie instantiatie.,
luie dagen
Er is niet veel verschil met de bovenstaande implementatie. De belangrijkste verschillen zijn dat de statische variabele in eerste instantie null wordt verklaard, en alleen wordt gestantieerd binnen de getInstance()
methode als – en alleen als – de instance variabele null blijft op het moment van de controle.
dit lost een probleem op, maar een ander probleem bestaat nog steeds. Wat als twee verschillende klanten toegang krijgen tot de Singleton klasse op hetzelfde moment, recht naar de milliseconde?, Nou, ze zullen controleren of de instantie is null op hetzelfde moment, en zal het waar vinden, en zo zullen twee instanties van de klasse voor elk verzoek door de twee clients te creëren. Om dit op te lossen, Thread veilige instantiation moet worden geïmplementeerd.
(Thread) Safety is Key
in Java wordt het sleutelwoord gesynchroniseerd gebruikt op methoden of objecten om thread safety te implementeren, zodat slechts één thread tegelijkertijd toegang heeft tot een bepaalde bron. De class instantiation wordt binnen een gesynchroniseerd blok geplaatst zodat de methode slechts door één cliënt op een bepaald moment kan worden benaderd.,
de overhead voor de gesynchroniseerde methode is hoog, en vermindert de prestaties van de hele operatie.
bijvoorbeeld, als de instance variabele al is geïnstalleerd, dan wordt elke keer dat een client de getInstance()
methode gebruikt, de synchronized
methode uitgevoerd en de prestaties dalen. Dit gebeurt alleen om te controleren of de waarde van instance
variabelen null is. Als het vindt dat het is, laat het de methode.
om deze overhead te verminderen, wordt dubbele vergrendeling gebruikt., De controle wordt ook gebruikt voor desynchronized
methode, en als de waarde alleen null is, wordt desynchronized
methode uitgevoerd.
nu naar de volgende classificatie.
Type 2: Structural – The Decorator Design Pattern
Ik geef je een klein scenario om een betere context te geven voor waarom en waar je het Decorator patroon moet gebruiken.
stel dat je een coffeeshop hebt, en zoals elke newbie, begin je met slechts twee soorten gewone koffie, de house blend en dark roast., In uw factureringssysteem, was er een klasse voor de verschillende koffiemengsels, die de drank abstracte klasse erft. Mensen eigenlijk beginnen te komen en hebben uw prachtige (zij het bitter?) koffie. Dan zijn er de koffie newbs die, God verhoede, suiker of melk willen. Wat een travestie voor koffie!! ??
nu moet u deze twee add-ons ook hebben, zowel in het menu als helaas op het factureringssysteem. Oorspronkelijk zal uw IT-persoon een subklasse maken voor beide koffies, de ene inclusief suiker, de andere melk., Dan, omdat klanten altijd gelijk hebben, zegt men deze gevreesde woorden:
” Kan ik een melk koffie, met suiker, alstublieft?”
???
daar gaat je factureringssysteem dat weer in je gezicht lacht. Nou, terug naar de tekentafel….
de IT-persoon voegt vervolgens melkkoffie met suiker toe als een andere subklasse aan elke ouderkoffieklasse. De rest van de maand gaat vlot, mensen staan in de rij om je koffie te drinken, je verdient echt geld. ??
maar wacht, er is meer!
de wereld is weer tegen u., Een concurrent opent zich aan de overkant van de straat, met niet alleen 4 soorten koffie, maar meer dan 10 add-ons ook! ?
Je koopt al die en meer, om zelf betere koffie te verkopen, en vergeet dan niet dat je vergeten bent om dat vervallen factureringssysteem bij te werken. Je kunt misschien niet het oneindige aantal subklassen voor een en alle combinaties van alle add-ons te maken, met de nieuwe koffie blends ook. Niet te vergeten, de grootte van het uiteindelijke systeem.??
tijd om daadwerkelijk te investeren in een goed factureringssysteem., Je vindt nieuw IT personeel, die eigenlijk weet wat ze doen en ze zeggen;
“waarom, dit zal zo veel gemakkelijker en kleiner zijn als het gebruik van de decorator patroon.”
Wat is dat in hemelsnaam?
het ontwerppatroon van de decorateur valt in de structurele categorie, die betrekking heeft op de werkelijke structuur van een klasse, hetzij door overerving, compositie of beide. Het doel van dit ontwerp is om de functionaliteit van een object tijdens runtime te wijzigen. Dit is een van de vele andere ontwerppatronen die gebruik maken van abstracte klassen en interfaces met compositie om het gewenste resultaat te krijgen.,
laten we wiskunde een kans geven (huiveren?) om dit alles in perspectief te brengen;
neem 4 koffiemelanges en 10 add-ons. Als we vast te houden aan de generatie van subklassen voor elke verschillende combinatie van alle add-ons voor een soort koffie. Dat is;
(10-1)2 = 92 = 81 subklassen
we trekken 1 af van de 10, omdat je een add-on niet kunt combineren met een andere van hetzelfde type, suiker met suiker klinkt dom. En dat is voor slechts één koffiemix. Vermenigvuldig die 81 met 4 en je krijgt maar liefst 324 verschillende subklassen!, Praat over al die codering …
maar met het decorator patroon vereist slechts 16 klassen in dit scenario. Wedden van wel?
Als we de kaart uit onze scenario ‘ s op basis van de klasse diagram hierboven, we krijgen 4 klassen voor de 4 koffiemelanges, 10 voor elke add-on en 1 voor de abstracte component en 1 voor de abstracte decorateur. Zie je! 16!, Geef nu die $100.?? (jk, maar het zal niet worden geweigerd als gegeven… alleen maar zeggen)
zoals je kunt zien van boven, net zoals de concrete koffie mengsels zijn subklassen van de drank abstracte klasse, De addon abstracte klasse erft ook zijn methoden uit. De add-ons, dat zijn de subklassen, op hun beurt erven alle nieuwe methoden om functionaliteit toe te voegen aan het basisobject wanneer dat nodig is.
laten we naar codering gaan, om dit patroon in gebruik te zien.,
eerst de klasse Abstracte drank maken, dat alle verschillende koffiemengsels zullen erven van:
vervolgens de beide klassen van betonkoffie toevoegen.
De addon abstract class erft ook van de drank abstract class (meer hierover hieronder).
en nu de concrete implementaties van deze abstracte klasse:
zoals u hierboven kunt zien, kunnen we elke subklasse drank doorgeven aan elke subklasse van AddOn, en krijgen de toegevoegde kosten evenals de bijgewerkte beschrijving. En, aangezien de addon klasse is in wezen van het type drank, kunnen we een AddOn doorgeven aan een andere AddOn., Op deze manier kunnen we een willekeurig aantal add-ons toevoegen aan een specifieke koffiemix.
schrijf nu wat code om dit uit te testen.
het eindresultaat is:
Het werkt! We waren in staat om meer dan één add-on toe te voegen aan een koffiemengsel en met succes de uiteindelijke kosten en beschrijving bij te werken, zonder de noodzaak om oneindige subklassen te maken voor elke add-on combinatie voor alle koffieblends.
tot slot, naar de laatste categorie.,
Type 3: Behavioral-the Command Design Pattern
een behavioral design pattern richt zich op hoe klassen en objecten met elkaar communiceren. De belangrijkste focus van het commandopatroon is het inprenten van een hogere mate van losse koppeling tussen betrokken partijen (lees: klassen).
Uhhhh … Wat is dat?
koppeling is de manier waarop twee (of meer) klassen die met elkaar interageren, nou ja, interageren. Het ideale scenario wanneer deze klassen interageren is dat ze niet sterk afhankelijk van elkaar. Dat is losse koppeling., Dus, een betere definitie voor losse koppeling zou zijn, klassen die onderling verbonden zijn, het maken van het minste gebruik van elkaar.
de behoefte aan dit patroon ontstond wanneer Verzoeken moesten worden verzonden zonder bewust te weten wat u vraagt of wie de ontvanger is.
in dit patroon wordt de aanroepende klasse losgekoppeld van de klasse die daadwerkelijk een actie uitvoert. De invoker-klasse heeft alleen de callable method execute, die het benodigde commando uitvoert, wanneer de client daarom verzoekt.
laten we een basic real-world voorbeeld nemen, een maaltijd bestellen in een chique restaurant., Als de stroom gaat, geef je je bestelling (Commando) aan de ober (invoker), die het vervolgens overhandigt aan de chef(ontvanger), zodat je eten kunt krijgen. Klinkt misschien simpel … maar een beetje meh naar code.
het idee is vrij eenvoudig, maar de codering gaat rond de neus.,
de stroom van de operatie aan de technische kant is, je maakt een concreet commando, dat de Command interface implementeert, vraagt de ontvanger om een actie te voltooien, en stuurt het commando naar de invoker. De invoker is de persoon die weet wanneer hij dit commando moet geven. De chef-kok is de enige die weet wat te doen wanneer gegeven de specifieke opdracht / Opdracht., Dus, wanneer de uitvoeren methode van de invoker wordt uitgevoerd, het, op zijn beurt, zorgt ervoor dat de opdracht objecten’ uitvoeren methode om te draaien op de ontvanger, waardoor de nodige acties te voltooien.,
Wat we nodig hebben om te implementeren is;
- Een interface Commando
- Een klasse Om die afhandeling van Commando-interface
- Een klasse Ober (ejb)
- Een class Chef (ontvanger)
Dus, het programmeren gaat als volgt:
Chef-kok, de ontvanger
Commando, de interface
public interface Command {public abstract void execute();}
Om de concrete opdracht
Ober, de ejb
public class Waiter {private Order order;public Waiter(Order ord) {this.order = ord;}public void execute() {this.order.execute();}}
U, de client
Zoals je hierboven kunt zien, de Klant doet een Bestelling en stelt de Ontvanger als de Chef-kok., De bestelling wordt naar de ober gestuurd, die weet wanneer de bestelling moet worden uitgevoerd (d.w.z. wanneer de chef-kok de opdracht moet geven om te koken). Wanneer de invoker wordt uitgevoerd, wordt de uitvoermethode van de Orders uitgevoerd op de ontvanger (dat wil zeggen dat de chef de opdracht krijgt om ofwel pasta te koken ? of taart bakken?).,
Quick recap
In dit bericht gingen we door:
- Wat een ontwerppatroon werkelijk is,
- de verschillende soorten ontwerppatronen en waarom ze verschillend zijn
- een basis of gemeenschappelijk ontwerppatroon voor elk type
Ik hoop dat dit nuttig was.
vind hier de code repo voor de post.