"Hei, Amigo!"
"Hei, Bilaabo!"
"Emnet vårt i dag vil ikke bare være interessant - det vil være direkte episk."
"I dag skal jeg fortelle deg hva designmønstre er. "
"Kult! Jeg har hørt mye om dem. Jeg kan ikke vente!"
"Erfarne programmerere må skrive mange klasser. Men den vanskeligste delen av denne jobben er å bestemme hvilke klasser de skal lage og hvordan de skal dele arbeidet mellom dem."
"Jo mer de løste slike spørsmål, jo mer innså de at noen løsninger er gode, mens andre er dårlige."
"Dårlige løsninger skaper vanligvis flere problemer enn de løser. De strekker seg dårlig, skaper mange unødvendige begrensninger osv. Og gode løsninger er det motsatte."
"Er det noen analogi du kan lage?"
"La oss si at du bygger et hus. Og du tenker på hva det skal være laget av. Du bestemmer deg for at du trenger vegger, gulv og tak. Som et resultat bygger du et hus med flatt tak og ingen fundament. Et slikt hus vil sprekke, og taket vil lekke. Dette er en dårlig løsning."
"Omvendt vil et hus bestående av fundament, vegger og sadeltak være en god løsning. Kraftig snøfall byr ikke på noe problem, siden snøen vil gli av taket. Og skiftende jord er ikke noe å frykte, fordi fundamentet vil sikre stabilitet. Vi vil kalle en slik løsning god."
"Jeg skjønner. Takk."
"OK. Da fortsetter jeg."
"Med tiden ble de gode løsningene kjent som designmønstre, mens de dårlige ble kalt anti-mønstre."
"Et designmønster er som et svar på et spørsmål. Det er vanskelig å forstå hvis du aldri har hørt spørsmålet."
" Den første kategorien av mønstre er kreasjonsmønstre. Slike mønstre beskriver gode løsninger knyttet til skapelse av objekter."
"Hva er så komplisert med å lage objekter?"
"Som det skjer, er det akkurat det vi skal utforske nå."
Singleton mønster.
"Ofte i programmer kan det bare eksistere en enkelt kopi av enkelte objekter. For eksempel konsollen, loggeren, søppelsamleren, etc."
" Dårlig løsning: ikke lag noen objekter - bare lag en klasse hvis metoder alle er statiske."
" God løsning: lag et enkelt objekt og lagre det i en statisk variabel. Hindre opprettelsen av et annet objekt i klassen. For eksempel:"
class Sun
{
private static Sun instance;
public static Sun getInstance()
{
if (instance == null)
instance = new Sun();
return instance;
}
private Sun()
{
}
}
Sun sun = Sun.getInstance();
"Det er enkelt."
"Først gjør vi konstruktøren privat. Nå kan den bare kalles fra innsiden av klassen vår. Vi har blokkert opprettelsen av Sun-objekter overalt bortsett fra innenfor metodene til Sun-klassen."
"For det andre kan et objekt av denne klassen bare oppnås ved å kalle getInstance()-metoden. Ikke bare er dette den eneste metoden som kan lage et Sun-objekt, den sikrer også at det bare er ett slikt objekt."
"Jeg skjønner."
"Når du tenker: «Nå, hvordan skulle jeg gjøre det?», sier et mønster, «du kan prøve dette - dette er en av de beste løsningene.»
"Takk. Nå begynner ting å bli klart."
"Du kan også lese om dette mønsteret her ."
Fabrikkmønster.
"Her er en situasjon som programmerere møter veldig ofte. Du har en grunnklasse og mange underklasser. For eksempel en spillkarakterklasse (GamePerson) og klasser for alle de andre karakterene som arver den."
"La oss si at du har følgende klasser:"
abstract class GamePerson
{
}
class Warrior extends GamePerson
{
}
class Mag extends GamePerson
{
}
class Troll extends GamePerson
{
}
class Elf extends GamePerson
{
}
"Spørsmålet er hvordan vi fleksibelt og bekvemt administrerer opprettelsen av objekter i disse klassene."
"Hvis dette problemet virker langsøkt for deg, forestill deg at du i spillet må lage dusinvis av sverd og skjold, hundrevis av magiske trollformler og tusenvis av monstre. Du kan ikke klare deg uten en praktisk tilnærming til objektskaping her. "
" Fabriksmønsteret tilbyr en god løsning."
"Først må vi lage en enum hvis verdier samsvarer med de forskjellige klassene."
"For det andre, lag en spesiell Factory- klasse som har statiske metoder som lager objekt(er) basert på en enum-verdi."
"For eksempel:"
public enum PersonType
{
UNKNOWN,
WARRIOR,
MAG,
TROLL,
ELF,
}
public class PersonFactory
{
public static GamePerson createPerson(PersonType personType)
{
switch(personType)
{
WARRIOR:
return new Warrior();
MAG:
return new Mag();
TROLL:
return new Troll();
ELF:
return new Elf();
default:
throw new GameException();
}
}
}
GamePerson person = PersonFactory.createPerson(PersonType.MAG);
"Med andre ord, vi opprettet en spesiell klasse for å administrere objektskaping?"
"Japp."
"Så hvilke fordeler gir dette?"
"For det første, i denne klassen, kan objektene initialiseres med de nødvendige dataene."
"For det andre kan du sende den nødvendige enum-verdien mellom metodene så mye du vil for til slutt å lage det ønskede objektet."
"For det tredje trenger ikke antall enum-felt å samsvare med antall klasser. Det kan være mange typer tegn, men få klasser."
"For eksempel kan en Mag & Warrior bruke én klasse (menneske), men med forskjellig styrke og magiske egenskaper (konstruktørargumenter)."
"Slik kan det se ut (for klarhetens skyld har jeg også lagt til mørke alver):"
public enum PersonType
{
UNKNOWN,
WARRIOR,
MAG,
TROLL,
ELF,
DARK_ELF
}
public class PersonFactory
{
public static GamePerson createPerson(PersonType personType)
{
switch(personType)
{
WARRIOR:
return new Human(10, 0); // Strength, magic
MAG:
return new Human(0, 10); // Strength, magic
TROLL:
OGR:
return new Troll();
ELF:
return new Elf(true); // true – good, false – evil
DARK_ELF:
return new Elf(false); // true – good, false – evil
default:
throw new GameException();
}
}
}
GamePerson person = PersonFactory.createPerson(PersonType.MAG);
"I eksemplet ovenfor brukte vi bare tre klasser for å lage seks forskjellige typer objekter. Dette er veldig praktisk. Dessuten har vi all denne logikken konsentrert i én klasse og én metode."
"Anta nå at vi bestemmer oss for å lage en egen klasse for Ogre - vi endrer ganske enkelt et par linjer med kode her, og ikke halve applikasjonen."
"Jeg er enig. Det er en god løsning."
"Og det er det jeg snakker om: designmønstre er samlinger av gode løsninger."
"Jeg skulle også ønske jeg visste hvor jeg skulle bruke dem ..."
"Jepp. Jeg er enig, du vil ikke forstå med en gang. Likevel er det bedre å vite og ikke kunne gjøre enn å ikke vite og ikke kunne gjøre. Her er en annen nyttig lenke for deg om dette mønsteret: Factory Pattern "
"Å, takk."
" Abstrakt fabrikkmønster ."
"Noen ganger når du har mange gjenstander, oppstår ideen om å lage en fabrikk for fabrikker. En slik fabrikk kalles en abstrakt fabrikk ."
"Hvor skulle dette være nødvendig?!"
"Anta at du har flere grupper av identiske objekter. Dette er lettere å vise med et eksempel."
"Nå, la oss si at du har tre raser i spillet ditt: mennesker, alver og demoner. Og for balansen har hver rase krigere, bueskyttere og magikere. En spiller kan bare lage gjenstander som tilhører rasen han eller hun spiller for i spillet. Slik vil det se ut i kode:"
class Warrior
{
}
class Archer
{
}
class Mag
{
}
class HumanWarrior extends Warrior
{
}
class HumanArcher extends Archer
{
}
class HumanMag extends Mag
{
}
class ElfWarrior extends Warrior
{
}
class ElfArcher extends Archer
{
}
class ElfMag extends Mag
{
}
class DaemonWarrior extends Warrior
{
}
class DaemonArcher extends Archer
{
}
class DaemonMag extends Mag
{
}
Og la oss nå lage rasene, eller vi kan også kalle dem hærer.
abstract class Army
{
public Warrior createWarrior();
public Archer createArcher();
public Mag createMag();
}
class HumanArmy extends Army
{
public Warrior createWarrior()
{
return new HumanWarrior();
}
public Archer createArcher()
{
return new HumanArcher();
}
public Mag createMag()
{
return new HumanMag();
}
}
class ElfArmy extends Army
{
public Warrior createWarrior()
{
return new ElfWarrior();
}
public Archer createArcher()
{
return new ElfArcher();
}
public Mag createMag()
{
return new ElfMag();
}
}
class DaemonArmy extends Army
{
public Warrior createWarrior()
{
return new DaemonWarrior();
}
public Archer createArcher()
{
return new DaemonArcher();
}
public Mag createMag()
{
return new DaemonMag();
}
}
"Men hvordan bruker du dette?"
"Du kan bruke klassene Army, Warrior, Archer og Mage hvor som helst i programmet, og for å lage de nødvendige objektene - bare passere et objekt av ønsket Army-underklasse."
"For eksempel:"
Army humans = new HumanArmy();
Army daemons = new DaemonArmy();
Army winner = FightSimulator.simulate(humans, daemons);
"I eksemplet ovenfor har vi en klasse som simulerer kamper mellom forskjellige raser (hærer). Du trenger bare å passere to hærobjekter. Klassen selv bruker dem til å lage forskjellige tropper og gjennomføre virtuelle kamper mellom dem for å identifisere vinneren ."
"Jeg skjønner. Takk. En veldig interessant tilnærming."
"En god løsning, uansett hva du sier."
"Japp."
"Her er en annen god lenke om emnet: Abstrakt fabrikkmønster "
GO TO FULL VERSION