Programmēšana

Java padoms 75: labākai organizēšanai izmantojiet ligzdotas klases

Tipiska Java lietojumprogrammas apakšsistēma sastāv no kopīgu klašu un saskarņu kopuma, no kuriem katrs veic noteiktu lomu. Dažas no šīm klasēm un saskarnēm ir nozīmīgas tikai citu klašu vai saskarņu kontekstā.

Projektējot no konteksta atkarīgas klases kā augšējā līmeņa ligzdotas klases (īsumā - ligzdotas klases), kuras ietver konteksta apkalpojošā klase, šī atkarība kļūst skaidrāka. Turklāt ligzdoto klašu izmantošana atvieglo sadarbības atpazīšanu, novērš vārda vietas piesārņojumu un samazina avota failu skaitu.

(Pilnu šī padoma avota kodu var lejupielādēt ZIP formātā no sadaļas Resursi.)

Ligzdotas klases pret iekšējām klasēm

Ligzdotas klases ir vienkārši statiskas iekšējās klases. Atšķirība starp ligzdotajām klasēm un iekšējām klasēm ir tāda pati kā atšķirība starp statiskajiem un nestatiskajiem klases dalībniekiem: ligzdotās klases ir saistītas ar pašu norobežojošo klasi, savukārt iekšējās klases ir saistītas ar norobežojošās klases objektu.

Tāpēc iekšējās klases objektiem ir nepieciešams norobežojošās klases objekts, savukārt ligzdotajiem klases objektiem tas nav nepieciešams. Tāpēc ligzdotās klases rīkojas tāpat kā augstākā līmeņa klases, izmantojot pievienojošo klasi, lai nodrošinātu pakotnei līdzīgu organizāciju. Turklāt ligzdotajām klasēm ir piekļuve visiem norobežojošās klases dalībniekiem.

Motivācija

Apsveriet tipisku Java apakšsistēmu, piemēram, Swing komponentu, izmantojot Model-View-Controller (MVC) dizaina modeli. Notikuma objekti ietver modeļa paziņojumus par izmaiņām. Skati reģistrē interesi par dažādiem notikumiem, komponenta pamatā esošajam modelim pievienojot klausītājus. Modelis paziņo saviem skatītājiem par izmaiņām savā stāvoklī, piegādājot šos notikumu objektus reģistrētiem klausītājiem. Bieži vien šie klausītāju un notikumu veidi ir raksturīgi modeļa tipam, un tāpēc tiem ir jēga tikai modeļa tipa kontekstā. Tā kā katram no šiem klausītāju un notikumu veidiem jābūt publiski pieejamiem, katram no tiem jābūt savam avota failā. Šajā situācijā, ja vien netiek izmantota kāda kodēšanas konvencija, savienojumu starp šiem tipiem ir grūti atpazīt. Protams, lai parādītu savienojumu, katrai grupai var izmantot atsevišķu paketi, taču tas rada lielu skaitu paku.

Ja klausītāja un notikumu tipus ieviešam kā modeļa saskarnes ligzdotos veidus, savienošana ir acīmredzama. Ar šiem ligzdotajiem veidiem mēs varam izmantot jebkuru vēlamo piekļuves modifikatoru, tostarp publisko. Turklāt, tā kā ligzdotie tipi izmanto pievienojošo saskarni kā nosaukumvietu, pārējā sistēma tos atsaucas kā ., izvairoties no nosaukumvietas piesārņojuma šajā iepakojumā. Modeļa saskarnes avota failā ir visi atbalsta veidi, kas atvieglo izstrādi un uzturēšanu.

Pirms: Piemērs bez ligzdotām klasēm

Piemēram, mēs izstrādājam vienkāršu komponentu, Šīferis, kuras uzdevums ir zīmēt figūras. Tāpat kā Swing komponenti, mēs izmantojam MVC dizaina modeli. Modelis, SlateModel, kalpo kā formu krātuve. SlateModelListenerparakstīties uz izmaiņām modelī. Modelis paziņo saviem klausītājiem, nosūtot veida pasākumus SlateModelEvent. Šajā piemērā mums ir nepieciešami trīs avota faili, pa vienam katrai klasei:

// SlateModel.java import java.awt.Shape; publiskā saskarne SlateModel {// Klausītāju pārvaldība public void addSlateModelListener (SlateModelListener l); public void removeSlateModelListener (SlateModelListener l); // Shape krātuves pārvaldība, skati ir jāinformē public void addShape (Shape s); public void removeShape (Shape s); public void removeAllShapes (); // Formas krātuves lasīšanas operācijas public int getShapeCount (); publiskā forma getShapeAtIndex (int indekss); } 
// SlateModelListener.java import java.util.EventListener; publiskā saskarne SlateModelListener paplašina EventListener {public void slateChanged (SlateModelEvent notikums); } 
// SlateModelEvent.java import java.util.EventObject; publiskā klase SlateModelEvent paplašina EventObject {public SlateModelEvent (SlateModel model) {super (model); }} 

(Avota kods vietnei DefaultSlateModel, šī modeļa noklusējuma ieviešana ir failā pirms / DefaultSlateModel.java.)

Tālāk mēs pievēršam uzmanību Šīferis, skats uz šo modeli, kas savu krāsošanas uzdevumu pārsūta lietotāja saskarnes delegātam, SlateUI:

// Slate.java import javax.swing.JComponent; publiskā klase Šīferis paplašina JComponent īsteno SlateModelListener {private SlateModel _model; public Slate (SlateModel model) {_model = model; _model.addSlateModelListener (šis); setOpaque (true); setUI (jauns SlateUI ()); } public Slate () {this (new DefaultSlateModel ()); } public SlateModel getModel () {return _model; } // Klausītāja ieviešana public void slateChanged (SlateModelEvent event) {pārkrāsot (); }} 

Visbeidzot, SlateUI, vizuālās GUI sastāvdaļa:

// SlateUI.java import java.awt. *; importēt javax.swing.JComponent; importēt javax.swing.plaf.ComponentUI; publiskā klase SlateUI paplašina ComponentUI {public void paint (Grafika g, JKomponents c) {SlateModel model = ((Slate) c) .getModel (); g.setColor (c.getForeground ()); Grafika2D g2D = (Grafika2D) g; for (int size = model.getShapeCount (), i = 0; i <size; i ++) {g2D.draw (model.getShapeAtIndex (i)); }}} 

Pēc: modificēts piemērs, izmantojot ligzdotas klases

Iepriekšējā piemērā klases struktūra neparāda attiecības starp klasēm. Lai to mazinātu, mēs izmantojām nosaukumu piešķiršanas kārtību, kurā visām saistītajām klasēm ir nepieciešams kopīgs prefikss, taču skaidrāk būtu parādīt attiecības kodā. Turklāt šo klašu izstrādātājiem un uzturētājiem jāpārvalda trīs faili: par SlateModel, priekš SlateEvent, un par SlateListener, lai īstenotu vienu koncepciju. Tas pats attiecas arī uz divu failu pārvaldīšanu Šīferis un SlateUI.

Mēs varam uzlabot lietas, darot SlateModelListener un SlateModelEvent ligzdoti SlateModel interfeiss. Tā kā šie ligzdotie veidi atrodas saskarnē, tie ir netieši statiski. Tomēr mēs esam izmantojuši skaidru statisku deklarāciju, lai palīdzētu apkopes programmētājam.

Klienta kods uz tiem atsauksies kā SlateModel.SlateModelListener un SlateModel.SlateModelEvent, bet tas ir lieks un nevajadzīgi garš. Mēs noņemam prefiksu SlateModel no ligzdotajām klasēm. Veicot šīs izmaiņas, klienta kods uz tiem atsauksies kā SlateModel.Listener un SlateModel.Event. Tas ir īss un skaidrs, un tas nav atkarīgs no kodēšanas standartiem.

Priekš SlateUI, mēs darām to pašu - mēs to padarām par ligzdotu klasi Šīferis un nomainiet tā nosaukumu uz UI. Tā kā tā ir ligzdota klase klases iekšienē (nevis saskarnē), mums jāizmanto precīzs statiskais modifikators.

Veicot šīs izmaiņas, mums ir nepieciešams tikai viens fails ar modeli saistītām klasēm un vēl viens ar skatu saistītām klasēm. The SlateModel kods tagad kļūst:

// SlateModel.java import java.awt.Shape; importēt java.util.EventListener; importēt java.util.EventObject; publiskā saskarne SlateModel {// Klausītāju pārvaldība public void addSlateModelListener (SlateModel.Listener l); public void removeSlateModelListener (SlateModel.Listener l); // Shape krātuves pārvaldība, skati ir jāinformē public void addShape (Shape s); public void removeShape (Shape s); public void removeAllShapes (); // Formas krātuves lasīšanas operācijas public int getShapeCount (); publiskā forma getShapeAtIndex (int indekss); // Saistītās augstākā līmeņa ligzdotās klases un saskarnes publiskā saskarne Klausītājs paplašina EventListener {public void slateChanged (SlateModel.Event event); } public class Event paplašina EventObject {public Event (SlateModel model) {super (model); }}} 

Un kods Šīferis tiek mainīts uz:

// Slate.java import java.awt. *; importēt javax.swing.JComponent; importēt javax.swing.plaf.ComponentUI; public class Slate paplašina JComponent ievieš SlateModel.Listener {public Slate (SlateModel model) {_model = model; _model.addSlateModelListener (šis); setOpaque (true); setUI (jauns Slate.UI ()); } public Slate () {this (new DefaultSlateModel ()); } public SlateModel getModel () {return _model; } // Klausītāja ieviešana public void slateChanged (SlateModel.Event event) {pārkrāsot (); } public static class UI paplašina ComponentUI {public void paint (Grafika g, JKomponents c) {SlateModel model = ((Slate) c) .getModel (); g.setColor (c.getForeground ()); Grafika2D g2D = (Grafika2D) g; for (int size = model.getShapeCount (), i = 0; i <size; i ++) {g2D.draw (model.getShapeAtIndex (i)); }}}} 

(Mainītā modeļa noklusējuma ieviešanas avota kods, DefaultSlateModel, atrodas failā pēc / DefaultSlateModel.java.)

Ietvaros SlateModel klasei, ligzdotajām klasēm un saskarnēm nav nepieciešams izmantot pilnībā kvalificētus vārdus. Piemēram, vienkārši Klausītājs pietiktu ar SlateModel.Listener. Tomēr pilnībā kvalificētu vārdu izmantošana palīdz izstrādātājiem, kuri kopē metožu parakstus no saskarnes un ielīmē tos ieviešanas klasēs.

JFC un ligzdoto klašu izmantošana

JFC bibliotēka noteiktos gadījumos izmanto ligzdotas klases. Piemēram, klase BasicBorders iepakojumā javax.swing.plaf.basic definē vairākas ligzdotas klases, piemēram, BasicBorders.ButtonBorder. Šajā gadījumā klase BasicBorders nav citu dalībnieku un vienkārši darbojas kā pakete. Atsevišķas paketes izmantošana tā vietā būtu bijusi vienlīdz efektīva, ja ne piemērotāka. Tas ir atšķirīgs lietojums nekā šajā rakstā aprakstītais.

Šī padoma pieeja, izmantojot JFC dizainu, ietekmētu ar modeļu tipiem saistīto klausītāju un notikumu veidu organizāciju. Piemēram, javax.swing.event.TableModelListener un javax.swing.event.TableModelEvent tiktu ieviesti attiecīgi kā ligzdota saskarne un iekšpusē ievietota klase javax.swing.table.TableModel.

Šīs izmaiņas kopā ar vārdu saīsināšanu radīs nosaukumu klausītāja interfeiss javax.swing.table.TableModel.Listener un pasākuma klase nosaukta javax.swing.table.TableModel.Event. TableModel tad būtu pilnībā noslēgta ar visām nepieciešamajām atbalsta klasēm un saskarnēm, nevis būtu vajadzīgas atbalsta klases un saskarne, kas sadalīta pa trim failiem un divām pakotnēm.

Ligzdotu klašu izmantošanas vadlīnijas

Tāpat kā jebkura cita modeļa gadījumā, saprātīgi izmantojot ligzdotas klases, dizains ir vienkāršāks un vieglāk saprotams nekā tradicionālā pakotņu organizēšana. Tomēr nepareiza lietošana rada nevajadzīgu sasaisti, kas padara neskaidru ligzdoto klašu lomu.

Ņemiet vērā, ka iepriekš ievietotajā ligzdotajā piemērā mēs ligzdotos tipus izmantojam tikai tiem tipiem, kuri nevar pastāvēt bez pievienojošā tipa konteksta. Mēs, piemēram, neradām SlateModel ligzdota saskarne Šīferis jo var būt arī citi skatu veidi, izmantojot to pašu modeli.

Ņemot vērā jebkuras divas klases, izmantojiet šīs vadlīnijas, lai izlemtu, vai jums jāizmanto ligzdotās klases. Izmantojiet ligzdotās klases, lai organizētu savas nodarbības tikai tad, ja atbilde uz abiem zemāk redzamajiem jautājumiem ir apstiprinoša:

  1. Vai ir iespējams skaidri klasificēt vienu no klasēm kā primāro klasi un otru kā palīgklasi?

  2. Vai atbalsta klasei nav jēgas, ja primārā klase tiek noņemta no apakšsistēmas?

Secinājums

Ligzdotu klašu izmantošanas modelis cieši savieno saistītos veidus. Tas ļauj izvairīties no nosaukumvietas piesārņojuma, kā nosaukumvietu izmantojot norobežojošo tipu. Tā rezultātā ir mazāk avota failu, nezaudējot iespēju publiski atmaskot atbalsta veidus.

Tāpat kā jebkuru citu modeli, izmantojiet šo modeli saprātīgi. Īpaši pārliecinieties, ka ligzdotie veidi ir patiesi saistīti un tiem nav nozīmes bez pievienojošā tipa konteksta. Pareiza modeļa izmantošana nepalielina savienojumu, bet tikai precizē esošo savienojumu.

Ramnivas Laddads ir Sun sertificēts Java tehnoloģiju (Java 2) arhitekts. Viņš ir ieguvis maģistra grādu elektrotehnikā ar specializāciju komunikāciju inženierijā. Viņam ir sešu gadu pieredze, izstrādājot un izstrādājot vairākus programmatūras projektus, kas saistīti ar GUI, tīklu veidošanu un izplatītām sistēmām. Viņš pēdējos divus gadus ir izstrādājis objektorientētas programmatūras Java un C ++ pēdējos piecus gadus. Pašlaik Ramnivas strādā Real-Time Innovations Inc. kā programmatūras inženieris. RTI viņš šobrīd strādā, lai izstrādātu un attīstītu ControlShell, uz komponentiem balstītu programmēšanas sistēmu sarežģītu reāllaika sistēmu veidošanai.
$config[zx-auto] not found$config[zx-overlay] not found