Programmēšana

Lai iegūtu drošāku un tīrāku kodu, izmantojiet nemainīgus veidus

Šajā apmācībā tiks paplašināta ideja par uzskaitītas konstantes kā aprakstīts Ērika Ārmstronga "Izveidot uzskaitītas konstantes Java." Pirms ieslīgt šajā rakstā, es ļoti iesaku izlasīt šo rakstu, jo es pieņemu, ka jums ir zināmi jēdzieni, kas saistīti ar uzskaitītajām konstantēm, un es izvērsīšu dažus no Ērika iesniegtajiem koda piemēriem.

Konstantu jēdziens

Nodarbojoties ar uzskaitītajām konstantēm, es apspriedīšu uzskaitīti koncepcijas daļa raksta beigās. Pagaidām mēs koncentrēsimies tikai uz nemainīgs aspekts. Konstanti būtībā ir mainīgie, kuru vērtība nevar mainīties. C / C ++ - atslēgvārds konst tiek izmantots, lai deklarētu šos nemainīgos mainīgos. Java valodā jūs izmantojat atslēgvārdu galīgais. Tomēr šeit ieviestais rīks nav vienkārši primitīvs mainīgais; tas ir faktisks objekta gadījums. Objekta gadījumi nav maināmi un nemaināmi - to iekšējo stāvokli nedrīkst mainīt. Tas ir līdzīgs vieninieka paraugam, kur klasē var būt tikai viens eksemplārs; tomēr šajā gadījumā klasei var būt tikai ierobežots un iepriekš noteikts gadījumu kopums.

Galvenie konstantu izmantošanas iemesli ir skaidrība un drošība. Piemēram, šāds koda fragments nav pašsaprotams:

 public void setColor (int x) {...} public void someMethod () {setColor (5); } 

Pēc šī koda mēs varam pārliecināties, ka tiek iestatīta krāsa. Bet kādu krāsu pārstāv 5? Ja šo kodu būtu uzrakstījis kāds no retajiem programmētājiem, kurš komentē viņa vai viņas darbu, atbildi mēs varētu atrast faila augšdaļā. Bet, visticamāk, mums būs jāapraksta daži veci projekta dokumenti (ja tādi vispār pastāv), lai tos paskaidrotu.

Skaidrāks risinājums ir piešķirt vērtībai 5 vērtību mainīgajam ar jēgpilnu nosaukumu. Piemēram:

 publiskais statiskais fināls int RED = 5; public void someMethod () {setColor (RED); } 

Tagad mēs varam uzreiz pateikt, kas notiek ar kodu. Krāsa tiek iestatīta uz sarkanu. Tas ir daudz tīrāk, bet vai tas ir drošāk? Ko darīt, ja cits kodētājs sajaukt un paziņo dažādas vērtības, piemēram:

publiskais statiskais fināls int RED = 3; publiskais statiskais fināls int ZAĻA = 5; 

Tagad mums ir divas problēmas. Pirmkārt, SARKANS vairs nav iestatīta uz pareizo vērtību. Otrkārt, sarkanās krāsas vērtību attēlo mainīgais mainīgais ZAĻA. Varbūt visbriesmīgākais ir tas, ka šis kods tiks sastādīts lieliski, un kļūda var netikt atklāta, kamēr produkts nav nosūtīts.

Mēs varam novērst šo problēmu, izveidojot noteiktu krāsu klasi:

publiskā klase Krāsa {public static final int RED = 5; publiskais statiskais fināls int ZAĻA = 7; } 

Pēc tam, izmantojot dokumentāciju un koda pārskatīšanu, mēs iesakām programmētājiem to izmantot šādi:

 public void someMethod () {setColor (Color.RED); } 

Es saku iedrošināt, jo šī koda saraksta dizains neļauj mums piespiest kodētāju ievērot; kods joprojām tiks sastādīts, pat ja viss nav kārtībā. Tādējādi, kaut arī tas ir nedaudz drošāk, tas nav pilnīgi droši. Kaut arī programmētāji vajadzētu izmantot Krāsa klasē, viņiem to neprasa. Programmētāji ļoti viegli varēja uzrakstīt un apkopot šādu kodu:

 setColor (3498910); 

Vai setColor metode atzīst šo lielo skaitu par krāsu? Visticamāk ne. Tātad, kā mēs varam pasargāt sevi no šiem negodīgiem programmētājiem? Tur palīgā nāk konstanti.

Mēs sākam no jauna definēt metodes parakstu:

 public void setColor (Krāsa x) {...} 

Tagad programmētāji nevar ievadīt patvaļīgu vesela skaitļa vērtību. Viņi ir spiesti norādīt derīgu Krāsa objekts. Īstenošanas piemērs varētu izskatīties šādi:

 public void someMethod () {setColor (new Color ("Red")); } 

Mēs joprojām strādājam ar tīru un salasāmu kodu, un mēs esam daudz tuvāk absolūtas drošības sasniegšanai. Bet mēs vēl neesam tur. Programmētājam joprojām ir vietas, lai izpostītu, un viņš var patvaļīgi izveidot jaunas krāsas, piemēram:

 public void someMethod () {setColor (new Color ("Sveiki, mani sauc Teds.")); } 

Mēs novēršam šo situāciju, veicot Krāsa klase ir nemaināma un instantācijas slēpšana no programmētāja. Mēs izgatavojam katru dažādu krāsu (sarkanu, zaļu, zilu) vienkrāsainu. Tas tiek panākts, padarot konstruktoru privātu un pēc tam publiskos rokturus pakļaujot ierobežotam un precīzi definētam gadījumu sarakstam:

public class Krāsa {private Color () {} public static final Krāsa RED = new Color (); public static final Krāsa ZAĻA = jauna Krāsa (); public static final Krāsa ZILA = jauna krāsa (); } 

Šajā kodeksā mēs beidzot esam sasnieguši absolūtu drošību. Programmētājs nevar izgatavot viltus krāsas. Drīkst izmantot tikai noteiktās krāsas; pretējā gadījumā programma netiks apkopota. Tagad mūsu ieviešana izskatās šādi:

 public void someMethod () {setColor (Color.RED); } 

Neatlaidība

Labi, tagad mums ir tīrs un drošs veids, kā tikt galā ar nemainīgiem tipiem. Mēs varam izveidot objektu ar krāsu atribūtu un būt pārliecināti, ka krāsas vērtība vienmēr būs derīga. Bet ko tad, ja mēs vēlamies šo objektu saglabāt datu bāzē vai ierakstīt failā? Kā mēs saglabājam krāsas vērtību? Šie tipi mums jāpiesaista vērtībām.

Iekš JavaWorld rakstā, kas minēts iepriekš, Ēriks Ārmstrongs izmantoja virknes vērtības. Virkņu izmantošana nodrošina papildu bonusu, kas dod jums kaut ko nozīmīgu, lai atgrieztos toString () metodi, kas padara izvades atkļūdošanu ļoti skaidru.

Stīgu glabāšana tomēr var būt dārga. Veselam skaitlim ir nepieciešami 32 biti, lai saglabātu tā vērtību, bet virknei - 16 biti katram rakstzīmei (pateicoties Unicode atbalstam). Piemēram, numuru 49858712 var saglabāt 32 bitos, bet virkni TIRKĪZS būtu nepieciešami 144 biti. Ja jūs glabājat tūkstošiem objektu ar krāsu atribūtiem, šī salīdzinoši nelielā bitu atšķirība (šajā gadījumā starp 32 un 144) var ātri palielināties. Tāpēc izmantosim veselu skaitļu vērtības. Kāds ir šīs problēmas risinājums? Mēs saglabāsim virkņu vērtības, jo tās ir svarīgas prezentācijai, taču mēs tās neglabāsim.

Java versijas, sākot ar 1.1. Versiju, var automātiski seriālizēt objektus, ja vien tās ievieš Serializējams interfeiss. Lai neļautu Java glabāt svešus datus, šādi mainīgie ir jādeklarē ar īslaicīgs atslēgvārds. Tātad, lai saglabātu veselu skaitļu vērtības, nesaglabājot virknes attēlojumu, mēs pasludinām virknes atribūtu par pārejošu. Lūk, jaunā klase, kā arī atribūti veselam skaitlim un virknei:

publiskā klase Color īsteno java.io.Serializable {private int value; privāts pārejošs virknes nosaukums; publiskā statiskā galīgā krāsa RED = jauna krāsa (0, "sarkana"); publiskā statiskā galīgā krāsa ZILA = jauna krāsa (1, "Zila"); public static final Krāsa ZAĻA = jauna krāsa (2, "Zaļa"); privāta krāsa (int vērtība, virknes nosaukums) {this.value = value; this.name = nosaukums; } public int getValue () {return value; } public String toString () {atgriešanās nosaukums; }} 

Tagad mēs varam efektīvi uzglabāt nemainīgā tipa gadījumus Krāsa. Bet kā ar to atjaunošanu? Tas būs nedaudz grūts. Pirms dodamies tālāk, izvērsīsim to sistēmā, kas tiks galā ar visiem iepriekšminētajiem trūkumiem, ļaujot mums koncentrēties uz vienkāršo jautājumu noteikšanas veidu.

Pastāvīgā tipa ietvars

Ar mūsu stingro izpratni par nemainīgiem tipiem es tagad varu izmantot šī mēneša rīku. Rīks tiek saukts Tips un tā ir vienkārša abstrakta klase. Viss, kas jums jādara, ir izveidot ļoti vienkārša apakšklase, un jums ir pilna Featured konstanta tipa bibliotēka. Lūk, ko mūsu Krāsa klase izskatīsies kā tagad:

publiskā klase Krāsu paplašinājums Tips {aizsargāta krāsa (int vērtība, virknes desc) {super (vērtība, desc); } public static final Color RED = jauna krāsa (0, "sarkana"); public static final Krāsa ZILA = jauna krāsa (1, "Zila"); public static final Krāsa ZAĻA = jauna krāsa (2, "Zaļa"); } 

The Krāsa klase sastāv tikai no konstruktora un dažām publiski pieejamām instancēm. Visa līdz šim apspriestā loģika tiks definēta un ieviesta superklasē Tips; mēs pievienosim vairāk, kad ejam līdzi. Lūk, ko Tips izskatās līdz šim:

public class Type ievieš java.io.Serializable {private int value; privāts pārejošs virknes nosaukums; aizsargāts tips (int vērtība, virknes nosaukums) {this.value = value; this.name = nosaukums; } public int getValue () {return value; } public String toString () {atgriešanās nosaukums; }} 

Atpakaļ pie neatlaidības

Ar savu jauno regulējumu rokās mēs varam turpināt tur, kur mēs neatlaidības diskusijā pārtraucām. Atcerieties, ka mēs varam saglabāt savus tipus, saglabājot to veselās vērtības, taču tagad mēs tos vēlamies atjaunot. Tam būs nepieciešams: uzmeklēšana - reverss aprēķins, lai atrastu objekta gadījumu, pamatojoties uz tā vērtību. Lai veiktu meklēšanu, mums ir nepieciešams veids, kā uzskaitīt visus iespējamos veidus.

Ērika rakstā viņš īstenoja pats savu uzskaitījumu, ieviešot konstantes kā mezglus saistītā sarakstā. Es atteikšos no šīs sarežģītības un tā vietā izmantoju vienkāršu hashtable. Hash atslēga būs visa veida vērtības (iesaiņotas Vesels skaitlis objekts), un hash vērtība būs atsauce uz tipa instanci. Piemēram, ZAĻA gadījums Krāsa tiktu glabāti šādi:

 hashtable.put (jauns Integer (GREEN.getValue ()), GREEN); 

Protams, mēs nevēlamies to rakstīt katram iespējamajam tipam. Varētu būt simtiem dažādu vērtību, tādējādi radot mašīnrakstīšanas murgu un atverot durvis dažām nepatīkamām problēmām - jūs, iespējams, aizmirstat ievietot kādu no vērtībām hashtable un pēc tam nevarēsiet to meklēt, piemēram, vēlāk. Tāpēc mēs pasludināsim globālu hashtable Tips un pārveidojiet konstruktoru, lai saglabātu kartēšanu pēc izveides:

 private static final Hashtable tips = new Hashtable (); aizsargāts tips (int vērtība, String desc) {this.value = vērtība; this.desc = desc; tips.put (jauns Integer (vērtība), šis); } 

Bet tas rada problēmu. Ja mums ir apakšklase, ko sauc Krāsa, kuram ir tips (tas ir, Zaļš) ar vērtību 5, un pēc tam mēs izveidojam citu apakšklasi, ko sauc Ēna, kuram ir arī tips (tas ir Tumšs) ar vērtību 5, tikai viena no tām tiks glabāta hashtable - pēdējā, kas tiks instantificēta.

Lai no tā izvairītos, mums ir jāuzglabā tāda veida rokturis, kura pamatā ir ne tikai tā vērtība, bet arī tā vērtība klasē. Izveidosim jaunu metodi tipu atsauču glabāšanai. Mēs izmantosim hashtable hashtable. Iekšējā hashtable būs vērtību kartēšana tipiem katrai konkrētai apakšklasei (Krāsa, Ēna, un tā tālāk). Ārējā hashtable būs apakšklases kartēšana uz iekšējām tabulām.

Šī rutīna vispirms mēģinās iegūt iekšējo galdu no ārējā galda. Ja tā saņem nulli, iekšējā tabula vēl nepastāv. Tātad, mēs izveidojam jaunu iekšējo galdu un ievietojam to ārējā galdā. Pēc tam mēs pievienojam vērtības / veida kartēšanu iekšējai tabulai, un mēs esam gatavi. Šeit ir kods:

 private void storeType (Type type) {String className = type.getClass (). getName (); Hashtable vērtības; sinhronizēti (veidi) // izvairieties no sacīkšu nosacījumiem, lai izveidotu iekšējo tabulu {vērtības = (Hashtable) tips.get (className); if (vērtības == nulle) {vērtības = jauna hashtable (); tips.put (className, vērtības); }} values.put (jauns Integer (type.getValue ()), tips); } 

Un šeit ir jaunā konstruktora versija:

 aizsargāts tips (int vērtība, String desc) {this.value = vērtība; this.desc = desc; storeType (šis); } 

Tagad, kad mēs glabājam veidu un vērtību ceļa karti, mēs varam veikt meklēšanu un tādējādi atjaunot instanci, pamatojoties uz vērtību. Uzmeklēšanai nepieciešamas divas lietas: mērķa apakšklases identitāte un veselā skaitļa vērtība. Izmantojot šo informāciju, mēs varam iegūt iekšējo tabulu un atrast rokturi atbilstošā tipa gadījumam. Šeit ir kods:

 public static tips getByValue (Class classRef, int value) {Type type = null; Virkne className = classRef.getName (); Hashtable vērtības = (Hashtable) tips.get (className); if (vērtības! = null) {type = (Type) vērtības.get (new Integer (vērtība)); } return (tips); } 

Tādējādi vērtības atjaunošana ir tikpat vienkārša kā šī (ņemiet vērā, ka atgriešanās vērtība ir jāizlieto):

 int vērtība = // lasīt no faila, datu bāzes utt. Krāsu fons = (ColorType) Type.findByValue (ColorType.class, value); 

Uzskaitot veidus

Pateicoties mūsu hashtable-of-hashtables organizācijai, ir ārkārtīgi vienkārši atklāt uzskaitīšanas funkcionalitāti, ko piedāvā Ēriks. Vienīgais brīdinājums ir tas, ka šķirošana, ko piedāvā Ērika dizains, netiek garantēta. Ja izmantojat Java 2, iekšējās atsauces tabulas var aizstāt ar sakārtoto karti. Bet, kā jau teicu šīs slejas sākumā, šobrīd es rūpējos tikai par JDK 1.1 versiju.

Vienīgā loģika, kas nepieciešama veidu uzskaitīšanai, ir iekšējās tabulas izgūšana un tās elementu saraksta atgriešana. Ja iekšējā tabula nepastāv, mēs vienkārši atgriežam nulli. Šeit ir visa metode:

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