Pred niekoľkými mesiacmi som bol požiadaný, aby som vytvoril malú knižnicu Java, ku ktorej bude mať prístup aplikácia, ktorá vykreslí grafické používateľské rozhranie (GUI) pre hru Checkers. Rovnako ako vykreslenie šachovnice a dámy musí grafické užívateľské rozhranie umožniť pretiahnutie dámy z jedného štvorca na druhý. Kontrola musí byť tiež sústredená na štvorec a nesmie byť priradená k štvorcu, ktorý je obsadený iným hráčom. V tomto príspevku predstavujem svoju knižnicu.
Návrh knižnice GUI pre dámu
Aké verejné typy by mala knižnica podporovať? V dámach každý z dvoch hráčov striedavo posúva jednu zo svojich bežných (iných ako kráľových) dám po doske iba smerom dopredu a prípadne preskočí dámu druhého hráča. Keď dáma dorazí na druhú stranu, je povýšený na kráľa, ktorý sa môže pohybovať aj dozadu. Z tohto popisu môžeme odvodiť nasledujúce typy:
Doska
Kontrola
CheckerType
Prehrávač
A Doska
objekt identifikuje šachovnicu. Slúži ako nádoba na Kontrola
predmety, ktoré zaberajú rôzne štvorce. Môže sa nakresliť a požiadať, aby každá obsahovala Kontrola
objekt sa nakreslí sám.
A Kontrola
objekt identifikuje kontrolu. Má farbu a označenie, či ide o bežnú kontrolu alebo kontrolu kráľa. Môže sa nakresliť a sprístupniť svoju veľkosť Doska
, na ktorého veľkosť má vplyv Kontrola
veľkosť.
CheckerType
je výčet, ktorý identifikuje farbu a typ kontroly pomocou svojich štyroch konštánt: BLACK_KING
, BLACK_REGULAR
, RED_KING
a RED_REGULAR
.
A Prehrávač
objekt je ovládač pre pohyb dáma s voliteľnými skokmi. Pretože som sa rozhodol implementovať túto hru do Swingu, Prehrávač
nie je potrebné. Namiesto toho som sa otočil Doska
do komponentu Swing, ktorého konštruktor registruje myš a poslucháčov pohybu myší, ktorí v mene ľudského hráča zvládajú pohyb kontroly. V budúcnosti by som mohol implementovať počítačový prehrávač pomocou iného vlákna, synchronizátora a iného Doska
metóda (ako napr presunúť ()
).
Čo robia verejné API Doska
a Kontrola
prispieť? Po chvíli premýšľania som prišiel s nasledujúcou verejnosťou Doska
API:
Doska ()
: Zostrojte aDoska
objekt. Konštruktér vykonáva rôzne inicializačné úlohy, napríklad registráciu poslucháča.void add (Kontrola kontroly, riadok int, stĺpec int)
: Pridaťkontrola
doDoska
v polohe označenejriadok
astĺpec
. Riadok a stĺpec sú hodnoty založené na 1, na rozdiel od hodnoty 0 (pozri obrázok 1). Thepridať ()
hodíjava.lang.IllegalArgumentException
keď je argument jeho riadku alebo stĺpca menší ako 1 alebo väčší ako 8. Taktiež hodí nezačiarknutéAlreadyOccupiedException
keď sa pokúsite pridať aKontrola
na obsadené námestie.Dimenzia getPreferredSize ()
: VrátiťDoska
preferovaná veľkosť komponentu na účely rozloženia.
Obrázok 1. Ľavý horný roh šachovnice je umiestnený na (1, 1)
Taktiež som vyvinul nasledujúcu verejnosť Kontrola
API:
Kontrola (CheckerType checkerType)
: Zostrojte aKontrola
objekt zadanéhocheckerType
(BLACK_KING
,BLACK_REGULAR
,RED_KING
aleboRED_REGULAR
).void draw (Grafika g, int cx, int cy)
: NakresliKontrola
pomocou zadaného grafického kontextug
so stredom kontroly umiestnenej na (cx
,cy
). Táto metóda je určená na volanie zDoska
iba.boolean obsahuje (int x, int y, int cx, int cy)
: Astatický
pomocná metóda volaná zDoska
ktorá určuje, či súradnice myši (X
,r
) ležia vnútri kontroly, ktorej stredové súradnice sú špecifikované (cx
,cy
) a ktorého rozmer je uvedený inde na interneteKontrola
trieda.int getDimension ()
: Astatický
pomocná metóda volaná zDoska
ktorá určuje veľkosť šachovnice, aby doska mohla zodpovedajúcim spôsobom zmenšiť svoje štvorce a celkovú veľkosť.
Toto do značnej miery pokrýva všetky knižnice GUI dámy z hľadiska ich typov a ich verejných API. Teraz sa zameriame na to, ako som implementoval túto knižnicu.
Implementácia knižnice GUI dámy
Knižnica GUI dámy pozostáva zo štyroch verejných typov umiestnených v zdrojových súboroch s rovnakým názvom: AlreadyOccupiedException
, Doska
, Kontrola
a CheckerType
. Zoznam 1 darčekov AlreadyOccupiedException
zdrojový kód.
Zoznam 1. AlreadyOccupiedException.java
public class AlreadyOccupiedException rozširuje RuntimeException {public AlreadyOccupiedException (String msg) {super (msg); }}
AlreadyOccupiedException
predlžuje java.lang.RuntimeException
, čo robí AlreadyOccupiedException
nekontrolovaná výnimka (nemusí byť zachytená alebo deklarovaná v a hodí
doložka). Keby som chcel vyrobiť AlreadyOccupiedException
skontrolované, predĺžil by som java.lang.Výnimka
. Tento typ som sa rozhodol zrušiť začiarknutie, pretože funguje podobne ako nezačiarknuté IllegalArgumentException
.
AlreadyOccupiedException
deklaruje konštruktor, ktorý prevezme reťazcový argument popisujúci dôvod výnimky. Tento argument je postúpený RuntimeException
nadtrieda.
Zoznam 2 darčekov Doska
.
Zoznam 2. Board.java
import java.awt.Color; import java.awt.Dimension; import java.awt.Grafika; import java.awt.Graphics2D; importovať java.awt.RenderingHints; import java.awt.event.MouseEvent; import java.awt.event.MouseAdapter; import java.awt.event.MouseMotionAdapter; import java.util.ArrayList; import java.util.List; import javax.swing.JComponent; rada verejnej triedy rozširuje JComponent {// rozmer štvorca šachovnice (o 25% väčší ako šachovnica) súkromná konečná statická int SQUAREDIM = (int) (Checker.getDimension () * 1,25); // rozmer šachovnice (šírka 8 štvorcov) súkromná konečná int BOARDDIM = 8 * SQUAREDIM; // preferovaná veľkosť komponentu dosky private Dimension dimPrefSize; // pretiahnutie príznaku - nastavený na true, keď užívateľ stlačí tlačidlo myši nad kontrolou // a vynulovaný na false, keď užívateľ pustí tlačidlo myši private boolean inDrag = false; // posun medzi súradnicami začiatku pretiahnutia a súradnicami stredu checkeru private int deltax, deltay; // odkaz na kontrolu polohy na začiatku pretiahnutia súkromné PosCheck posCheck; // umiestnenie stredu checkeru na začiatku pretiahnutia private int oldcx, oldcy; // zoznam objektov Checker a ich počiatočných pozícií private List posChecks; public Board () {posChecks = new ArrayList (); dimPrefSize = nová dimenzia (BOARDDIM, BOARDDIM); addMouseListener (new MouseAdaptér) pod stlačením myši. for (PosCheck posCheck: posChecks) if (Checker.contains (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; návrat;}} @Override public void mouseReleased (MouseEvent me) {// Keď myš pustíte, vymažte inDrag (aby // neznamenalo pretiahnutie prebieha) ak je inDrag // už nastavený. if (inDrag) inDrag = false; else return; // Kontrola Snapov do stredu štvorca. int x = me.getX (); int y = me.getY (); posCheck .cx = (x - deltax) / SQUAREDIM * SQUAREDIM + SQUAREDIM / 2; posCheck.cy = (y - deltay) / SQUAREDIM * SQUAREDIM + SQUAREDIM / 2; // Neposúvajte šachovnicu na obsadený štvorec. pre (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 = oldcy; } posCheck = null; premaľovať (); }}); // Pripojenie poslucháča pohybu myši k appletu. Tento poslucháč počúva // udalosti pretiahnutia myšou. addMouseMotionListener (nový MouseMotionAdapter () {@Override public void mouseDragging (MouseEvent me) {if (inDrag) {// Aktualizácia umiestnenia centra kontroly. posCheck.cx = me.getX () - deltax; posCheck.cy = me.getY ( ) - deltay; prekresliť ();}}}); } public void add (Checker checker, int row, int col) {if (row 8) throw new IllegalArgumentException ("row out of range:" + row); if (col 8) throw new IllegalArgumentException ("col out of range:" + col); PosCheck posCheck = nový PosCheck (); posCheck.checker = kontrola; posCheck.cx = (stĺpec - 1) * SQUAREDIM + SQUAREDIM / 2; posCheck.cy = (riadok - 1) * SQUAREDIM + SQUAREDIM / 2; for (PosCheck _posCheck: posChecks) if (posCheck.cx == _posCheck.cx && posCheck.cy == _posCheck.cy) throw new AlreadyOccupiedException ("square at (" + row + "," + col + ") is employed") ); posChecks.add (posCheck); } @Override public Dimension getPreferredSize () {return dimPrefSize; } @Override protected void paintComponent (Grafika g) {paintCheckerBoard (g); pre (PosCheck posCheck: posChecks) if (posCheck! = Board.this.posCheck) posCheck.checker.draw (g, posCheck.cx, posCheck.cy); // Nakreslite ťahanú kontrolu naposledy, aby sa zobrazila nad akýmkoľvek základným // kontrolou. 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); // Maľovanie šachovnice. pre (int riadok = 0; riadok <8; riadok ++) {g.setColor (((riadok & 1)! = 0)? Color.BLACK: Color.WHITE); pre (int col = 0; col <8; col ++) {g.fillRect (col * SQUAREDIM, riadok * SQUAREDIM, SQUAREDIM, SQUAREDIM); g.setColor ((g.getColor () == Color.BLACK)? Color.WHITE: Color.BLACK); }}} // pomocná trieda pozičnej kontroly súkromná trieda PosCheck {verejná kontrola checkeru; public int cx; verejná int cy; }}
Doska
predlžuje javax.swing.JComponent
, čo robí Doska
komponent Swing. Ako taký môžete priamo pridať a Doska
komponent na tablu s obsahom aplikácie Swing.
Doska
vyhlasuje SQUAREDIM
a BOARDDIM
konštanty, ktoré identifikujú rozmery pixelov štvorca a šachovnice. Pri inicializácii SQUAREDIM
, Dovolávam sa Checker.getDimension ()
namiesto prístupu k ekvivalentnej verejnosti Kontrola
konštantný. Joshua Block odpovedá, prečo to robím v položke # 30 (namiesto int
konštanty) druhého vydania jeho knihy, Efektívna Java: "Programy, ktoré používajú int
enum vzor sú krehké. Pretože int
enums sú konštanty kompilácie, kompilujú sa do klientov, ktorí ich používajú. Ak int
spojené s konštantou enum sa zmení, je potrebné prekompilovať jej klientov. Ak nie, budú stále bežať, ale ich správanie bude nedefinované. ““
Kvôli rozsiahlym komentárom toho nemám veľa k slovu Doska
. Všimnite si však vnorené PosCheck
triedy, ktorá popisuje polohovú kontrolu uložením a Kontrola
odkaz a jeho stredové súradnice, ktoré sú relatívne k ľavému hornému rohu Doska
zložka. Keď pridáte a Kontrola
namietať proti Doska
, je uložený v novom PosCheck
objekt spolu so stredovou pozíciou kontroly, ktorá sa počíta zo zadaného riadku a stĺpca.
Zoznam 3 darčekov Kontrola
.