Programavimas

Daugiau apie getters ir seters

Tai 25 metų senumo objektinio (OO) projektavimo principas, pagal kurį neturėtumėte atskleisti objekto įgyvendinimo jokioms kitoms programos klasėms. Programą yra be reikalo sunku išlaikyti, kai atskleidžiate įgyvendinimą, visų pirma todėl, kad pakeitus objektą, kuris atskleidžia jos įgyvendinimo įgaliojimus, pasikeičia visos klasės, naudojančios objektą.

Deja, „getter“ / „seterio“ idioma, kurią daugelis programuotojų laiko orientuota į objektą, kastuvuose pažeidžia šį pagrindinį OO principą. Apsvarstykite a pavyzdį Pinigai klasė, kurioje yra a getValue () metodas, kuris grąžina „vertę“ doleriais. Visoje savo programoje turėsite tokį kodą:

dviguba tvarkaVisa; Pinigų suma = ...; //... orderTotal + = suma.getValue (); // orderSotal turi būti doleriais

Šio požiūrio problema yra ta, kad pirmiau pateiktas kodas daro didelę prielaidą, kaip Pinigai klasė yra įgyvendinta (kad „vertė“ saugoma a dvigubai). Kodas, dėl kurio įgyvendinimo prielaidos nutrūksta, kai pasikeičia įgyvendinimas. Jei, pavyzdžiui, turite internacionalizuoti savo programą, kad palaikytumėte kitas nei dolerių valiutas, tada getValue () negrąžina nieko prasmingo. Galite pridėti a „getCurrency“ (), bet tai padarytų visą kodą, supantį getValue () skambinkite daug sudėtingiau, ypač jei vis dar naudojate „getter“ / „seterio“ strategiją, kad gautumėte informaciją, kurios jums reikia darbui atlikti. Tipiškas (ydingas) diegimas gali atrodyti taip:

Pinigų suma = ...; //... reikšmė = suma.getValue (); valiuta = suma.getCurrency (); konversija = CurrencyTable.getConversionFactor (valiuta, USDOLLARS); viso + = vertės * perskaičiavimas; //...

Šis pakeitimas yra per daug sudėtingas, kad jį būtų galima atlikti automatizuotai pertvarkant. Be to, jūs turėtumėte atlikti tokius pakeitimus visur savo kode.

Verslo logikos lygiu išspręskite šią problemą - atlikti darbą objekte, kuriame yra darbui atlikti reikalinga informacija. Užuot ištraukę „vertę“, kad atliktumėte tam tikrą išorinę operaciją, turėtumėte turėti Pinigai klasė atlieka visas su pinigais susijusias operacijas, įskaitant valiutos konvertavimą. Tinkamai struktūrizuotas objektas apdorotų bendrą sumą taip:

Pinigai iš viso = ...; Pinigų suma = ...; total.increaseBy (suma); 

papildyti() metodas išsiaiškins operando valiutą, atliks bet kokį būtiną valiutos konvertavimą (tai yra operacija su pinigų) ir atnaujinkite bendrą sumą. Jei pirmiausia naudojote šią objekto strategiją, kuri turi informaciją, daro darbą, tai sąvoka valiuta galėtų būti pridėta prie Pinigai klasę be jokių pakeitimų, reikalingų naudojamame kode Pinigai objektai. Tai reiškia, kad tik dolerių pertvarkymas tarptautiniam įgyvendinimui būtų sutelktas vienoje vietoje: Pinigai klasė.

Problema

Daugumai programuotojų nekyla sunkumų suvokiant šią koncepciją verslo logikos lygmeniu (nors nuosekliai taip galvoti gali prireikti šiek tiek pastangų). Problemos pradeda kilti, kai vartotojo sąsaja (UI) patenka į paveikslėlį. Problema yra ne ta, kad negalite pritaikyti tokių technikų, kaip ką tik aprašiau, kuriant vartotojo sąsają, bet tai, kad daugelis programuotojų yra užrakinti „getter / setter“ mentalitetu, kai kalbama apie vartotojo sąsajas. Aš kaltinu šią problemą iš esmės procedūriniais kodų konstravimo įrankiais, tokiais kaip „Visual Basic“ ir jo klonais (įskaitant „Java“ vartotojo sąsajos kūrėjus), kurie priverčia jus laikytis šio procedūrinio, „geresnio / nustatomojo“ mąstymo.

(Nukrypimas: kai kurie iš jūsų atsisakys ankstesnio teiginio ir rėks, kad VB remiasi pašventinta „Model-View-Controller“ (MVC) architektūra, taigi yra šventai šventa. Turėkite omenyje, kad MVC buvo sukurtas beveik prieš 30 metų. Aštuntajame dešimtmetyje didžiausias superkompiuteris buvo lygiavertis šių dienų darbalaukiams. Dauguma mašinų (pvz., DEC PDP-11) buvo 16 bitų kompiuteriai, turintys 64 KB atminties, o laikrodžio dažnis buvo matuojamas dešimtimis megahercų. Jūsų vartotojo sąsaja tikriausiai buvo šūsnis skylėtų kortelių. Jei jums pasisekė turėti vaizdo terminalą, galbūt naudojote ASCII pagrįstą konsolės įvesties / išvesties (įvesties / išvesties) sistemą. Per pastaruosius 30 metų mes daug ko išmokome. Net „Java Swing“ turėjo pakeisti MVC panašia „atskirto modelio“ architektūra, visų pirma todėl, kad grynas MVC nepakankamai izoliuoja vartotojo sąsają ir domeno modelio sluoksnius.)

Taigi trumpai apibūdinkime problemą:

Jei objektas negali atskleisti įgyvendinimo informacijos (naudodamas „get / set“ metodus ar bet kokiomis kitomis priemonėmis), tai suprantama, kad objektas turi kažkaip susikurti savo vartotojo sąsają. Tai yra, jei objekto atributų atvaizdavimo būdas yra paslėptas nuo likusios programos, tada negalėsite išskleisti tų atributų, kad sukurtumėte vartotojo sąsają.

Beje, atkreipkite dėmesį, kad neslepiate fakto, kad yra atributas. (Aš apibrėžiu atributas, čia, kaip esminė objekto savybė.) Žinote, kad an Darbuotojas privalo turėti atlyginimą arba atlyginimo atributą, kitaip tai nebūtų Darbuotojas. (Tai būtų a Asmuo, a Savanoris, a Valkata, ar dar kažkas, kas neturi atlyginimo.) Ko nežinote ar nenorite žinoti, kaip tas atlyginimas yra pavaizduotas objekto viduje. Tai gali būti a dvigubai, a Stygos, mastelis ilgasarba dvejetainiai koduojamas dešimtainis skaičius. Tai gali būti „sintetinis“ arba „išvestinis“ atributas, kuris apskaičiuojamas vykdymo metu (pavyzdžiui, iš darbo užmokesčio lygio ar pareigų arba gaunant vertę iš duomenų bazės). Nors "get" metodas iš tikrųjų gali paslėpti kai kurias šios įgyvendinimo detales, kaip matėme Pinigai Pavyzdžiui, jis negali pakankamai paslėpti.

Taigi, kaip objektas sukuria savo vartotojo sąsają ir lieka prižiūrimas? Tik patys paprasčiausi objektai gali palaikyti kažką panašaus į a rodyti save () metodas. Realistiniai objektai turi:

  • Rodyti save skirtingais formatais (XML, SQL, kableliais atskirtos vertės ir kt.).
  • Rodyti kitokį Peržiūrų jų pačių (viename rodinyje gali būti rodomi visi atributai; kitame gali būti rodomas tik pogrupis atributų; trečiame - atributai gali būti pateikiami kitaip).
  • Rodyti save skirtingose ​​aplinkose (kliento pusėje (JKomponentas) ir aptarnaujamas klientui (pvz., HTML)) ir tvarko tiek įvestį, tiek išvestį abiejose aplinkose.

Kai kurie mano ankstesnio „Getter / Setter“ straipsnio skaitytojai priėjo prie išvados, kad aš pasisakiau už tai, kad objekte pridėtumėte metodus, kurie padengtų visas šias galimybes, tačiau tas „sprendimas“ yra akivaizdžiai nesąmoningas. Gautas sunkiasvoris objektas yra ne tik pernelyg sudėtingas, bet ir turėsite jį nuolat modifikuoti, kad galėtumėte patenkinti naujus vartotojo sąsajos reikalavimus. Praktiškai objektas tiesiog negali susikurti visų įmanomų vartotojo sąsajų, jei dėl jokios kitos priežasties, išskyrus daugelį tų vartotojo sąsajų, dar nebuvo sukurta kuriant klasę.

Sukurkite sprendimą

Šios problemos sprendimas yra atskirti vartotojo sąsajos kodą nuo pagrindinio verslo objekto, priskiriant jį atskirai objektų klasei. Tai reiškia, kad turėtumėte atskirti kai kurias funkcijas galėjo būti objekte visiškai į atskirą objektą.

Šis objekto metodų išsišakojimas pasireiškia keliais dizaino modeliais. Greičiausiai esate susipažinę su strategija, kuri naudojama kartu su įvairiomis java.awt. Konteineris klasės daryti maketą. Išdėstymo problemą galite išspręsti naudodami išvesties sprendimą: „FlowLayoutPanel“, „GridLayoutPanel“, „BorderLayoutPanel“ir t. t., tačiau tai įpareigoja per daug klases ir daug besikartojančių tų klasių kodų. Vienas sunkiasvorės klasės sprendimas (metodų pridėjimas prie Konteineris Kaip layOutAsGrid (), layOutAsFlow ()ir kt.) taip pat yra nepraktiška, nes negalite modifikuoti šaltinio kodo Konteineris vien todėl, kad jums reikia nepalaikomo išdėstymo. Pagal strategijos modelį sukuriate Strategija sąsaja („LayoutManager“) įgyvendino keli Konkreti strategija klasės („FlowLayout“, „GridLayout“ir pan.). Tada jūs sakote a Kontekstas objektas (a Konteineris) kaip ką nors padaryti jį perduodant a Strategija objektas. (Praeini a Konteineris a „LayoutManager“ tai apibrėžia maketo strategiją.)

„Builder“ modelis yra panašus į „Strategy“. Pagrindinis skirtumas yra tas, kad Statybininkas klasė įgyvendina kažko konstravimo strategiją (pvz., a JKomponentas arba XML srautas, vaizduojantis objekto būseną). Statybininkas objektai paprastai kuria savo produktus naudodami ir daugiapakopį procesą. Tai yra raginimai naudoti įvairius metodus Statybininkas reikalaujama užbaigti statybos procesą, ir Statybininkas paprastai nežino skambučių atlikimo tvarkos ar vieno iš jo būdų skambučių skaičiaus. Svarbiausia statybininko savybė yra ta, kad verslo objektas (vadinamas Kontekstas) tiksliai nežino, kas Statybininkas objektas statomas. Šablonas izoliuoja verslo objektą nuo jo vaizdavimo.

Geriausias būdas pamatyti, kaip veikia paprastas statybininkas, yra pažvelgti į jį. Pirmiausia pažvelkime į Kontekstas, verslo objektas, kuriam reikia atskleisti vartotojo sąsają. 1 sąrašas rodo supaprastintą Darbuotojas klasė. Darbuotojas turi vardas, idir atlyginimas atributus. (Šių klasių šakos yra sąrašo apačioje, tačiau šios šaknys yra tikros vietos žymekliai. Tikiuosi, jūs galite lengvai įsivaizduoti, kaip šios klasės veiks.)

Šis konkretus Kontekstas naudojasi mano manymu kaip dvikryptis statybininkas. Klasikinė keturių statybininkų gauja eina viena kryptimi (išvestis), bet aš taip pat pridėjau a Statybininkas kad an Darbuotojas objektas gali naudoti pats inicializuoti. Du Statybininkas reikalingos sąsajos. Darbuotojas. Eksportuotojas sąsaja (1 sąrašas, 8 eilutė) tvarko išvesties kryptį. Tai apibrėžia sąsają su Statybininkas objektas, kuris sukuria dabartinio objekto vaizdą. Darbuotojas deleguoja tikrąją NS sąsają Statybininkas viduje konors eksportas () metodas (31 eilutėje). Statybininkas neperduodamas tikrasis laukas, o naudojamas Stygoss perduoti tų laukų reprezentaciją.

Sąrašas 1. Darbuotojas: „Builder“ kontekstas

 1 importuoti java.util.Locale; 2 3 viešoji klasė 4 darbuotojas {privatus vardas, pavardė; 5 privatus „EmployeeId“ ID; 6 privatus Pinigų atlyginimas; 7 8 viešoji sąsaja eksportuotojas 9 {void addName (eilutės pavadinimas); 10 negaliojantis addID (eilutės ID); 11 niekinis addSalary (String alga); 12} 13 14 viešoji sąsaja Importuotojas 15 {String suteiktiName (); 16 eilutė suteiktiID (); 17 Stygos teiktiSalary (); 18 tuštuma atidaryta (); 19 niekinis uždarymas (); 20} 21 22 valstybinis darbuotojas (importuotojas statybininkas) 23 {statybininkas.atvaryti (); 24 this.name = naujas vardas (statybininkas.provideName ()); 25 this.id = new EmployeeId (statybininkas.provideID ()); 26 this.salary = nauji pinigai (builder.provideSalary (), 27 nauji lokalės ("en", "US")); 28 statybininkas.uždaryti (); 29} 30 31 viešas negaliojantis eksportas (eksportuotojo kūrėjas) 32 {statybininkas.addName (vardas.toString ()); 33 statybininkas.addID (id.toString ()); 34 statybininkas.addSalary (alga.toString ()); 35} 36 37 //... 38 } 39 //---------------------------------------------------------------------- 40 // Vieneto bandymo dalykai 41 // 42 klasės pavadinimas 43 {private String value; 44 public Name (String value) 45 {this.value = value; 46} 47 public String toString () {grąžinimo vertė; }; 48} 49 50 klasės „EmployeeId 51“ {private String value; 52 public EmployeeId (String value) 53 {this.value = value; 54} 55 public String toString () {return value; } 56} 57 58 klasės pinigai 59 {privati ​​Styginių vertė; 60 viešųjų pinigų (eilutės vertė, lokalės vieta) 61 {this.value = value; 62} 63 public String toString () {return value; } 64} 

Pažvelkime į pavyzdį. Šis kodas sukuria 1 paveikslo vartotojo sąsają:

Darbuotojo wilma = ...; JComponentExporter uiBuilder = naujas JComponentExporter (); // Sukurkite statybininką wilma.export (uiBuilder); // Sukurkite vartotojo sąsają JComponent userInterface = uiBuilder.getJComponent (); //... someContainer.add (vartotojo sąsaja); 

2 sąraše rodomas „JComponentExporter“. Kaip matote, visas su vartotojo sąsaja susijęs kodas yra sutelktas Betono gamintojas ( „JComponentExporter“), ir Kontekstas ( Darbuotojas) lemia kūrimo procesą tiksliai nežinodamas, ką jis kuria.

Sąrašas 2. Eksportavimas į kliento vartotojo sąsają

 1 importo javax.swing. *; 2 importuoti java.awt. *; 3 importuoti java.awt.event. *; 4 5 klasės „JComponentExporter“ įgyvendina „Employee.Exporter 6“ {privatus eilutės pavadinimas, ID, atlyginimas; 7 8 public void addName (String name) {this.name = vardas; } 9 public void addID (eilutės ID) {this.id = id; } 10 public void addSalary (String atlyginimas) {this.salary = atlyginimas; } 11 12 JComponent getJComponent () 13 {JComponent panel = new JPanel (); 14 panel.setLayout (naujas „GridLayout“ (3,2)); 15 panel.add (nauja „JLabel“ („Vardas:“)); 16 panel.add (nauja „JLabel“ (vardas)); 17 panel.add (nauja „JLabel“ („Darbuotojo ID:“)); 18 panel.add (nauja „JLabel“ (id)); 19 panel.add (nauja „JLabel“ („Atlyginimas:“)); 20 panel.add (nauja JLabel (atlyginimas)); 21 grąžinimo skydelis; 22} 23} 
$config[zx-auto] not found$config[zx-overlay] not found