Det är en smärta att man måste ändra parametrarna för en funktion; du måste ändra alla andra samtal till den funktionen för att undvika fel. Men du kan komma runt detta med bara en parameter: ett konfigurationsobjekt.
Här är ett dumt exempel på en funktion för att skapa en robot:
funktion genereraRobot (armar: int, personlighet: String): Robot var robot: Robot = ny robot (); för (var i: int = 0; i < arms; i++) //create arm and add it to robot if (personality == "evil") robot.commands = "Destroy mankind."; else robot.commands = "Bake cookies." return robot; generateRobot(2, "evil");
Nu är det samma exemplet med ett konfigurationsobjekt:
funktion generateRobot (conf: Object): Robot var robot: Robot = ny robot (); för (var i: int = 0; i < conf.arms; i++) //create arm and add it to robot if (conf.personality == "evil") robot.commands = "Destroy mankind."; else robot.commands = "Bake cookies." return robot; generateRobot(arms:2, personality:"evil");
Jag har markerat de linjer som behöver ändras. du kan se att det inte finns någon stor skillnad.
Så om det inte finns någon skillnad, varför skulle vi bry oss göra det andra sättet? Det gör trots allt faktiskt lite svårare att använda; medan före vår IDE skulle kunna ge oss denna information om parametrarna som förväntas:
... nu kan det bara ge oss detta:
Antag att du vill lägga till ett par fler parametrar: en specificerar materialet att använda och en annan för att ange vilken färg dess laser ska vara. Det är inte för hårt, i båda fallen:
funktion genereraRobot (armar: int, personlighet: String, material: String, laserColor: String): Robot var robot: Robot = ny robot (); för (var i: int = 0; i < arms; i++) //create arm and add it to robot if (personality == "evil") robot.commands = "Destroy mankind."; else robot.commands = "Bake cookies." switch (material) case "wood": //wooden robot break; case "steel": default: //steel robot break; robot.laser = new Laser(); robot.laser.color = laserColor; return robot; generateRobot(2, "evil", "steel", "red");
funktion generateRobot (conf: Object): Robot var robot: Robot = ny robot (); för (var i: int = 0; i < conf.arms; i++) //create arm and add it to robot if (conf.personality == "evil") robot.commands = "Destroy mankind."; else robot.commands = "Bake cookies." switch (conf.material) case "wood": //wooden robot break; case "steel": default: //steel robot break; robot.laser = new Laser(); robot.laser.color = conf.laserColor; return robot; generateRobot(arms:2, personality:"evil", material:"steel", laserColor:"red");
Hittills, fortfarande inte mycket av en skillnad. Vad händer om du vill att dina robotar till alla har röda lasrar som standard? Enkelt igen. Utan ett konfigurationsobjekt behöver du bara ändra metodens signatur ( fungera
linje) och sedan kan du ta bort det senaste argumentet från funktionssamtalet:
funktion genereraRobot (armar: int, personlighet: String, material: String, laserColor: String = "röd"): Robot // det här är detsamma generateRobot (2, true, "steel"); // Jag tog bort det sista argumentet
Med ett konfigurationsobjekt är det lite knepigare - men inte så mycket:
funktion generateRobot (conf: Object): Robot if (! conf.laserColor) conf.laserColor = "red"; var robot: Robot = ny robot (); för (var i: int = 0; i < conf.arms; i++) //create arm and add it to robot if (conf.personality == "evil") robot.commands = "Destroy mankind."; else robot.commands = "Bake cookies." switch (conf.material) case "wood": //wooden robot break; case "steel": default: //steel robot break; robot.laser = new Laser(); robot.laser.color = conf.laserColor; return robot; generateRobot(arms:2, personality:"evil", material:"steel"); //I removed the last argument
Okej. Antag nu att du fastställer nästan alla dina robotar för att vara onda (jag menar, varför inte?), Så det är faktiskt typ av smärta att skriva "ondska" som en parameter varje gång. Naturligtvis vill du ställa in "ondskan" som standard - men du inte vill ställa in ett standardmaterial.
Det enda sättet du kan göra med en vanlig uppsättning av funktionsparametrar är att byta ordning på personlighet
och material
parameters:
funktion generateRobot (armar: int, material: String, personlighet: String = "evil", laserColor: String = "red"): Robot
Åh, men nu måste du byta ordningsföljdens ordning på varje enskilt funktionssamtal!
genereraRobot (2, "ondska", "stål"); // fungerar inte längre
Ett konfigurationsobjekt ger dig inte detta problem. Kolla in det:
funktion generateRobot (conf: Object): Robot if (! conf.laserColor) conf.laserColor = "red"; om (! conf.personality) conf.personality = "evil" // det här är detsamma generateRobot (arms: 2, material: "steel"); // ingen "personlighet" parameter? inga problem!
Propert! Alla dina gamla generateRobot ()
Funktionssamtal fortsätter att fungera, men du kan skapa nya samtal som inte stör att specificera personlighet
.
Du kan även bestämma dig för att bli av med personlighet
parameter helt och hållet:
funktion generateRobot (conf: Object): Robot if (! conf.laserColor) conf.laserColor = "red"; om (! conf.personality) conf.personality = "evil" var robot: Robot = ny robot (); för (var i: int = 0; i < conf.arms; i++) //create arm and add it to robot robot.commands = "Destroy mankind."; switch (conf.material) case "wood": //wooden robot break; case "steel": default: //steel robot break; robot.laser = new Laser(); robot.laser.color = conf.laserColor; return robot;
Ovanstående version av funktionen hänvisar inte till conf.personality
alls - men du kommer inte få ett fel om du fortfarande har samtal så här:
genereraRobot (armar: 2, personlighet: "ondskan", material: "stål");
Visst kan du få några förvirrade användare om du har samtal så här:
genereraRobot (armar: 2, personlighet: "bra", material: "stål");
... eftersom alla robotar är nu onda. Men åtminstone kommer koden att kompilera.
Av samma anledning kan du ändra ordningsföljden utan att det mattas alls och till och med lägga till nya parametrar som inte gör någonting än:
genereraRobot (material: "stål", laserColor: "grönt", armar: 2, röst: "Mr. T");
Koden för inställning av standardinställningarna är lätt att förstå hittills men kommer att bli väldigt irriterande att utvidga om vi behöver ha många parametrar:
om (! conf.laserColor) conf.laserColor = "red"; om (! conf.personality) conf.personality = "evil"
Låt oss skriva en mer generell kod för att klara det:
var standard: Objekt = laserColor: röd, personlighet: "ondskan" för (var-tangent: String i standardvärden) if (! conf [key]) conf [key] = standardvärden [tangent];
Den där för
Slingan kan vara lite förvirrande, så jag slår ner den. Först titta på detta:
för (var-tangent: String i standardvärden) spår (tangent);
Det här är en för ... in
loop, som kommer att skriva ut namnen på nycklarna inuti standard
objekt:
laserColor personlighet
Titta nu på den här raden:
trace (standard [ "laserColor"]);
Detta kommer att matas ut röd
- det är detsamma som att skriva spåra (defaults.laserColor)
.
Sedan följer du det här exemplet:
var exempel: Objekt = demo: "test"; trace (exempel [ "demo"]); trace (exempel [ "foo"]);
Vad tror du att detta kommer att ge ut?
Väl, exempel [ "demo"]
är det samma som example.demo
, vilket är lika med "testa"
. Men example.foo
existerar inte, så exempel [ "foo"]
kommer att återvända null
. Detta innebär att !exempel [ "foo"]
(notera utropstecken) motsvarar Sann
.
Sätt det hela tillsammans, och du borde kunna förstå varför den här koden fungerar:
var standard: Objekt = laserColor: röd, personlighet: "ondskan" för (var-tangent: String i standardvärden) if (! conf [key]) conf [key] = standardvärden [tangent];
Ge mig en rop i kommentarerna om du behöver en hand!
För en ännu snabbare version, prova det här:
funktion generateRobot (conf: Object = null): Robot var conf: Objekt = conf || ; var standard: Objekt = laserColor: röd, personlighet: "ondskan" för (var-tangent: String i standardvärden) conf [key] = conf [key] || defaults [nyckel];
Förändringen i Linje 1 (och Ny Linje 2) betyder att även conf
objektet är valfritt, så du kan bara ringa generateRobot ()
. (Självklart måste du ändra koden för att hantera de värden som för närvarande inte har standardinställningar.)
Som jag nämnde ovan kan IDE inte ge dig några tips om vilka parametrar en funktion förväntar sig om den funktionen använder ett konfigurationsobjekt. Det här är en stor nackdel, eftersom det kan göra din kod verkligen svår att använda. du måste komma ihåg vilka parametrar som går in i conf
objekt, liksom alla deras namn och typer.
Men vi kan fortfarande visa denna information till kodaren när det behövs. vi måste bara göra det manuellt, så här:
/ ** * Generera en robot, baserat på de angivna parametrarna. * @param conf Konfigurationsobjekt. Förväntar sig: * armar (int) Antal vapenrobotar ska ha. * personlighet (String) Personlighet av robot. Kan vara "ont" eller "bra". Standard till "ondskan". * Material (String) Vad roboten ska göras av. Kan vara "stål" eller "trä" vid denna tidpunkt. * LaserColor (String) Färg på robotens laser. Standard till "röd". * Voice (String) Vocal stylings av robot. För närvarande inte genomförd. * @return Den färdiga roboten. * / funktion generateRobot (conf: Object): Robot //
Om jag börjar skriva ett samtal till den här funktionen i FlashDevelop (mitt IDE val) ser jag detta:
Visst, det är lite ont att hålla det manuellt uppdaterat, men i många fall är det värt det.
Jag hävdar inte att du ska använda ett konfigurationsobjekt för varje enskild funktion du skapar från och med nu; Tänk bara på det som ett annat användbart verktyg i din arsenal.
Personligen tycker jag att det är ett särskilt användbart mönster när jag bygger det första utkastet till en uppsättning klasser som alla behöver arbeta tillsammans. Den extra flexibiliteten hos a conf
ger mig så mycket mer flexibilitet, frigör mig till zip runt alla olika funktioner och ändrar hur de ringer varandra, utan att oroa dig för att bryta koden genom att infoga eller ta bort en parameter.
Kom ihåg fördelarna:
Det finns nackdelar med att använda enkla objekt som jag har, dock - speciellt om du gör det i ett projekt som är förbi prototyperingsfasen. Kolla in de stora kommentarerna nedan för mer information!