Programmēšana

Vairāk par getters un seters

Tas ir 25 gadus vecs objektorientētas (OO) dizaina princips, ka nevajadzētu pakļaut objekta ieviešanu citām programmas klasēm. Programmu ir nevajadzīgi grūti uzturēt, pakļaujot ieviešanu, galvenokārt tāpēc, ka mainot objektu, kas atklāj tā ieviešanas pilnvaras, tiek mainītas visas klases, kuras izmanto objektu.

Diemžēl getter / setter idioma, ko daudzi programmētāji domā par orientētu uz objektu, pīķos pārkāpj šo OO pamatprincipu. Apsveriet a. Piemēru Nauda klase, kurai ir a getValue () metode, kas atgriež "vērtību" dolāros. Visā programmā jums būs šāds kods:

dubultā kārtībaKopā; Naudas summa = ...; //... orderTotal + = summa.getValue (); // orderTotal jābūt dolāros

Šīs pieejas problēma ir tā, ka iepriekšējais kods liek lielu pieņēmumu par to, kā Nauda klase ir ieviesta (ka "vērtība" tiek saglabāta a dubultā). Kods, kas padara ieviešanas pieņēmumus pārtraukumus, mainoties ieviešanai. Ja, piemēram, jums jāpaplašina sava lietojumprogramma, lai atbalstītu citas valūtas, nevis dolārus, tad getValue () neatgriež neko jēdzīgu. Jūs varētu pievienot a getCurrency (), bet tas padarītu visu kodu, kas ieskauj getValue () zvaniet daudz sarežģītāk, it īpaši, ja jūs joprojām izmantojat getter / setter stratēģiju, lai iegūtu darbu veikšanai nepieciešamo informāciju. Tipiska (nepilnīga) ieviešana varētu izskatīties šādi:

Naudas summa = ...; //... vērtība = summa.getValue (); valūta = summa.getCurrency (); konversija = CurrencyTable.getConversionFactor (valūta, USDOLLARS); kopējā + = vērtība * konversija; //...

Šīs izmaiņas ir pārāk sarežģītas, lai ar tām varētu rīkoties automatizēta atjaunošana. Turklāt jums vajadzēs veikt šāda veida izmaiņas visur savā kodā.

Šīs problēmas biznesa loģikas līmeņa risinājums ir veikt darbu objektā, kuram ir šī darba veikšanai nepieciešamā informācija. Tā vietā, lai iegūtu "vērtību", lai ar to veiktu kādu ārēju darbību, jums vajadzētu būt Nauda klase veic visas ar naudu saistītās operācijas, ieskaitot valūtas konvertāciju. Pareizi strukturēts objekts kopējo summu apstrādās šādi:

Nauda kopā = ...; Naudas summa = ...; total.increaseBy (summa); 

The pievienot () metode noskaidro operanda valūtu, veic visu nepieciešamo valūtas konvertāciju (kas ir pareizi operācija naudu), un atjauniniet kopējo. Ja sākumā izmantojāt šo objekta stratēģiju, kurai ir informācija, tas veic darbu, tad jēdziens valūta varētu pievienot Nauda klase bez izmaiņām, kas nepieciešamas izmantotajā kodā Nauda objektiem. Tas ir, darbs, kas saistīts tikai ar dolāru pārveidošanu līdz starptautiskai ieviešanai, būtu koncentrēts vienā vietā: Nauda klasē.

Problēma

Lielākajai daļai programmētāju nav grūtību uztvert šo jēdzienu biznesa loģikas līmenī (lai gan pastāvīgi domāt šādā veidā var būt nepieciešamas zināmas pūles). Problēmas sāk parādīties, kad lietotāja saskarne (UI) nonāk attēlā. Problēma nav tā, ka, lai izveidotu lietotāja interfeisu, jūs nevarat izmantot tādas metodes kā to, ko tikko aprakstīju, bet gan tas, ka daudzi programmētāji ir bloķēti getter / setter mentalitātē, kad runa ir par lietotāja saskarnēm. Es vainoju šo problēmu ar principiāli procesuāliem koda veidošanas rīkiem, piemēram, Visual Basic un tā kloniem (ieskaitot Java lietotāja saskarnes veidotājus), kas liek jums izmantot šo procesuālo, labāku / iestatītāku domāšanas veidu.

(Novirze: Daži no jums izskaidros iepriekšējo paziņojumu un kliedza, ka VB ir balstīta uz svētīto Model-View-Controller (MVC) arhitektūru, tāpēc tas ir svēts. Paturiet prātā, ka MVC tika izstrādāts gandrīz pirms 30 gadiem. 1970. gados lielākais superdators bija līdzvērtīgs mūsdienu darbvirsmām. Lielākā daļa mašīnu (piemēram, DEC PDP-11) bija 16 bitu datori ar 64 KB atmiņu un pulksteņa ātrumu, kas mērīts desmitos megahercu. Jūsu lietotāja interfeiss, iespējams, bija kaudze štancētu karšu. Ja jums paveicās ar video termināli, iespējams, izmantojāt uz ASCII balstītu konsoles ievades / izvades (I / O) sistēmu. Pēdējo 30 gadu laikā mēs esam daudz iemācījušies. Pat Java Swing bija jāaizstāj MVC ar līdzīgu "atdalāmā modeļa" arhitektūru, galvenokārt tāpēc, ka tīrs MVC nepietiekami izolē lietotāja interfeisa un domēna modeļa slāņus.)

Tātad, definēsim problēmu īsumā:

Ja objekts var neatklāt ieviešanas informāciju (izmantojot get / set metodes vai ar jebkādiem citiem līdzekļiem), tad ir pamats uzskatīt, ka objektam kaut kā jāizveido savs lietotāja interfeiss. Tas ir, ja objekta atribūtu attēlošanas veids ir paslēpts no pārējās programmas, tad šos atribūtus nevarat izvilkt, lai izveidotu lietotāja interfeisu.

Starp citu, ņemiet vērā, ka jūs neslēpjat faktu, ka atribūts pastāv. (Es definēju atribūts, šeit, kā būtiska objekta īpašība.) Jūs zināt, ka an Darbinieks jābūt atalgojumam vai algas atribūtam, pretējā gadījumā tas nebūtu Darbinieks. (Tas būtu Persona, a Brīvprātīgais, a Klaidonis, vai kaut kas cits, kam nav algas.) Tas, ko jūs nezināt vai vēlaties zināt, ir tas, kā šī alga tiek attēlota objekta iekšienē. Tas varētu būt a dubultā, a Stīga, mērogots ilgivai binārā koda decimāls. Tas var būt atribūts "sintētisks" vai "atvasināts", kas tiek aprēķināts izpildes laikā (piemēram, no algas pakāpes vai amata nosaukuma vai iegūstot vērtību no datu bāzes). Lai gan get metode patiešām var slēpt daļu no šīs ieviešanas detaļas, kā mēs to redzējām ar Nauda Piemēram, tas nevar pietiekami daudz slēpt.

Tātad, kā objekts izveido pats savu lietotāja interfeisu un paliek uzturams? Tikai visvienkāršākie objekti var atbalstīt kaut ko līdzīgu a displayYourself () metodi. Reālistiskiem objektiem:

  • Parādiet sevi dažādos formātos (XML, SQL, ar komatiem atdalītas vērtības utt.).
  • Displejs atšķirīgs skati paši par sevi (vienā skatā var parādīt visus atribūtus; citā var parādīt tikai atribūtu apakškopu; bet trešajā atribūtus var parādīt citādi).
  • Parādīt sevi dažādās vidēs (klienta puse (JKomponents) un klientam (HTML), piemēram) un apstrādā gan ievadi, gan izvadi abās vidēs.

Daži no mana iepriekšējā getter / setter raksta lasītājiem nolēma secināt, ka es iestājos, ka objektam jāpievieno metodes, lai aptvertu visas šīs iespējas, taču šis "risinājums" acīmredzami ir bezjēdzīgs. Iegūtais smagā svara objekts ir ne tikai pārāk sarežģīts, bet tas būs nepārtraukti jāpārveido, lai rīkotos ar jaunām lietotāja saskarnes prasībām. Praktiski objekts vienkārši nevar izveidot sev visas iespējamās lietotāja saskarnes, ja cita iemesla dēļ, izņemot to, ka daudzi no šiem lietotāja interfeisiem nebija pat izveidoti, kad klase tika izveidota.

Veidojiet risinājumu

Šīs problēmas risinājums ir UI koda atdalīšana no pamatdarbības objekta, ievietojot to atsevišķā objektu klasē. Tas ir, jums vajadzētu atdalīt dažas funkcionalitātes, kas varēja būt objektā pilnībā atsevišķā objektā.

Šī objekta metožu bifurkācija parādās vairākos dizaina modeļos. Jūs, visticamāk, esat iepazinies ar stratēģiju, kas tiek izmantota dažādajām stratēģijām java.awt. Konteiners klases veikt maketu. Izkārtojuma problēmu jūs varētu atrisināt ar atvasināšanas risinājumu: FlowLayoutPanel, GridLayoutPanel, BorderLayoutPanelutt., taču tas prasa pārāk daudz klases un daudz dublētu kodu šajās klasēs. Viens smagā svara klases risinājums (metožu pievienošana Konteiners patīk layOutAsGrid (), layOutAsFlow ()utt.) ir arī nepraktiska, jo jūs nevarat modificēt koda avota kodu Konteiners vienkārši tāpēc, ka jums ir nepieciešams neatbalstīts izkārtojums. Stratēģijas modelī jūs izveidojat Stratēģija interfeiss (LayoutManager), ko īsteno vairāki Konkrēta stratēģija klases (FlowLayout, GridLayoututt.). Pēc tam jūs sakāt a Konteksts objekts (a Konteiners) kā kaut ko darīt, nokārtojot to a Stratēģija objekts. (Jūs nokārtojat a Konteiners a LayoutManager kas nosaka izkārtojuma stratēģiju.)

Builder modelis ir līdzīgs stratēģijai. Galvenā atšķirība ir tā, ka Celtnieks klase īsteno stratēģiju kaut kā konstruēšanai (piemēram, a JKomponents vai XML plūsma, kas attēlo objekta stāvokli). Celtnieks objekti parasti ražo savus produktus, izmantojot arī daudzpakāpju procesu. Tas ir, aicinājumi izmantot dažādas metodes Celtnieks ir nepieciešami, lai pabeigtu būvniecības procesu, un Celtnieks parasti nezina zvanu veikšanas secību vai to, cik reizes tiks izsaukta kāda no tās metodēm. Celtnieka vissvarīgākā īpašība ir tā, ka biznesa objekts (saukts par Konteksts) precīzi nezina, kas Celtnieks objekts tiek būvēts. Modelis izolē biznesa objektu no tā attēlojuma.

Labākais veids, kā pārliecināties, kā darbojas vienkāršs celtnieks, ir to aplūkot. Vispirms apskatīsim Konteksts, biznesa objekts, kuram jāatklāj lietotāja saskarne. 1. saraksts parāda vienkāršotu Darbinieks klasē. The Darbinieks ir nosaukums, id, un alga atribūti. (Šo nodarbību stumbri ir saraksta beigās, taču šie stublāji ir tikai vietas rezervētāji īstajai lietai. Es ceru, ka varat viegli iedomāties, kā šīs nodarbības darbotos.)

Šis konkrētais Konteksts izmanto to, ko es domāju par divvirzienu celtnieku. Klasiskā Četru celtnieku banda iet vienā virzienā (izeja), bet es esmu pievienojis arī a Celtnieks ka an Darbinieks objektu var izmantot, lai inicializētu sevi. Divas Celtnieks ir nepieciešamas saskarnes. The Darbinieks. Eksportētājs interfeiss (1. saraksta 8. rinda) apstrādā izejas virzienu. Tas nosaka saskarni ar Celtnieks objekts, kas konstruē pašreizējā objekta attēlojumu. The Darbinieks deleģē faktisko lietotāja saskarnes uzbūvi Celtnieks iekš eksports () metodi (31. rindā). The Celtnieks netiek nodots faktiskajiem laukiem, bet tā vietā tiek izmantots Stīgas nokārtot šo lauku attēlojumu.

Uzskaitīšana 1. Darbinieks: veidotāja konteksts

 1 importēt java.util.Locale; 2 3 publiskā klase Darbinieks 4 {privātais Vārda nosaukums; 5 privāts EmployeeId ID; 6 privātā naudas alga; 7 8 publiskā saskarne eksportētājs 9 {void addName (virknes nosaukums); 10 void addID (virknes ID); 11 void addSalary (String alga); 12} 13 14 publiskās saskarnes importētājs 15 {virkne nodrošinātName (); 16 virkne nodrošinātID (); 17 virkne nodrošināt algu (); 18 tukšs atvērts (); 19 spēkā neesošs aizvērt (); 20} 21 22 valsts darbinieks (importētāja celtnieks) 23 {celtnieks.atvērt (); 24 this.name = new Name (celtnieks.nodrošinātNosaukums ()); 25 this.id = new EmployeeId (celtnieks.provideID ()); 26 this.salary = jauna nauda (builder.provideSalary (), 27 jauna locale ("lv", "US")); 28 celtnieks.slēgt (); 29} 30 31 public void export (eksportētāja veidotājs) 32 {builder.addName (nosaukums.toString ()); 33 builder.addID (id.toString ()); 34 celtnieks.addSalary (alga.toString ()); 35} 36 37 //... 38 } 39 //---------------------------------------------------------------------- 40 // Vienības testa sīkumi 41 // 42 klases nosaukums 43 {privāta virknes vērtība; 44 public Name (String value) 45 {this.value = value; 46} 47 public String toString () {atgriešanās vērtība; }; 48} 49 50 klases EmployeeId 51 {privātā virknes vērtība; 52 public EmployeeId (virknes vērtība) 53 {this.value = value; 54} 55 public String toString () {atgriešanās vērtība; } 56} 57 58 klases nauda 59 {privāta Stīgas vērtība; 60 publiskā nauda (virknes vērtība, atrašanās vietas atrašanās vieta) 61 {this.value = value; 62} 63 public String toString () {return value; } 64} 

Apskatīsim piemēru. Šis kods veido 1. attēla lietotāja saskarni:

Darbinieku wilma = ...; JComponentExporter uiBuilder = jauns JComponentExporter (); // Izveidojiet celtnieku wilma.export (uiBuilder); // Izveidojiet lietotāja saskarni JComponent userInterface = uiBuilder.getJComponent (); //... someContainer.add (userInterface); 

2. Sarakstā ir redzams JComponentExporter. Kā redzat, viss ar lietotāja interfeisu saistītais kods ir koncentrēts Betona celtnieks ( JComponentExporter) un Konteksts ( Darbinieks) vada būvēšanas procesu, precīzi nezinot, ko tas veido.

Saraksts 2. Eksportēšana uz klienta lietotāja saskarni

 1 importa javax.swing. *; 2 importa java.awt. *; 3 importēt java.awt.event. *; 4 5 klases JComponentExporter ievieš Employee.Exporter 6 {privātais virknes nosaukums, id, alga; 7 8 public void addName (virknes nosaukums) {this.name = name; } 9 public void addID (virknes ID) {this.id = id; } 10 public void addSalary (String alga) {this.salary = alga; } 11 12 JKomponents getJComponent () 13 {JKomponenta panelis = jauns JPanel (); 14 panel.setLayout (jauns režģa izkārtojums (3,2)); 15 panel.add (jauna JLabel ("Vārds:")); 16 panel.add (jauna JLabel (nosaukums)); 17 panel.add (jauna JLabel ("Darbinieka ID:")); 18 panel.add (jauna JLabel (id)); 19 panel.add (jauna JLabel ("Alga:")); 20 panel.add (jauna JLabel (alga)); 21 atgriešanās panelis; 22} 23} 
$config[zx-auto] not found$config[zx-overlay] not found