Prototype mønster i Java

1. Introduktion

I denne vejledning lærer vi om en af ​​de kreative designmønstre - prototype-mønsteret. Først forklarer vi dette mønster og fortsætter derefter med at implementere det i Java.

Vi diskuterer også nogle af dens fordele og ulemper.

2. Prototype mønster

Prototypemønsteret er generelt bruges når vi har en forekomst af klassen (prototype), og vi vil gerne oprette nye objekter ved blot at kopiere prototypen.

Lad os bruge en analogi til bedre at forstå dette mønster.

I nogle spil vil vi have træer eller bygninger i baggrunden. Vi kan måske indse, at vi ikke behøver at oprette nye træer eller bygninger og gengive dem på skærmen, hver gang karakteren bevæger sig.

Så vi opretter først en forekomst af træet. Derefter kan vi oprette så mange træer, som vi ønsker, fra denne forekomst (prototype) og opdatere deres positioner. Vi kan også vælge at ændre træernes farve til et nyt niveau i spillet.

Prototypemønsteret er ret ens. I stedet for at oprette nye objekter vi er bare nødt til at klone den prototypiske forekomst.

3. UML-diagram

I diagrammet ser vi, at klienten fortæller prototypen at klone sig selv og oprette et objekt. Prototype er en grænseflade og erklærer en metode til kloning af sig selv. BetonPrototype1 og BetonPrototype2 implementere operationen for at klone sig selv.

4. Implementering

En af måderne vi kan implementere dette mønster på Java er ved hjælp af klon () metode. For at gøre dette implementerer vi Klonabel interface.

Når vi prøver at klone, vi skal vælge mellem at lave en lav eller en dyb kopi. Til sidst koger det ned til kravene.

For eksempel, hvis klassen kun indeholder primitive og uforanderlige felter, kan vi bruge en lav kopi.

Hvis den indeholder henvisninger til foranderlige felter, skal vi gå efter en dyb kopi. Vi kan muligvis gøre det med kopi konstruktører eller serialisering og deserialisering.

Lad os tage det eksempel, vi nævnte tidligere, og fortsætte med at se, hvordan man anvender prototype-mønsteret uden at bruge Klonabel interface. For at gøre dette, lad os oprette en abstrakt klasse kaldes Træ med en abstrakt metode 'kopi'.

public abstract class Tree {// ... public abstract Tree copy (); }

Lad os nu sige, at vi har to forskellige implementeringer af Træ hedder PlasticTree og Fyrretræ:

offentlig klasse PlasticTree udvider Tree {// ... @Override public Tree copy () {PlasticTree plasticTreeClone = new PlasticTree (this.getMass (), this.getHeight ()); plasticTreeClone.setPosition (this.getPosition ()); returnere plasticTreeClone; }}
offentlig klasse PineTree udvider Tree {// ... @Override public Tree copy () {PineTree pineTreeClone = new PineTree (this.getMass (), this.getHeight ()); pineTreeClone.setPosition (this.getPosition ()); returner pineTreeClone; }}

Så her ser vi, at de klasser, der strækker sig Træ og implementere kopi metode kan fungere som prototyper til oprettelse af en kopi af sig selv.

Prototype mønster lader os også oprette kopier af objekter uden at afhænge af de konkrete klasser. Lad os sige, at vi har en liste over træer, og vi vil gerne oprette kopier af dem. På grund af polymorfisme kan vi nemt oprette flere kopier uden at kende træernes typer.

5. Testning

Lad os nu teste det:

public class TreePrototypesUnitTest {@Test public void givenAPlasticTreePrototypeWhenClonedThenCreateA_Clone () {// ... PlasticTree plasticTree = ny PlasticTree (masse, højde); plasticTree.setPosition (position); PlasticTree anotherPlasticTree = (PlasticTree) plasticTree.copy (); anotherPlasticTree.setPosition (otherPosition); assertEquals (position, plasticTree.getPosition ()); assertEquals (otherPosition, anotherPlasticTree.getPosition ()); }}

Vi ser, at træet er blevet klonet fra prototypen, og vi har to forskellige forekomster af PlasticTree. Vi har lige opdateret positionen i klonen og bevaret de andre værdier.

Lad os nu klone en liste over træer:

@Test offentlig ugyldighed givenA_ListOfTreesWhenClonedThenCreateListOfClones () {// Opret forekomster af PlasticTree og PineTree List træer = Arrays.asList (plasticTree, pineTree); Liste over treeClones = trees.stream (). Map (Tree :: copy) .collect (toList ()); // ... assertEquals (højde, plasticTreeClone.getHeight ()); assertEquals (position, plasticTreeClone.getPosition ()); }

Bemærk, at vi er i stand til at lave en dyb kopi af listen her uden at være afhængige af de konkrete implementeringer af Træ.

6. Fordele og ulemper

Dette mønster er praktisk, når vores nye objekt kun adskiller sig lidt fra vores eksisterende. I nogle tilfælde kan forekomster kun have nogle få kombinationer af tilstand i en klasse. Så i stedet for at oprette nye forekomster, vi kan oprette forekomsterne med den rette tilstand på forhånd og derefter klone dem, når vi vil.

Nogle gange støder vi muligvis på underklasser, der kun adskiller sig i deres tilstand. Vi kan eliminere disse underklasser ved at oprette prototyper med den oprindelige tilstand og derefter klone dem.

Prototype mønster, ligesom alle andre design mønstre, bør kun bruges, når det er passende. Da vi kloner objekterne, kan processen blive kompleks, når der er mange klasser, hvilket resulterer i et rod. Derudover er det svært at klone klasser, der har cirkulære referencer.

7. Konklusion

I denne vejledning lærte vi nøglebegreberne i Prototype-mønsteret og så, hvordan vi implementerer det i Java. Vi diskuterede også nogle af dets fordele og ulemper.

Som normalt er kildekoden til denne artikel tilgængelig på Github.