Megyen!

 ( tombenko | 2014. július 11., péntek - 0:10 )

Valaha régen azzal a céllal kezdtem el tanulni a Java nyelvet, hogy legyen egy használható keresztrejtvény-szerkesztő szoftverem. Nos, már van. Igaz, fapadosnak is gyalulatlan, de működik, és azt csinálja, amit akarok. Ugyan rengeteg még vele a tennivaló, de kellenek az ilyen apró örömök.
BATHORY - The Land
Manowar - Kingdom Come
Helloween - Kings will be Kings
Rhapsody of Fire - Dawn of Victory
Judas Priest - United
Hammerfall - Living in Victory

Hozzászólás megjelenítési lehetőségek

A választott hozzászólás megjelenítési mód a „Beállítás” gombbal rögzíthető.

Legalább egy-két screenshot? ;)

Ott tartok, hogy kirajzol egy hálózatot (még egyelőre nem is tetszőleges méretűt), meg lehet beleírni betűket és fekete négyzeteket. De ha kell: http://kepfeltoltes.hu/140712/CrossWordMaker_www.kepfeltoltes.hu_.png
--
Fight / For The Freedom / Fighting With Steel

Minél fapadosabb, legalább annál kevésbé bloat-abb..

Lehet a fapadságról eltérő elképzeléseink vannak, de nagyon gyakran találkozom "retek fapad de rohadt bloat" szoftverekkel, amiknél a fejlesztők láthatóan úgy vannak, hogy új feature-t programozni viccesebb, mint használhatóra megcsinálni a már létező feature-öket, úgyhogy semmi sem működik kényelmesen, de legalább bármi bármitől összeborul.

Úgyhogy tombenko: előbb legyen tökéletes, utána tudjon mindent ;) És ja, screenshotok-at please. Szerk: És természetesen grat.

BlackY
--
"en is amikor bejovok dolgozni, nem egy pc-t [..] kapcsolok be, hanem a mainframe-et..." (sj)

Forráskód is jöhet, mondjuk a github-on, ha nem üzleti titok. ;)

Feltöltöm, csak a kommenteket kicsit gatyába rázom.
Szerk.: Hubazmeg, ennyit balfaszkodni... De a lényeg - olvashatod. (A commitok a vi-s ismereteim hiányának eredményei egyelőre.)
Már csak arra kellene rájönnöm, hogy linkeljem ide. De biztosan van valami egyszerű módja...
--
Fight / For The Freedom / Fighting With Steel

https://github.com/tombenko/CrossWordMaker
Így jó lesz? ;)

ui: ha elég erős a géped, akkor próbáld ki a vi helyett pl. az eclipse-t vagy a netbeans-t! Azért ezekkel valamivel kényelmesebb a fordítgatás, ráadásul ezek ingyenesek.

Geany-vel dolgozom. Egyelőre. A vi a git miatt merült fel, de már magamhoz tértem.
Szerk.: Sejtettem, hogy megint agyonbonyolítok valamit... :)
--
Fight / For The Freedom / Fighting With Steel

Ha ez vigasztal, nekem is ráment anno pár órám, mire rájöttem, hogy azért nincs google találat, mert ott van az URL-ben :D

Igyekszem.
És természetesen kösz.
--
Fight / For The Freedom / Fighting With Steel

Építő jellegű kritika, javaslatok jöhetnek?

1. Jelenleg támogatott, de csúnyán néznek miatta kategóriás, ha az AWT Thread-en kívülről dolgozol UI osztályokkal (néhány Swing Look&Feel speciel ezt egy exception-nel köszöni meg). Az ajánlás a UI kód bootstrap-jére a main()-ből a SwingUtilities.invokeLater vagy SwingUtilities.invokeAndWait hívása.

SwingUtilities.invokeLater(new Runnable() {
    public void run() {
       new WorkingWindow();
    }
});

2. Valszeg az OCD beszél belőlem, de a WorkingWindow ne jelenítse meg magát, bízd a hívó félre (new WorkingDisplay().setVisible(true);)

3. Több helyen is van, hogy int tömbbel kezelsz koordinátákat, miközben van beépített Point osztály - memóriahasználatban kb. ugyanott leszel, viszont később nem kell azonnal agyalnod, hogy melyik koordinátát melyik indexhez rendelted

4. Írod egy helyen az egérkezelést, a mostani kézzel rajzolgatóssal megoldással ezzel szerintem sokat fogsz szívni, amikor elég bátornak érzed magad, megpróbálhatsz elveszni a JTable és kapcsolódó osztályok körében (nem egy egyszerű interfész, az biztos) - addig is hagyd meg egyszerűre a WorkingPanel interfészét (setPosition/setLetter).

BlackY
--
"en is amikor bejovok dolgozni, nem egy pc-t [..] kapcsolok be, hanem a mainframe-et..." (sj)

Igen, természetesen. Sőt...
4; Nézegettem a JTable-t, egyelőre még a mérleg rossz oldalán van. Ha az interfészbe bele kell nyúljak, akkor viszont már nem biztos.
3; Ez tetszik, meg is nézem. Az ilyenek nagyon jól jönnek, köszi!
1-2; Nem teljesen értem az okát, úgyhogy egy kis magyarázatot kérek.
--
Fight / For The Freedom / Fighting With Steel

Az 1-es: amikor létrehozol UI elemeket (pontosabban ha jól emlékszem legkésőbb akkor, amikor pack()-el lefoglalod a rendszertől őket), a háttérben létrejön egy új szál, ami a UI eseményeket dolgozza fel, az AWT Event thread. Innentől kezdve - akarva, akaratlanul - többszálú a programod, ha más nem addig, amíg a main() függvényed nem végez - az eseménykezelőket az event thread-ben futtatja az AWT (egy eseménykezelőben levő Thread.sleep hívás pl. szépen meg tudja fagyasztani az UI-t).

És ebből következik az, hogy nem "illik" más thread-ből módosítani az UI elemek állapotát, mert ezzel bejön a képbe a szinkronizáció összes problémája. A SwingUtilities osztályban van két metódus (invokeLater és invokeAndWait), amikkel át tudsz adni egy Runnable példányt, hogy amikor éppen nincs dolga az AWT Event Thread-nek, akkor futtassa le őket - az előbbi azonnal vissza is tér, az invokeAndWait pedig vár, amíg ténylegesen le is futott (vagy dob egy kivételt, ha gond volt).

Így pl. egy fájl mentés (tipikus "lassú I/O" feladat) úgy néz ki, hogy a gombra kattintásra lefut az AWT Thread-ben az eseménykezelő, ami mondjuk megjelenít egy progress bar-t aztán indít egy új Thread-et és vissza is tér. Az új Thread pl. kiírt blokkonként invokeLater-rel bedobja az event queue-ba, hogy "akkor a progress bar-t egy kicsit növelni kéne" (eredmény szempontjából mindegy, hogy mikor fut le és növeli a progree bar-t, a feldolgozás sorrendje miatt monoton nőni fog), és a fájl lezárása után invokeAndWait-el eltünteti a progress bar-t - ezzel a szál meg is szűnik.

A Look & Feel dolog: a Swing nem feltétlenül az OS natív UI komponenseit használja (az AWT-tal és a "közös nevező" megoldással ezt csúnyán megszívták), nagyon sok komponens önmagát rajzolja ki - egy külön nyilvántartott téma alapján, ezek az ún. Look and Feel-ek. Többnyire szokott lenni a rendszerbe illő téma (UIManager.getSystemLookandFeel), van a default (azt hiszem Metal a neve), de sajátot is telepíthetsz ill. futás közben lekérdezheted a telepített LaF-ok listáját (http://docs.oracle.com/javase/7/docs/api/javax/swing/UIManager.html#getInstalledLookAndFeels()).
Nem akarok hülyeséget mondani, mert nem emlékszem, hogy pontosan melyik look and feel volt, de van olyan, ami minden publikus metódusban azzal nyit, hogy ellenőrzi, az AWT Event Thread-ben hívták-e (SwingUtilities.isEventDispatchThread() megmondja), és ha nem, akkor azonnal dob egy exceptiont.

A 2: tényleg csak annyi, hogy a konstruktor-ban ne tedd láthatóvá az ablakod, mert így később nem tudod majd kompozícióval más osztályokban használni - mert amint létrehozod a példányt, az meg is jelenik (ha nagyon sarkítani akarnék, akkor ez mellékhatásnak minősül). És nagyjából ipari standard a kívülről később nem hivatkozott ablakok létrehozására+megjelenítésére a new FooWindow().setVisible(true); forma, előbb-utóbb úgyis meg kell szoknod :)

BlackY
--
"en is amikor bejovok dolgozni, nem egy pc-t [..] kapcsolok be, hanem a mainframe-et..." (sj)

Tehát, ha jól értem, van egy szál, ami jelen esetben a WorkingWindow. Ebben van két eseménykezelő (egy KeyListener és egy MouseListener), amik minden eseménynél egy-egy új szálat indítanak, és ha ezek lefutottak, akkor foglalkozik a WorkingWindow megjelenésével?
2; Értem. Akkor igyekszem rákapni.
--
Fight / For The Freedom / Fighting With Steel

Nem igazán. Az összes eseménykezelőd egy szálban (az AWT Event Thread-ben) fut, ebből tudsz kézzel "kiugrálni" (*), ha valami hosszabb dolgot akarsz csinálni (többnyire I/O, de számításigényes feladatokat is ki lehet rakni - a lényeg, hogy az eseménykezelőd elég gyorsan lefusson ahhoz, hogy a felhasználó ne érezze úgy, hogy megfagyott a UI), a másik szálakból pedig - legegyszerűbben - a fenti két SwingUtilities metódussal tudsz "visszaugrani" hogy frissíteni tudd a UI felületet.

Indításkor - amikor elindul a CrossWordMaker.main() - még csak egy szálad van (a main thread). Az első verzióban akkor jött létre az AWT Event Thread, amikor a WorkingWindow konstruktorban meghívtad a pack()-ot, az új verzióban a SwingUtilities hívással létrehozod.

Szerk.: Alkalomadtán adj majd egy próbát az Eclipse-nek, szúrj be 1-1 helyre egy breakpointot, és indíts egy Debug run-t, a Debug panelen mindig szépen látszik, hogy éppen milyen szálak futnak, és az adott töréspontot melyik szálon érted el.

BlackY
(*): A legegyszerűbben úgy, hogy az eseménykezelőbe teszel egy new Thread(fooRunnable).start() hívást, de így könnyen tudsz túl sok szálat indítani, érdmesebb helyette a SwingWorker osztályt megnézni (kulcsrakész), vagy ha nagyobb kontrollra van szükséged, akkor az Executor interfészt. A SwingWorker osztály doksija egész olvasmányosra sikerült: http://docs.oracle.com/javase/7/docs/api/javax/swing/SwingWorker.html
--
"en is amikor bejovok dolgozni, nem egy pc-t [..] kapcsolok be, hanem a mainframe-et..." (sj)

"Igen, természetesen. Sőt..."

Átnéztem én is. :)

  1. Amikor osztályról, metódusról, paraméterről írod le, hogy mit csinál, az lehetne javadoc is.
  2. A Fields, Constructors, Methods kommentek szerintem feleslegesek, de ez egyéni ízlés kérdése is, végülis nem bántanak senkit.
  3. Nem használsz package-eket, ami egyelőre nem gond, de előbb-utóbb nem árthat, pl. én külön package-be raknám a guit és a modellt. Illetve a default package-be nem szerencsés kódot rakni, pl. névütközéseknél problémás lehet.
  4. Az előző ponthoz kapcsolódik, az osztályaid, metódusaid mind package private (modifier nélküli) láthatóságúak, érdemes mindig a célodnak megfelelő láthatóságokat használni, de ez csak onnantól érdekes, ha több package-ed van.
  5. A CrossWord osztályban a Row és Column eltárolásának jelenleg nem látom sok hasznát, egyrészt mert nem használod, másrészt mert javaban a tömbök tárolják magukról a méretüket a length attribútumban. Persze van, amikor nem tudod garantálni, hogy a tömb le is van foglalva, vagy épp egy mátrix egyik dimenziója nulla elemű, ekkor van értelme letárolni, különben elveszne az információ.
  6. A Square osztály jelenleg több felelősséggel rendelkezik. Egyszerre lehet benne karaktereket, számokat, fekete négyzeteket, határvonalakat tárolni. A problémára alapvetően a leszármazás a megoldás, vagyis a Square egy abstract osztály vagy interface, és ennek vannak konkrét implementációi, pl. CharacterSquare, NumberSquare, BlackSquare, sőt, EmptySquare. A határvonalakhoz én a decorator patternt használnám. Ez a változtatás annyi módosítást hozna magával, hogy futásidőben kellene cserélgetni a modellben tárolt objektmokat, de ez mindössze 2-3 sor módosítást jelentene a CrossWord osztályban. Ha érdekel, ezt részletesebben is ki tudom fejteni.
  7. A Square osztályban a határvonalakat egy bitmaszkkal tárolod. Ez alapvetően nem rossz megoldás, viszont az annak állítgatására kitalált módszered nem épp fejlesztő barát. A witch byte-ról ugyanis ki kell deríteni, hogy milyen sorrendben kódolod benne az oldalakat, mi történik, ha egynél több bit 1 benne, illetve mi a metódus szemantikája (esetedben xor). Ehhez mind az implementációt kell nézegetni vagy dokumentációt kell olvasni, röviden nem egyértelmű az api. Valamivel szebb megoldás lenne, ha egyrészt a setSideLine helyett mondjuk toggleSideLine lenne a metódus neve (ezzel máris tudom, hogy xor van mögötte), másrészt valahogy nevesíted az oldalakat. Erre C-ben az a megoldás, hogy definiálsz 4 konstans byteot a 4 oldalhoz, amiket akár vegyíthetsz is bitenkénti vaggyal. Javaban szerintem viszont szebb, ha enumot vársz, amiben szintén nevesítve van a 4 oldal. Az enum javaban egyben osztály is, vagyis minden oldalhoz letárolhatod, hogy milyen byte érték tartozik hozzá, vagyis a metódusban továbbra is megmaradhat az xor, csak az enumból kell kivenni a which byteot. Nem tudom ez így mennyire érthető, ezt is kifejtem jobban ha érdekel.

Két kiegészítés:
5. Az Eclipse-ben az opciók közt bekapcsolhatod, hogy figyelmeztessen a nem használt tagváltozókra, hasznos. A másik, ami csak most tűnt fel: a tagváltozókra is érvényes a Java style guide szerint a lowerCamelCase írásmód, te a C#-féle UpperCamelCase-t használod.

7. ha Enum-okat használsz, akkor dobhatod is akár a bitmágiát (nem tudom, nekem mindig a C-s rémálmok jutnak róla eszembe - ha használni is kell, valami wrapper osztályba zárom) és használhatod az EnumSet osztályt - itt már nőni fog a memória igényed (plusz egy objektum, int helyett long, de 64 elem alatt ugyanúgy bitműveletekkel implementált RegularEnumSet-et kapsz), cserébe "ingyen" kapsz jónéhány kényelmi függvényt is (a halmazműveletek felett pl. iterátort, ilyesmiket)

BlackY
--
"en is amikor bejovok dolgozni, nem egy pc-t [..] kapcsolok be, hanem a mainframe-et..." (sj)

5; Írásmódot tudok váltani, semmibe nem kerül.
7; EZt is megnézem.
--
Fight / For The Freedom / Fighting With Steel

1; Mindig keverem, hogy melyik melyik, eredetileg javadoc akart lenni. Van ilyen...
2; Egyelőre nekem szamárvezető. Sajnos tapasztalom, hogy ha nem írom magamnak elő, akkor eléggé áttekinthetetlenné tudom varázsolni.
3; Éppen próbálok zöld ágra vergődni velük, már régen gondoltam rá.
4; -> 3
5; Ezt megfogadom, mert tudom garantálni.
6; Nem véletlenül: egy négyzetben főleg az van, hogy betű vagy fekete. Az első esetben még mellé kerülhet egy szám, ami a szókezdetet jelölheti (pl. a hagyományos keresztrejtvényben), egy szám, ami a betűre hivatkozik (a betűkirakósban), illetve a négyzeteket elválasztó vastag vonalak. Ez mind ugyanoda tartozik, úgyhogy nem látom értelmét külön osztályokat csinálni rájuk. Ha fekete négyzet, akkor értelemszerűen nem számít mindez. Esetleg azt lehet, hogy fekete vagy információs négyzet, előbbi esetben minden érték értelemszerűen nulla, így lehetnek ugyanannak az osztálynak a leszármazottjai.
7; Ez megint jó, tetszik. Ha jól értem, így akkor lehet nyugodtan névvel hivatkozni az oldalakra. A metódus nevére tett ajánlatodat szintén megfogadom. Szerintem értem, ha mégsem, még zaklatlak majd.
--
Fight / For The Freedom / Fighting With Steel

1: javadoc az, ami /**-gal kezdődik, nyilván egy rakás szabállyal együtt. IDE-k tudnak olyat, hogy generate javadoc, és akkor csak ki kell tölteni az egyes részeket a kommentben.
2: Hangsúlyozom, hogy ez tényleg ízlés kérdése. Egyébként erre is jó egy IDE, gyakorlatilag úgy tudsz ugrálni a fieldekre, metódsokra, mintha linkek lennének egy weboldalon és akkor nem kell szemmel greppelni.
6: Na akkor alaposan félreértettem. :) Így már oké, hogy a számok, betűk egyben vannak. Az elválasztóval lehet vitatkozni, mert az nem kimondottan egy mezőre vonatkozik, inkább két mező határára, de most hirtelen nekem se jut eszembe jobb megoldás, amivel nem bonyolítod túl a modelled.
7: Javaban az enumokkal nevesített singleton objektumokat lehet gyártani, általában akkor használjuk ha több ilyen kell egy adott típusból.

"Nevesített singleton"!? A Singletonnak pont az a lényege, hogy csak egy van belőle :) [a Wikipedia szerint az Effective Java szerint Enum-okkal lehet a leghatékonyabban singleton osztályt csinálni]

Gyakorlatilag egy final osztály, aminek csak privát konstruktora van/lehet, és public static adattagokban teszi elérhetővé a példányokat (+/- egy két fordítási idejű móka az ordinal-lal és az osztályon belüli példányosítás letiltására, hasonlók).

Szerk.: Megvan amit kerestem: Multiton pattern - bár az meg azért nem illik ide, mert ott futási időben kéne nevesíteni a példányokat.

BlackY
--
"en is amikor bejovok dolgozni, nem egy pc-t [..] kapcsolok be, hanem a mainframe-et..." (sj)

Picit siettem, nem gondoltam végig teljesen hogy mit írtam, jó nagy hülyeség is lett belőle. :) Nagyjából azt akartam leírni, hogy (ha csak nem trükközöl), akkor csak azok a példányai jönnek létre on demand, amiket az enumban névvel felsoroltál. A singletonra ez annyiban hasonlít, hogy ott is korlátos számú példányod lesz, és az is sokszor on demand van létrehozva.

Megnyugodtam, majd beledobok még csillagokat. Az elválasztóvonalat esetleg még úgy lehetne egyszerűsíteni, hogy csak jobb oldali meg alsó van, így még talán néhány hibalehetőséget is ki lehet irtani. Gondolok itt arra, hogy két, egymással érintkező mező közös vonalára az egyikben rakok vonalat, a másikban meg nem, akkor kicsit nehéz eldönteni, hogy melyikhez is tartozik.
7; Na, ezt fejtsd ki légy szíves, mert egyszerűen nem értem.
--
Fight / For The Freedom / Fighting With Steel

Ez nem vonzana jobban (ruby + shoes)? Ahogy elnézem a kódot, a szívásfaktor 3 nagyságrenddel kisebb lenne? Már ha hobbi. Ha Java fejlesztőnek készülsz akkor nem szóltam.

http://shoesrb.com/walkthrough.html

Fene tudja. Rubyval még nem próbálkoztam, de lehet, hogy megpróbálom.
--
Fight / For The Freedom / Fighting With Steel