23
Tags:
.net
c#
Skrevet af
Bruger #2730
@ 02.05.2004
De fleste rynker nok lidt på næsen når man nævner ordet object factory, for hvad er det og hvorfor skulle man bruge det? I denne artikel vil jeg svare på disse spørgsmål samt vise en konkret implementation af en object factory. Der benyttes Visual Studio .NET som udviklingsværktøj i denne artikel. Brugere der benytter andre udviklingsværktøjer skal således tilpasse deres løsning ud fra de værktøjsspecifikke referencer i denne artikel.
Hvad er en object Factory, og hvad bruges den til?
En object factory er, lidt som navnet antyder, en fabrik der producerer en masse objekter. Disse objektet kan så efterfølgende bruges som ganske almindelige objekter i den applikation udvikleren er ved at bygge. Man støder ofte på udtrykket object factory i forbindelse med spiludvikling hvor der er et stort behov for at loade alle de objekter der skal indgå i et spil. Hvis vi tager udgangspunkt i et kendt strategispil som 'Age of Mythology' fra Ensemble Studios så er stort set alle de genstande der eksisterer i spillet noget der er loadet med en eller anden form for objekt factory. Det er typisk en rigtig god ide at bygge en objekt factory til ethvert spil hvad enten det er stort eller ej, dette bevirker at arkitekturen i spillet samt udviddelsesmulighederne er meget lette at gå til. En object factory kan komme i mange forskellige afskygninger, et eksempel på object factory der ikke er så langt væk findes i planerne for det spil der udvikles i en gruppe her på udvikleren. Planen var at lave et strategispil! I ethvert strategispil skal der loades et antal spillere, mange forskellige bygninger, teknologier og terrænobjekter. Alt dette blev designet til at skulle ske i en object factory, således spillet var nemt at udvidde samtidig med at man havde muligheden for i designfasen af spillet at kunne modificere eksempelvis de forskellige bygninger ved blot at ændre i den fil der blev indlæst. En objekt factory er stort set ikke andet end en mængde funktionalitet der tilsammen kan producere en masse objekter, hurtigt. Der er ingen faste design patterns for en Object factory eller hovedregler for opbygningen, de varierer fra projekt til projekt.
En object factory i kode
For at bygge en object factory er det vigtigste mantra overhovedet: "design". Det kan ikke anbefales nok at man gør sig en masse tanker om hvordan ens objekter kommer til at se ud således man undgår for mange frustrationer med hensyn til at modificere og udvidde sin object factory til at favne nye attributter. Typisk når jeg laver object factories benytter jeg XML som mit underliggende filformat, dette gøres fordi det simpelthen er så legende let at tilgå og utroligt nemt at redigere i, samtidig med at selv enorme XML dokumenter er hurtige at loade. For nu at komme i gang med at kode en object factory skal vi have lavet et projekt i Visual Studio .NET. Vi vil bygge en almindelig Windows Applikation for at forenkle metodikken, selvom man nok ville have valgt at bygge et class library til at holde en object factory. Start med at lave en Windows Applikation i Visual Studio .NET, og kald den noget så opfindsomt som 'ObjectFactory'. For at vise de objekter i applikationen vi vil bygge med vores object factory tilføjer vi et ListView til vores applikation, sætter 'Dock' til 'Fill' og sæt 'View' til details. Ud over dette kan der også aktiveres gridlines og der laves to kolonner, en der hedder 'Object' og en der hedder 'Name'. Disse to skal vise henholdsvist Objekt typen og navnet på objektet samt dets attributter.
Den Object factory vi vil bygge skal kunne loade bygninger og enheder til et fiktivt strategispil. Der tages udgangspunkt i en XMl fil der indeholder de forskellige informationer omkring hvilke status bygninger og enheder har. Der tages udgangspunkt i følgende
XML dokument.
<?xml version="1.0" encoding="utf-8" ?>
<objects>
<buildings>
<building>
<name>Blacksmith</name>
<hitpoints>150</hitpoints>
</building>
<building>
<name>Town hall</name>
<hitpoints>100</hitpoints>
</building>
<building>
<name>Lumber mill</name>
<hitpoints>175</hitpoints>
</building>
<building>
<name>Temple</name>
<hitpoints>200</hitpoints>
</building>
<building>
<name>House</name>
<hitpoints>50</hitpoints>
</building>
</buildings>
<units>
<unit>
<name>Swordsman</name>
<hitpoints>100</hitpoints>
<armor>6</armor>
<strength>25</strength>
</unit>
<unit>
<name>Archer</name>
<hitpoints>120</hitpoints>
<armor>5</armor>
<strength>23</strength>
</unit>
<unit>
<name>Mage</name>
<hitpoints>50</hitpoints>
<armor>3</armor>
<strength>14</strength>
</unit>
<unit>
<name>Paladin</name>
<hitpoints>150</hitpoints>
<armor>10</armor>
<strength>32</strength>
</unit>
</units>
</objects>
Et meget simpelt XML dokument der indeholder nogle få, meget simple enheder og bygniner. For at kunne lave de korrekte objekter skal der bygges nogle klasser i vores applikation der afspejler den objekt struktur der findes i denne XML fil. Det er her vi kommer til det punkt jeg talte om indledningsvist i denne artikel, nu gælder det om for så vidt muligt at have defineret alle sine attributter på hvert objekt i XML filen ellers bliver det en evig kamp mellem at først tilpasse XML filen, så tilpasse klasserne og herefter object factoryen. Vi vil nu lave to klasser der passer til hver type objekt, det vil sige en 'unit' klasse og en 'building' klasse. Vi tilføjer to nye klasser til vores løsning og bygger disse to klasser:
Unit klassen
using System;
namespace ObjectFactory
{
public class Unit
{
private string name;
private int hitpoints;
private int armor;
private int strength;
/// <summary>
/// Constructor til Unit objektet
/// </summary>
/// <param name="newName">Navnet som dette unit objekt skal have</param>
/// <param name="newHitpoints">Antallet af hitpoints</param>
/// <param name="newArmor">En armor værdi</param>
/// <param name="newStrength">Styrken på unit'en</param>
public Unit(string newName, int newHitpoints, int newArmor, int newStrength)
{
name = newName;
hitpoints = newHitpoints;
armor = newArmor;
strength = newStrength;
}
public string Name
{
get { return name; }
set { name = value; }
}
public int HitPoints
{
get { return hitpoints; }
set { hitpoints = value; }
}
public int Armor
{
get { return armor; }
set { armor = value; }
}
public int Strength
{
get { return strength; }
set { strength = value; }
}
}
}
Building klassen
using System;
namespace ObjectFactory
{
public class Building
{
private string name;
private int hitpoints;
/// <summary>
/// Constructor til en bygning
/// </summary>
/// <param name="newName">Navnet på bygningen</param>
/// <param name="newHitpoints">Antallet af hitpoints</param>
public Building(string newName, int newHitpoints)
{
name = newName;
hitpoints = newHitpoints;
}
public string Name
{
get { return name; }
set { name = value; }
}
public int Hitpoints
{
get { return hitpoints; }
set { hitpoints = value; }
}
}
}
Der er ikke noget specielt ved disse to klasser, det er ganske almindelige klasser der er defineret. Det der nu skal ske er at vi laver en reel ObjectFactory klasse der indeholder funktionalitet til at loade fra XML filen og oprette objekter ud fra dette. Når et objekt er blevet oprettet bliver det lagt i en statisk liste der så er tilgængelig for andre applikationer. Det betyder at skal man have fat i en bygning henter man en liste af bygninger og finder den man skal bruge (alternativt kan man jo bare tilbyde forskellige metoder på ObjectFactoryen der klarer denne funktionelitet). Vi vil dog i første omgang holde os til at tilbyde to lister, en der indeholder alle bygninger og en der indeholder alle enheder. For at komme i gang tilføjer vi endnu en ny klasse til vores løsning der hedder 'ObjectFactory', og tilføjer følgende 'using' klausul til de eksisterende
using System.Xml;
Dette namespace gør at vi kan tilgå og manipulere XML dokumenter. Det næste der skal ske er at vi skal have lavet et XML dokument samt loadet vores Objects.xml dokument ind i dette dokument. Da vores Object factory kun skal loade alle elementerne een gang loader vi vores dokument i constructoren, og så tilbyder to metoder (disse behøver nødvendig ikke være public, det ville faktisk være bedre at sjule dem og så lade constructoren kalde dem, således at ObjectFactory klassen selv håndterede det) til at loade henholdsvis bygninger og enheder. Dette betyder at vores ObjectLoader fil efter et par modifikationer med load af xml dokument samt midlertidige, tomme metoder ser således ud:
using System;
using System.Collections;
using System.Xml;
namespace ObjectFactory
{
public class ObjectFactory
{
public static ArrayList Buildings;
public static ArrayList Units;
private XmlDocument myDoc;
public ObjectFactory()
{
//initialiser vores lister
Buildings = new ArrayList();
Units = new ArrayList();
//initialiser og load XML dokument
myDoc = new XmlDocument();
myDoc.Load("Objects.xml");
}
public void LoadBuildings()
{
}
public void LoadUnits()
{
}
}
}
Nu kommer alt det sjove! Nu skal vi til at loade først alle vores bygninger og så alle vores enheder. Først finder vi en liste der indeholder alle buildings i vores XML fil, det er nemt og hurtigt at gøre med en xpath. Herefter begynder vi at konstruere objekter ud fra den enkelte XmlNode i den XmlNodeList vores xpath expression giver os. Herefter bygger vi objekter ud fra hver enkelt element i vores node og tilføjer dette objekt til listen over enten bygninger eller enheder alt efter hvor det hører hjemme. Koden til den metoder der skal loade alle bygningerne ser således ud, den er lidt simplificeret for at understrege hvad der skal ske.
public void LoadBuildings()
{
XmlNodeList myList = myDoc.SelectNodes("objects/buildings/building");
for(int x=0;x<myList.Count;x++)
{
XmlNode buildingNode = (XmlNode)myList[x];
XmlNode nameNode = buildingNode.SelectSingleNode("name");
XmlNode hitpointNode = buildingNode.SelectSingleNode("hitpoints");
Building myBuilding = new Building(nameNode.InnerText, Convert.ToInt32(hitpointNode.InnerText));
Buildings.Add(myBuilding);
}
}
Efter at have implementeret vores metode der loader bygninger er det tid til at få loadet alle enheder. Dette sker på samme måde, dog er der et par ekstra attributter der skal tages højde for, men ikke noget der skulle betyde noget som helst.
public void LoadUnits()
{
XmlNodeList myList = myDoc.SelectNodes("objects/units/unit");
for(int i=0;i<myList.Count;i++)
{
XmlNode unitNode = (XmlNode)myList[x];
XmlNode nameNode = unitNode.SelectSingleNode("name");
XmlNode hitpointsNode = unitNode.SelectSingleNode("hitpoints");
XmlNode armorNode = unitNode.SelectSingleNode("armor");
XmlNode strengthNode = unitNode.SelectSingleNode("strength");
Unit myUnit = new Unit(nameNode.InnerText, Convert.ToInt32(hitpointsNode.InnerText),
Convert.ToInt32(armorNode.InnerText),
Convert.ToInt32(strengthNode.InnerText));
Units.Add(myUnit);
}
}
Det sidste der skal laves er at implementere load eventen på vores form således at vi så snart applikationen loader får indlæst alle vores objekter. Læg mærke til at vi lavede vores lister 'static' hvilket betyer at vi bare kan referere dem uden at skulle lave en instans af vores object factory og dermed også loade XML dokumentet igen. Den modificerede load event på vores form ser nu således ud:
private void Form1_Load(object sender, System.EventArgs e)
{
//load alle objekter i object factory
ObjectFactory myFactory = new ObjectFactory();
myFactory.LoadBuildings();
myFactory.LoadUnits();
//vis alle objekter i skema
for(int i=0;i<ObjectFactory.Buildings.Count;i++)
{
Building myBuilding = (Building)ObjectFactory.Buildings[x];
ListViewItem myItem = new ListViewItem("Building");
myItem.SubItems.Add(myBuilding.Name+": Hitpoints:"+Convert.ToString(myBuilding.Hitpoints));
listView1.Items.Add(myItem);
}
for(int j=0;j<ObjectFactory.Units.Count;j++)
{
Unit myUnit = (Unit)ObjectFactory.Units[j];
ListViewItem myItem = new ListViewItem("Unit");
myItem.SubItems.Add(myUnit.Name+":"+
" Armor:"+Convert.ToString(myUnit.Armor)+
" Hitpoints:"+Convert.ToString(myUnit.HitPoints)+
" Strength:"+Convert.ToString(myUnit.Strength));
listView1.Items.Add(myItem);
}
}
Hvis man kører applikationen nu, uden at have gjort andet end følge mine anvisninger vil man få en fejl der siger noget i retning af at den ikke kan finde Objects.xml. Dette skyldes at vi default kigger i samme bibliotek som vores fil eksekveres fra og i dette tilfælde er det i bin biblioteket under debug folderen, så flyt Objects.xml ned i samme mappe som .exe filen og kør applikationen
Vi har nu en fuldt integreret object factory der er i stand til at loade objekter direkte fra en xml fil. Dette begrænsede eksempel har kun indeholdt 9 forskellige objekter, det er overkommeligt at vedligeholde den statisk i en applikation og blot ændre dem direkte i koden hver gang de bliver ændret. Kommer man derimod op i eksempelvis store spil hvor der skal loades 1000 objekter er en object factory et must!
Hvad synes du om denne artikel? Giv din mening til kende ved at stemme via pilene til venstre og/eller lægge en kommentar herunder.
Del også gerne artiklen med dine Facebook venner:
Kommentarer (17)
Brian.
Fusket, at skrive om det samme emne 2 gange.
Og enda, er 80% af teksten det samme...
Selvfølgelig er det ok. Men det er jo at tjene dobelt så meget up for det... Det er lidt snyd. Og du har skrevet 100% det samme og så bare nogle andre koder.
Se selv hans artikel om det samme emne under delphi:
http://www.udvikleren.dk/article.php?aid=207
Jeg synes også det er fusket at skriove samme kommentar to steder, en gang ved hver artikel. Du har selvfølgelig set mit indlæg i foraet til co-admins hvor jeg stiller spørgsmålet omkring hvordan man tackler denne situation med to artikler i hver sit sprog.... du har også set svaret ikk.... Selvfølgelig har du det siden du kan få dig til at anklage mig..... ellers ville det jo være tåbeligt
Synes ikke at man kan anklage en forfatter for at skrive to artikler. Ok det er godt nok det samme der står i dem men bare i to forskellige sprog, men så synes jeg samtidig at Shafth skal anklage forlaget som har udgivet HC Andersens bøger på 20 forskellige sprog. For det er jo også fusk at det samme bliver udgivet men bare på Japansk eller Russisk for så tjener forlaget jo flere penge:p HALLO Shafth... Det er sgu da kun godt at han vil skrive både til Delphi brugerne og til .NET brugerne. Det skulle vi være glade for at der er en som vil ofre sin tid på at skrive artiklerne til to forskellige sprog.
Så skulle han måske kun have 750 points per artikel!
Hold da op med at ha' så ondt i røven, det er da altid noget at der er nogen der gidder skrive nogle .NET relaterede artikler .. der er ikke for mange af dem herinde og Brians artikler er i det mindste af en rimelig høj kvalitet og desuden drejer det sig jo bare om nogle virtuelle point så om han skal have 1000 eller 750 kan da vel for pokker være ligegyldigt.
Kom videre folkens og lad os så få nogle kommentarer henvendt til selve artiklen istedet
Jonas har sådan set ret i at man godt kunne vælge at give lidt mindre point for hver artikel, når der nu er 2 med meget af det samme indhold. Hvis man kigger på Brian's profil vil man dog se at han leverer den ene kvalitetsartikel efter den anden, og derudover er den forfatter herinde der har skrevet flest artikler, så vi blev enige om at vi ikke havde nogen problemer i at give ham 1000 UP pr stk.
Brian, keep up the good work, det er altid en fornøjelse at modtage artikler fra dig
Jeg unskylder også her får det jeg skrev, og siger at det ikke var meningen at skrive det på den måde, så det lyd som en anklage...
Så alle ikke betrakter mig som en, der ønsker at være onde mod folk, beder jeg dem der har læst det tage en tur på linken, til delphi's artikel om dette emne, læse hvad jeg har skrevet, så jeg ikke får et dårligt ry af at jeg er den mand her på udvikleren, som går og anklager folk.
Her er linken:
http://www.udvikleren.dk/article.php?aid=208&techid=2Jeg beklager om må sige at det jo var får sjov, får at gøre lidt sjov. Og sådan at folk kan se det spøjse.
På den link har jeg lave en ny udgave af det jeg skrev, hvor jeg skriver det mere så det ligner mere at det er ironisk (som det også skulle være).
Brian jeg unskylder. Jeg er nok dårlig at skrive noget ironisk på nettet. Det er svært at skrive igennem tekst.
Håber at når du nu læser det igennem, kan godt se at jeg ikke ville gøre noget fortræd med de ord. I kan jo ikke se om jeg griner over det, og har et udtryk i mit ansigt der viser at det er en "and".
Eller om i kan se om jeg er irriteret over at du har gjort det sådan.
Jeg unskylder. og håber, at du kan aceptere det.
Og på den måde gøre det godt igen.
IM SO SORRY. Cant chance it. so word may do it better. ;-(
Fint nok, jeg var også selv i tvivl omkring hvordan emnet skulle behandles... derfor hørte jeg Co-admin....
Diskutionen er afsluttet !
:-)
tak.
Og igen flot artikel (har kun læst delphi versionen)... Its allright
Hvad er det for noget vrøvl, "han fortjener kun dit og dat", nej gu fandme nej, han fortjener det dobbelte hvis vi skal gå ind i de detajler (det dobbelte for hver artikel), jeg synes godt nok det er stærkt gået at han skriver den samme artikel til 2 sprog, thumbs up der. Det giver jo mange flere muligheden for at læse den (og bruge den), selvom det til tider kan være ret kedeligt at skrive den til 2 artikler. Jeg synes personligt godt om det, da mange flere kan læse disse kvalitets artikler. 5 Herfra!
Enig med Kaare
Det er jeg sådan set også.
Men doppel op. Ahhh. Det ville da værer lige så godt hvis han skrev to forskællige artikler.
Og nu har han jo også skrev 90% det samme i begge artikler...
Men jeg syns det han har fået nu er ok.
Kaper TSW har nu også ret! Det er ikke løgn af Brian Hvarregaard leverer mange gode artikler. Kan godt forstå beslutningen med pointgivningen...han fortjener det!
Syndes ikke man skal ha undt i røven over de points.... Til 7 og sist får vi jo nok brug for at læse artiklen om man kan delphi eller C#....! Se hvis han kun havede skrevet den til C# kunne jeg ikke bruge den da jeg bruger delphi
Det er en meget fin artikel som både demonstrere ægte objekt orienteret programering og hvad man også kan bruge XML teknologierne til. Jeg kunne måske savne lidt fokus på syntaksologi og sprog - men, den er jo også skrevet til 'lettere øvede'. Thumbs up.
Hej. Jeg er ny herinde.
Jeg har fundet nogle fejl i dine kodeeksempler.
Eksempelvis din forløkke som henter alle units.
Du itererer på i, men udvælger på x (ved ikke om det er en copy/paste fejl) - men en fejl er det da :-)
Men ellers fin artikel. Det skal lige siges, at den er nem at konvertere til WPF.
Du skal være
logget ind for at skrive en kommentar.