Interpreteerimisest üldisemalt
Siin aines käsitleme interpretaatoreid eelkõige selleks, et programmidest paremini aru saada ja seega ehitada oma programmeerimisoksus tugevamale põhjale. Samas tasub rõhutada, et avaldised ei ole ainsad asjad maailmas, mida võiks "interpreteerida". Käsude täitmine on üks võimalus eraldada äriloogika (mida peab tegema?) ja selle implementatsioonist. Vaatame siin lihtsa näite läbi, kus ühtlasi saab illustreerida ka ANTLRi mõningaid võimalusi. Kood asub paketis week8.interdemo.spammer.
Nagu ikka selliste eluliste ülesannete puhul on meil vaja kedagi aidata. Seekord on hädas testimisega sõbralik spämmija Stefan, kes tahab kõikidele oma nimekirjas olevatele inimestele sõnumeid saata. Tal on mõnede jaoks telefoninumber ja mõnedel ainult e-mail. Sisendfail näeb tal välja järgmine:
Vesal Vojdani: vesal.vojdani@spammail.com Varmo Vene: +371 666 6666
Kuna päris rämpsposti saatmine ei ole võib-olla Ülikoolis õpetatava näitena kõige parem mõte, siis peab siin kutsuma klassi Relay meetodeid sendMail ja sendSMS. Need trükivad lihtsalt midagi ekraanile spämmi saatmise asemel.
Otse ANTLRiga
Spämmija Stefan on tubli AKT tudeng ja oskab isegi kasutada ANTLRi kuulajad, et väga kiiresti sellise suurepärase lahenduse kirjutada. Grammatika on defineeritud failis week8/interdemo/spammer/Spammer.g4 ja spämmi saatmise kood on klassi SpammerAst meetodis sendSpam:
SpammerBaseListener listener = new SpammerBaseListener() { @Override public void exitEmail(EmailContext ctx) { Relay.sendMail(ctx.EMAIL().getText(), ctx.NAME().getText(), MSG); } @Override public void exitPhone(PhoneContext ctx) { String numStr = ctx.NUMBER().getText(); long number = Long.parseLong(numStr.replaceAll("[+ -]", "")); Relay.sendSMS(number, ctx.NAME().getText(), MSG); } };
Stefan kasutab siin ANTLRi listener liidest ja seetõttu sai väga kiiresti selle asja valmis, aga selle koodi testimine on üks igavene nuhtlus. Üks variant on meie Relay klassi mockida ja seeläbi kontrollida, et kutsutakse õigeid asju, aga palju lihtsam on tegelikult siin kasutada interpretaatorit!
Sinu ülesanne: interpretaatori abil
Interpretaatori mustri peamine mõtte on sellise vaheandmestruktuuri loomine, mille sisse saame kergesti vaadata ja seega kontrollida, et plaanitud tegevused on õiged. Meil on paketis spammer.ast kahte tüüpi käske:
- MailCmd (konstruktor võtab e-mail aadressi)
- SmsCmd (konstruktor võtab telefoninumbri)
Nad on siis mõlemad RelayCommands alamklassid, mistõttu interpretaator ainult kutsub nende send meetodit:
public static void interpret(List<RelayCommands> commands) { for (RelayCommands cmd : commands) cmd.send(); }
Sinu ülesanne on siis implementeerida klassi SpammerAst meetodid createAst, mis loob sellise RelayCommandide list, mille täitmisel me saaks sama tulemuse kui Stefani lahendus! Sellise listi puhul on aga palju lihtsam testida, et kõik soovitud tegevused on nagu oodatud. Üks lihtne test ongi klassis SpammerAstTest, millega saad proovida, et Sul on spämmeri loogika õigesti implementeeritud.