Programovanie

Vytvorte vymenované konštanty v prostredí Java

Sada „vymenovateľných konštánt“ je usporiadaná zbierka konštánt, ktoré je možné spočítať, napríklad čísla. Táto vlastnosť vám umožňuje používať ich ako čísla na indexovanie poľa alebo ich môžete použiť ako premennú indexu v cykle for. V Jave sú takéto objekty najčastejšie známe ako „vymenované konštanty“.

Používanie vymenovaných konštánt môže zvýšiť čitateľnosť kódu. Možno budete napríklad chcieť definovať nový údajový typ s názvom Farba s konštantami ČERVENÁ, ZELENÁ a MODRÁ ako svoje možné hodnoty. Cieľom je mať Color ako atribút iných objektov, ktoré vytvoríte, napríklad Car objekty:

 trieda Auto {farebná farba; ...} 

Potom môžete napísať jasný a čitateľný kód, napríklad takto:

 myCar.color = ČERVENÉ; 

namiesto niečoho ako:

 myCar.color = 3; 

Ešte dôležitejším atribútom vymenovaných konštánt v jazykoch ako Pascal je to, že sú bezpečné pre typ. Inými slovami, atribútu farieb nie je možné priradiť neplatnú farbu - musí to byť vždy ČERVENÁ, ZELENÁ alebo MODRÁ. Naproti tomu, ak bola premenná farby int, môžete jej priradiť akékoľvek platné celé číslo, aj keď toto číslo nepredstavovalo platnú farbu.

Tento článok poskytuje šablónu na vytváranie vymenovaných konštánt, ktoré sú:

  • Typ bezpečný
  • Pre tlač
  • Objednané, na použitie ako index
  • Prepojené, na slučkovanie dopredu alebo dozadu
  • Vyčísliteľné

V budúcom článku sa dozviete, ako rozšíriť vymenované konštanty tak, aby sa implementovalo stavovo závislé správanie.

Prečo nevyužiť statické finále?

Spoločný mechanizmus pre vymenované konštanty používa statické premenné final int, napríklad takto:

 statický konečný int ČERVENÉ = 0; statický konečný int ZELENÁ = 1; statický konečný int MODRÝ = 2; ... 

Statické finále je užitočné

Pretože sú konečné, hodnoty sú stále a nemenné. Pretože sú statické, vytvárajú sa iba raz pre triedu alebo rozhranie, v ktorom sú definované, namiesto jedného pre každý objekt. A keďže ide o celočíselné premenné, je možné ich vymenovať a použiť ako index.

Napríklad môžete napísať slučku a vytvoriť zoznam obľúbených farieb zákazníka:

 for (int i = 0; ...) {if (customerLikesColor (i)) {favouriteColors.add (i); }} 

Pomocou premenných môžete tiež indexovať do poľa alebo vektora, aby ste získali hodnotu spojenú s farbou. Predpokladajme napríklad, že máte stolovú hru, ktorá má pre každého hráča rôzne sfarbené figúrky. Povedzme, že pre každý farebný kúsok máte bitmapu a tzv. Metódu display () ktorý skopíruje túto bitmapu do aktuálneho umiestnenia. Jedným zo spôsobov, ako dať kúsok na hraciu plochu, môže byť asi toto:

PiecePicture redPiece = nový PiecePicture (ČERVENÝ); PiecePicture greenPiece = nový PiecePicture (ZELENÝ); PiecePicture bluePiece = nový PiecePicture (MODRÝ);

void placePiece (int umiestnenie, int farba) {setPosition (umiestnenie); if (color == RED) {display (redPiece); } else if (farba == ZELENÁ) {display (greenPiece); } else {display (bluePiece); }}

Ale pomocou celočíselných hodnôt na indexovanie do poľa častí môžete kód zjednodušiť na:

 PiecePicture [] piece = {new PiecePicture (RED), new PiecePicture (GREEN), new PiecePicture (BLUE)}; void placePiece (int umiestnenie, int farba) {setPosition (umiestnenie); displej (kus [farba]); } 

Hlavnou výhodou statických konečných celých čísel je schopnosť opakovať sa v rozsahu konštánt a indexovať do poľa alebo vektora. A keď počet možností rastie, efekt zjednodušenia je ešte väčší.

Ale statické finále je riskantné

Stále existuje niekoľko nevýhod použitia statických konečných celých čísel. Hlavnou nevýhodou je nedostatok bezpečnosti typu. Akékoľvek celé číslo, ktoré je vypočítané alebo načítané, je možné použiť ako „farbu“ bez ohľadu na to, či to má zmysel. Môžete prechádzať slučkami priamo za koniec definovaných konštánt alebo prestať pokrývať všetky, čo sa môže ľahko stať, ak pridáte alebo odstránite konštantu zo zoznamu, ale zabudnete upraviť index slučiek.

Napríklad vaša slučka preferencií farieb môže znieť takto:

 pre (int i = 0; i <= MODRÉ; i ++) {if (customerLikesColor (i)) {obľúbenéColors.add (i); }} 

Neskôr môžete pridať novú farbu:

 statický konečný int ČERVENÉ = 0; statický konečný int ZELENÁ = 1; statický konečný int MODRÝ = 2; statický konečný int MAGENTA = 3; 

Alebo môžete jednu odstrániť:

 statický konečný int ČERVENÉ = 0; statický konečný int MODRÝ = 1; 

V obidvoch prípadoch nebude program fungovať správne. Ak odstránite farbu, zobrazí sa runtime chyba, ktorá upozorní na problém. Ak pridáte farbu, nebudete mať vôbec žiadnu chybu - program jednoducho nedokáže pokryť všetky možnosti farieb.

Ďalším nedostatkom je nedostatok čitateľného identifikátora. Ak na zobrazenie aktuálnej voľby farieb použijete výstup zo schránky alebo konzoly, získate číslo. To ladenie pekne sťažuje.

Problémy s vytváraním čitateľného identifikátora sa niekedy riešia pomocou statických koncových konštánt reťazca, napríklad takto:

 statický konečný reťazec ČERVENÝ = "červený" .intern (); ... 

Pomocou stážista () metóda zaručuje, že vo vnútornej oblasti reťazcov je iba jeden reťazec s týmto obsahom. Ale pre stážista () aby bol efektívny, musí ho používať každý reťazec alebo premenná reťazca, ktorá sa kedy porovnáva s ČERVENOU. Ani potom statické konečné reťazce neumožňujú opakovanie alebo indexovanie do poľa a stále neriešia otázku bezpečnosti typu.

Bezpečnosť typu

Problém so statickými koncovými celými číslami je, že premenné, ktoré ich používajú, sú vo svojej podstate neobmedzené. Sú to premenné typu int, čo znamená, že môžu obsahovať akékoľvek celé číslo, nielen konštanty, ktoré mali obsahovať. Cieľom je definovať premennú typu Color, aby ste vždy, keď je tejto premennej priradená neplatná hodnota, dostali skôr chybu kompilácie ako chybu za behu.

Elegantné riešenie prinieslo článok Philipa Bishopa v JavaWorld „Konstanty Typesafe v C ++ a Java“.

Myšlienka je naozaj jednoduchá (akonáhle ju uvidíte!):

verejná finálna trieda Farba {// finálna trieda !! private Color () {} // súkromný konštruktor !!

verejná statická konečná Farba ČERVENÁ = nová Farba (); verejná statická konečná Farba ZELENÁ = nová Farba (); verejná statická konečná Farba MODRÁ = nová Farba (); }

Pretože je trieda definovaná ako konečná, nemožno ju podtriediť. Z toho nebudú vytvorené ďalšie triedy. Pretože je konštruktor súkromný, iné metódy nemôžu použiť triedu na vytvorenie nových objektov. Jediné objekty, ktoré budú kedy vytvorené s touto triedou, sú statické objekty, ktoré si trieda vytvorí pre seba pri prvom použití odkazu na triedu! Táto implementácia je variáciou Singletonovho vzoru, ktorý obmedzuje triedu na vopred definovaný počet inštancií. Tento vzor môžete použiť na vytvorenie presne jednej triedy kedykoľvek potrebujete Singleton, alebo ho môžete použiť tak, ako je to znázornené na vytvorenie pevného počtu inštancií. (Singletonov vzor je definovaný v knihe Dizajnové vzory: Prvky opakovane použiteľného objektovo orientovaného softvéru Gamma, Helm, Johnson a Vlissides, Addison-Wesley, 1995. Odkaz na túto knihu nájdete v časti Zdroje.)

Mätúca časť tejto definície triedy je, že trieda používa sám vytvárať nové objekty. Prvýkrát, keď odkazujete na ČERVENÉ, neexistuje. Ale akt prístupu k triede, v ktorej je RED definovaná, spôsobí jej vytvorenie spolu s ostatnými konštantami. Je pravda, že tento druh rekurzívneho odkazu je dosť ťažké vizualizovať. Výhodou je ale úplná bezpečnosť typu. Premennej typu Farba nikdy nemožno priradiť nič iné ako ČERVENÉ, ZELENÉ alebo MODRÉ objekty, ktoré Farba trieda vytvára.

Identifikátory

Prvým vylepšením triedy konštantných typov s vymenovanými typmi je vytvorenie reťazcovej reprezentácie konštánt. Chcete byť schopní vytvoriť čitateľnú verziu hodnoty pomocou tohto riadku:

 System.out.println (myColor); 

Kedykoľvek prenášate objekt na výstupný prúd znakov, napríklad System.out, a vždy, keď zreťazíte objekt na reťazec, Java automaticky vyvolá znak natiahnuť() metóda pre tento objekt. To je dobrý dôvod na definovanie a natiahnuť() metóda pre každú novú triedu, ktorú vytvoríte.

Ak trieda nemá a natiahnuť() metódou sa kontroluje hierarchia dedičstva, kým sa nenájde. Na vrchole hierarchie je natiahnuť() metóda v Objekt trieda vráti názov triedy. Takže natiahnuť() metóda vždy má niektoré význam, ale väčšinou nebude predvolená metóda veľmi užitočná.

Tu je úprava súboru Farba triedy, ktorá poskytuje užitočné natiahnuť() metóda:

verejná záverečná trieda Farba { súkromné ​​ID reťazca; súkromná farba (Reťazec anID) {this.id = anID; } public String toString () {return this.id; }

verejná statická konečná Farba ČERVENÁ = nová Farba (

„Červená“

); verejná statická konečná Farba ZELENÁ = nová Farba (

"Zelená"

); verejná statická konečná Farba MODRÁ = nová Farba (

"Modrá"

); }

Táto verzia pridáva súkromnú reťazcovú premennú (id). Konštruktor bol upravený tak, aby prevzal argument String a uložil ho ako ID objektu. The natiahnuť() metóda potom vráti ID objektu.

Jeden trik, ktorým môžete vyvolať natiahnuť() metóda využíva skutočnosť, že je automaticky vyvolaná, keď je objekt zreťazený na reťazec. To znamená, že môžete vložiť názov objektu do dialógového okna zreťazením na nulový reťazec pomocou nasledujúceho riadku:

 textField1.setText ("" + mojaColor); 

Pokiaľ náhodou nemilujete všetky zátvorky v Lispi, zistíte, že je o niečo čitateľnejšia ako alternatíva:

 textField1.setText (myColor.toString ()); 

Ľahšie je tiež zaistiť správny počet záverečných zátvoriek!

Objednávanie a indexácia

Ďalšou otázkou je, ako indexovať do vektora alebo poľa pomocou členov prvku

Farba

trieda. Mechanizmom bude pridelenie poradového čísla každej konštante triedy a odkazovanie na ňu pomocou atribútu

.ord

, Páči sa ti to:

 void placePiece (int umiestnenie, int farba) {setPosition (umiestnenie); displej (kus [farba.ord]); } 

Aj keď pripútanie .ord previesť odkaz na farba do počtu nie je nijako zvlášť pekný, nie je ani strašne dotieravý. Zdá sa to ako celkom rozumný kompromis pre typicky bezpečné konštanty.

Poradové čísla sa prideľujú takto:

verejná konečná trieda Farba {súkromné ​​ID reťazca; verejny konecny ​​int ord;private static int upperBound = 0; private Color (String anID) {this.id = anID; this.ord = upperBound ++; } public String toString () {return this.id; } public static int size () {return upperBound; }

verejná statická konečná farba ČERVENÁ = nová farba („červená“); verejná statická konečná Farba ZELENÁ = nová Farba („Zelená“); verejná statická konečná Farba MODRÁ = nová Farba („modrá“); }

Tento kód používa novú definíciu JDK verzie 1.1 premennej „blank final“ - premennej, ktorej je priradená hodnota iba raz a iba raz. Tento mechanizmus umožňuje každému objektu mať svoju vlastnú nestatickú konečnú premennú, ord, ktoré budú priradené raz pri vytváraní objektu a ktoré potom zostanú nemenné. Statická premenná Horná hranica sleduje ďalší nepoužitý index v zbierke. Táto hodnota sa stáva ord atribút pri vytváraní objektu, po ktorom sa zvýši horná hranica.

Pre kompatibilitu s Vektor trieda, metóda veľkosť () je definované na vrátenie počtu konštánt, ktoré boli definované v tejto triede (čo je to isté ako horná hranica).

Purista by sa mohol rozhodnúť, že premenná ord by mali byť súkromné ​​a pomenovaná metóda ord () by ho mal vrátiť - ak nie, metódu s názvom getOrd (). Prikláňam sa k priamemu prístupu k atribútu, a to z dvoch dôvodov. Prvým je, že koncept ordinálu je jednoznačne konceptom int. Je malá pravdepodobnosť, že by sa implementácia niekedy zmenila. Druhým dôvodom je to, čo skutočne máte chcieť je schopnosť používať objekt, akoby to bol int, ako by ste mohli v jazyku, ako je Pascal. Môžete napríklad použiť tento atribút farba indexovať pole. Na to však nemôžete priamo použiť objekt Java. Naozaj by ste chceli povedať:

 displej (kus [farba]); // žiaduce, ale nefunguje 

Ale to nemôžete urobiť. Minimálna zmena nevyhnutná na získanie požadovaného kódu je prístup k atribútu, napríklad takto:

 displej (kus [color.ord]); // najbližšie k želanému 

namiesto zdĺhavej alternatívy:

 displej (kus [color.ord ()]); // zátvorky navyše 

alebo ešte dlhšie:

 display (piece [color.getOrd ()]); // zátvorky a text navyše 

Eiffelov jazyk používa rovnakú syntax pre prístup k atribútom a pre vyvolanie metód. To by bol ideál. Vzhľadom na nutnosť výberu jedného alebo druhého som však prešiel k prístupu ord ako atribút. Pri troche šťastia identifikátor ord v dôsledku opakovania sa stane tak známym, že jeho používanie sa bude javiť rovnako prirodzené ako písanie int. (Akokoľvek prirodzené.)

Opakovanie

Ďalším krokom je možnosť iterácie cez konštanty triedy. Chcete byť schopní slučky od začiatku do konca:

 for (Color c = Color.first (); c! = null; c = c.next ()) {...} 

alebo od konca späť na začiatok:

 pre (Farba c = Color.last (); c! = null; c = c.prev ()) {...} 

Tieto úpravy používajú statické premenné na sledovanie posledného vytvoreného objektu a na jeho prepojenie s nasledujúcim objektom:

$config[zx-auto] not found$config[zx-overlay] not found