Programovanie

Dáma, niekto?

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_KINGa 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 a Doska 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 do Doska v polohe označenej riadok a stĺpec. Riadok a stĺpec sú hodnoty založené na 1, na rozdiel od hodnoty 0 (pozri obrázok 1). The pridať () 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ť a Kontrola 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 a Kontrola objekt zadaného checkerType (BLACK_KING, BLACK_REGULAR, RED_KINGalebo RED_REGULAR).
  • void draw (Grafika g, int cx, int cy): Nakresli Kontrola pomocou zadaného grafického kontextu g so stredom kontroly umiestnenej na (cx, cy). Táto metóda je určená na volanie z Doska iba.
  • boolean obsahuje (int x, int y, int cx, int cy): A statický pomocná metóda volaná z Doska 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 internete Kontrola trieda.
  • int getDimension (): A statický pomocná metóda volaná z Doska 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, Kontrolaa CheckerType. Zoznam 1 darčekov AlreadyOccupiedExceptionzdrojový 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.