Pred nekaj meseci so me prosili, da ustvarim majhno knjižnico Java, do katere lahko dostopa aplikacija za upodabljanje grafičnega uporabniškega vmesnika (GUI) za igro Checkers. Poleg upodabljanja šahovnice in kockic mora tudi GUI omogočati vleko preverjalnika iz enega kvadrata v drugega. Preverjevalnik mora biti tudi centriran na kvadrat in ne sme biti dodeljen kvadratu, ki ga zaseda drug preglednik. V tem prispevku predstavljam svojo knjižnico.
Oblikovanje knjižnice GUI za preverjanje
Katere javne tipe naj knjižnica podpira? Pri damah vsak od dveh igralcev izmenično premakne enega od svojih rednih (ne-kraljevih) dama preko plošče samo v smeri naprej in po možnosti preskoči dama (-e) drugega igralca. Ko checker doseže drugo stran, je povišan v kralja, ki se lahko premakne tudi v smeri nazaj. Iz tega opisa lahko sklepamo na naslednje vrste:
Odbor
Checker
CheckerType
Igralec
A Odbor
objekt identificira šahovnico. Služi kot posoda za Checker
predmeti, ki zasedajo različne kvadratke. Lahko se nariše in zahteva, da je vsak vsebovan Checker
predmet nariše sam.
A Checker
objekt identificira preverjevalnik. Ima barvo in oznako, ali gre za navaden ali kraljev. Lahko se nariše in svojo velikost da na voljo Odbor
, na velikost katerega vpliva Checker
velikost.
CheckerType
je enum, ki prek svojih štirih konstant identificira barvo in tip preverjevalnika: BLACK_KING
, ČRNA_REGULARNA
, RDEČE KRALJE
, in RDEČA_REGULARNA
.
A Igralec
object je krmilnik za premikanje dame z neobveznimi skoki. Ker sem se odločil, da bom to igro implementiral v Swing, Igralec
ni potrebno. Namesto tega sem se obrnil Odbor
v komponento Swing, katere konstruktor registrira poslušalce gibanja miške in miške, ki v imenu človeškega predvajalnika upravljajo gibanje kontrolnikov. V prihodnosti bi lahko računalniški predvajalnik implementiral prek druge niti, sinhronizatorja in druge Odbor
metoda (kot npr premakni ()
).
Kaj počnejo javni API-ji Odbor
in Checker
prispevati? Po premisleku sem prišel do naslednje javnosti Odbor
API:
Board ()
: Sestavite aOdbor
predmet. Konstruktor izvaja različne naloge inicializacije, kot je registracija poslušalca.void add (Checker checker, int vrstica, int stolpec)
: Dodajchecker
doOdbor
na položaju, ki ga je določilvrstici
instolpec
. Vrstica in stolpec sta vrednosti 1, v nasprotju s tem, da sta 0 (glej sliko 1). Thedodaj ()
metijava.lang.IllegalArgumentException
ko je njegov argument vrstice ali stolpca manjši od 1 ali večji od 8. Prav tako vrže neoznačenoAlreadyOccupiedException
ko poskušate dodatiChecker
na zaseden trg.Dimenzija getPreferredSize ()
: VrniteOdbor
prednostna velikost komponente za namene postavitve.
Slika 1. Zgornji levi kot kontrolne plošče se nahaja na (1, 1)
Razvil sem tudi naslednjo javnost Checker
API:
Checker (CheckerType checkerType)
: Sestavite aChecker
predmet navedenegacheckerType
(BLACK_KING
,ČRNA_REGULARNA
,RDEČE KRALJE
, aliRDEČA_REGULARNA
).void draw (grafike g, int cx, int cy)
: Nariši aChecker
z uporabo določenega grafičnega kontekstag
s središčem čekera na (cx
,cy
). To metodo naj bi poklicali izOdbor
samo.logična vsebina vsebuje (int x, int y, int cx, int cy)
: Astatično
pomožna metoda, poklicana izOdbor
ki določa, ali bodo koordinate miške (x
,y
) ležijo znotraj kontrolnika, katerega sredinske koordinate so določene z (cx
,cy
) in katerih dimenzija je določena drugje vChecker
razred.int getDimension ()
: Astatično
pomožna metoda, poklicana izOdbor
ki določa velikost dama, tako da lahko plošča ustrezno prilagodi svoje kvadrate in celotno velikost.
To v veliki meri pokriva vse knjižnice grafičnega uporabniškega vmesnika checkers glede na njihove vrste in njihove javne API-je. Zdaj se bomo osredotočili na to, kako sem uvedel to knjižnico.
Izvajanje knjižnice GUI za preverjanje
GUI knjižnica checkers je sestavljena iz štirih javnih vrst, ki se nahajajo v istoimenskih izvornih datotekah: AlreadyOccupiedException
, Odbor
, Checker
, in CheckerType
. Seznam 1 predstavlja AlreadyOccupiedException
izvorna koda.
Seznam 1. AlreadyOccupiedException.java
javni razred AlreadyOccupiedException razširja RuntimeException {public AlreadyOccupiedException (String msg) {super (msg); }}
AlreadyOccupiedException
podaljša java.lang.RuntimeException
, kar naredi AlreadyOccupiedException
nepreverjena izjema (ni je treba ujeti ali prijaviti v meti
klavzula). Če bi hotel narediti AlreadyOccupiedException
preverjeno, podaljšal bi java.lang.Exception
. Za to vrsto sem se odločil, da deluje nepreverjeno, ker deluje podobno kot nepreverjeno IllegalArgumentException
.
AlreadyOccupiedException
razglasi konstruktor, ki sprejme nizni argument, ki opisuje razlog za izjemo. Ta argument se posreduje RuntimeException
superrazred.
Seznam 2 predstavlja Odbor
.
Seznam 2. Board.java
uvoz java.awt.Color; import java.awt.Dimension; uvoz java.awt.Graphics; uvoz java.awt.Graphics2D; uvoz java.awt.RenderingHints; import java.awt.event.MouseEvent; uvoz java.awt.event.MouseAdapter; uvoz java.awt.event.MouseMotionAdapter; uvoz java.util.ArrayList; uvoz java.util.List; uvoz javax.swing.JComponent; javni razred Board razširja JComponent {// dimenzija kvadrata (25% večja od checkerja) private final static int SQUAREDIM = (int) (Checker.getDimension () * 1.25); // dimenzija šahovnice (širina 8 kvadratov) private final int BOARDDIM = 8 * SQUAREDIM; // prednostna velikost komponente plošče private Dimension dimPrefSize; // vlečna zastavica - nastavljena na true, ko uporabnik pritisne gumb miške na preverjevalnik // in odstranjena na false, ko uporabnik sprosti zasebni logični gumb inDrag = false; // premik med koordinatami vlečnega zagona in koordinatami centra preverjanja private int deltax, deltay; // sklic na pozicioniran preverjevalec na začetku povlečenja zasebnega PosCheck posCheck; // osrednja lokacija pregledovalnika na začetku povlečenja private int oldcx, oldcy; // seznam predmetov Checker in njihovi začetni položaji private List posChecks; javni odbor () {posChecks = nov ArrayList (); dimPrefSize = nova dimenzija (BOARDDIM, BOARDDIM); addMouseListener (new MouseAdapter () {@Override public void mousePress (MouseEvent me) {// Pridobi koordinate miške v času pritiska. int x = me.getX (); int y = me.getY (); // Poiščite pozicioniran kontrolnik pod pritiskom miške. for (PosCheck posCheck: posChecks) if (Checker.contens (x, y, posCheck.cx, posCheck.cy)) {Board.this.posCheck = posCheck; oldcx = posCheck.cx; oldcy = posCheck.cy ; deltax = x - posCheck.cx; deltay = y - posCheck.cy; inDrag = true; return;}} @Override public void mouseReleased (MouseEvent me) {// Ko je miš sproščena, počistite inDrag (za // pomeni, da ni povlečenja v teku), če je inDrag // že nastavljen. if (inDrag) inDrag = false; else return; // Snap checker v sredino kvadrata. int x = me.getX (); int y = me.getY (); posCheck .cx = (x - deltax) / SQUAREDIM * SQUAREDIM + SQUAREDIM / 2; posCheck.cy = (y - deltay) / SQUAREDIM * SQUAREDIM + SQUAREDIM / 2; // Ne premikajte dame na zasedeni kvadrat. za (PosCheck posCheck : posChecks) if (posCheck! = Board.this.posCheck && posC heck.cx == Board.this.posCheck.cx && posCheck.cy == Board.this.posCheck.cy) {Board.this.posCheck.cx = oldcx; Board.this.posCheck.cy = starost; } posCheck = null; prebarvati (); }}); // Na programček priključimo poslušalca gibanja miške. Ta poslušalec // posluša dogodke vlečenja miške. addMouseMotionListener (new MouseMotionAdapter () {@Override public void mouseDragged (MouseEvent me) {if (inDrag) {// Posodobi lokacijo centra za preverjanje. posCheck.cx = me.getX () - deltax; posCheck.cy = me.getY ( ) - deltay; prebarvaj ();}}}); } public void add (Checker checker, int row, int col) {if (vrstica 8) vrže novo IllegalArgumentException ("vrstica izven obsega:" + vrstica); če (col 8) vrže novo IllegalArgumentException ("col out of range:" + col); PosCheck posCheck = nov PosCheck (); posCheck.checker = preveritelj; posCheck.cx = (col - 1) * SQUAREDIM + SQUAREDIM / 2; posCheck.cy = (vrstica - 1) * SQUAREDIM + SQUAREDIM / 2; za (PosCheck _posCheck: posChecks), če (posCheck.cx == _posCheck.cx && posCheck.cy == _posCheck.cy) vrže nov AlreadyOccupiedException ("kvadrat pri (" + vrstica + "," + col + ") je zaseden" ); posChecks.add (posCheck); } @Override public Dimension getPreferredSize () {return dimPrefSize; } @Override protected void paintComponent (Graphics g) {paintCheckerBoard (g); za (PosCheck posCheck: posChecks) if (posCheck! = Board.this.posCheck) posCheck.checker.draw (g, posCheck.cx, posCheck.cy); // Nazadnje narišite povlečeni preverjevalnik, tako da se prikaže nad katerim koli temeljnim // preverjalnikom. if (posCheck! = null) posCheck.checker.draw (g, posCheck.cx, posCheck.cy); } private void paintCheckerBoard (Graphics g) {((Graphics2D) g) .setRenderingHint (RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); // Prebarvaj šahovnico. za (int vrstica = 0; vrstica <8; vrstica ++) {g.setColor (((vrstica & 1)! = 0)? Color.BLACK: Color.WHITE); for (int col = 0; col <8; col ++) {g.fillRect (col * SQUAREDIM, vrstica * SQUAREDIM, SQUAREDIM, SQUAREDIM); g.setColor ((g.getColor () == Color.BLACK)? Color.WHITE: Color.BLACK); }}} // pozicijski razred pomožnega razreda checker zasebni razred PosCheck {javni Checker checker; javni int cx; javni int cy; }}
Odbor
podaljša javax.swing.JComponent
, kar naredi Odbor
Swing komponenta. Kot tak lahko neposredno dodate Odbor
komponento v podokno z vsebino aplikacije Swing.
Odbor
izjavlja SQUAREDIM
in BOARDDIM
konstante, ki določajo dimenzije pikslov kvadrata in kontrolne plošče. Pri inicializaciji SQUAREDIM
, Prikličem Checker.getDimension ()
namesto dostopa do enakovredne javnosti Checker
konstanten. Joshua Block odgovarja, zakaj to počnem, v postavki št. 30 (namesto int
konstante) druge izdaje njegove knjige, Učinkovita Java: "Programi, ki uporabljajo int
enum vzorec so krhki. Ker int
enumi so konstante časa prevajanja in so zbrane v odjemalcih, ki jih uporabljajo. Če je int
povezana s konstanto enum spremenjena, je treba njene stranke ponovno sestaviti. Če niso, bodo še vedno kandidirali, vendar njihovo vedenje ne bo določeno. "
Zaradi obsežnih komentarjev o tem ne bi imel kaj več povedati Odbor
. Vendar upoštevajte ugnezdene PosCheck
razred, ki opisuje nameščen preglednik s shranjevanjem datoteke Checker
referenca in njene sredinske koordinate, ki so glede na zgornji levi kot Odbor
komponenta. Ko dodate Checker
ugovarjajo Odbor
, shranjeno je v novem PosCheck
predmet skupaj s središčnim položajem pregledovalnika, ki se izračuna iz podane vrstice in stolpca.
Seznam 3 predstavlja Checker
.