-
Notifications
You must be signed in to change notification settings - Fork 67
mallivastaus ja arvosteluperusteet
1 a) (2p) Mitä sisältyy ohjelmiston vaatimusmäärittelyyn? (0.4 / kohta) eli 5 alla olevista antoi täydet pisteet.
Vaatimusmäärittelyn vaiheet mainittu:
(0.4) kartoitus
(0.4) analysointi
(0.4) dokumentointi
(0.4) validointi
(0.4) hallinnointi
Mitä vaatimusmäärittelyssä määritellään: (0.4) toiminnalliset vaatimukset (0.4) ja ei-toiminnalliset vaatimukset
b) (4p) Kerro mitä ketterään vaatimusmäärittelyyn liittyvillä termeillä User story ja Product backlog tarkoitetaan. Anna konkreettinen esimerkki oikeaoppisesta user storystä ja product backlogista.
Selitä User Story (1p):
(0.25) Kuvaa käyttäjän kannalta ohjelman arvokasta toiminnallisuutta. (0.25) Asiakkaan kielellä määritetty. (0.25) User Story toimii muistutuksena, että asiasta on vielä puhuttava asiakkaan kanssa ennen sen toteuttamista. (0.25) User Storyyn liittyy Storyn testaaminen.
Konkreettinen esimerkki oikeaoppisesta User Storystä (1p):
(0.25) Esimerkistä ilmenee estimointi. (0.25) Story kuvaa toimminnallista vaatimusta. (0.25) Storya on tarkennettu. (0.25) Storyn on oltava kyllin pieni.
Selitä Product Backlog (1p):
Product Backlog on priorisoitu lista asiakkaan tuotteelle asettamista vaatimuksista eli toivotuista ominaisuuksista ja toiminnoista. Nykyään Product Backlog koostuu yleensä User Storyistä.
Konkreettinen esimerkki oikeaoppisesta Product Backlogista (1p):
Konkreettisesta esimerkistä on tultava esille, että Product Backlog on: (0.33) Priorisoitu (0.33) User Storyjä on tarkennettu (0.33) User Storyt on estimoitu.
(0.33) jos mainitsi, että backlog on emergent, mutta jokin muu puuttui.
Vastauksesta tuli ilmetä seuraavat asiat (1p): (0.2) story on pilkottu taskeiksi (0.2) sisältää product backlogista asiakkaan kanssa valitut storyt kyseiseen sprinttiin. (0.2) Sprint backlogista näkyy missä vaiheessa mikäkin taski on. (0.2) Estimaattit (0.2) jäljelle jäänyt aika
Verifioinnissa varmistetaan että tuote on kehitetty vaatimustenmäärittelyn aikana sille asetettujen vaatimusten mukaisesti; “are we building the product right”. (1p, -0.5 jos vaatimuksia ei mainittu lainkaan)
Validoinnilla varmistetaan että tuote täyttää käyttäjän odotukset, mitä tuotteelta oikeasti halutaan; “are we building the right product”. (1p, -0.5 jos käyttäjää ei mainittu missään muodossa)
Verifioinnin ja validoinnin tavoitteena on varmistaa että tuote on riittävän hyvä käyttötarkoitukseensa.
Validoinnin tarkoituksena on varmistaa onko vaatimustenmäärittelyn mukainen tuote sellainen jonka asiakas haluaa ja tarvitsee. Ketterissä validointia tehdään koko tuotekehityksen ajan jokaisen sprintin päätteeksi järjestetyssä katselmoinnissa (sprint review).
Katselmoinnin tarkoituksena on lisätä läpinäkyvyyttä tuotteen kehitykseen ja kuka tahansa katselmointiin osallistuva saa antaa kehitysehdotuksia tuotteesta. Asiakkaan tulee kertoa vastaako lopputulos sitä mitä asiakas oikeasti halusi, vai tuleeko vaatimustenmäärittelyä muokata. Kehitysehdotuksien pohjalta tuotteelle voidaan myös lisätä uusia vaatimuksia product backlogiin.
pisteytys: 1p jatkuvasta validoinnista ja katselmoinnista ja 1p vaatimusten muokkaamisesta tarpeen vaatiessa.
Continuous integraatiolla tarkoitetaan kunkin kehittäjän tekemän työn integrointia, eli sisällyttämistä, muuhun koodiin mahdollisimman usein (vähintään päivittäin), tarkoituksena välttää “integrointihelvetti”. Työkaluna käytetään integrointipalvelinta, jossa suoritetaan kaikki ohjelmakoodin testit automaattisesti kun koodia siirretään versionhallintaan ja näin varmistetaan, että integroitava osa on tuotantokelpoinen. (1p, -0.5 jos testejä ei mainittu)
Continuous deployment on tästä askel eteenpäin, jossa onnistuneen integroinnin jälkeen tuote siirretään ns. staging palvelimelle, jossa ohjelma ajetaan mahdollisimman paljon tuotantopalvelinta vastaavissa olosuhteissa ja jossa ohjelmalle suoritetaan hyväksymätestit. Testit läpäistyään ohjelma siirretään (deployataan) tuotantopalvelimelle ja näin tuotannossa on aina uusin versio tuotteesta. Koko prosessi on automatisoitu. (1p, -0.5 jos staging palvelinta tai hyväksymätestejä ei mainittu)
Ohjelmiston suunnittelu tehdään yhden erillisen vaiheen sijaan vähitellen, iteraatio iteraatiolta. Alussa tehdään todennäköisesti vaan karkealla tasolla oleva arkkitehtuurisuunnittelu. Menetelmällä saatu arkkitehtuuri/design on useimmiten itseorganisoituvan tiimin omalla vastuulla.
Hyvää: Tiimillä on suurempi motivaatio sitoutua itse suunnittelemaansa arkkitehtuuriin (vrt norsunluutorni-arkkitehdin ulkoapäin pakottama arkkitehtuuri). Kuten vaatimusten suhteen, ketterässä on oletuksena, että myöskään arkkitehtuuria/suunnittelua ei pystytä etukäteen tekemään optimaalisesti, sillä tiimin tuntemus ongelma-alueeseen on vielä vajavainen. Näinollen parempi arkkitehtuuri/design saadaan aikaan muodostamalla se vähitellen prosessin kuluessa.
Huonoa: vaatii suurta kurinalaisuutta että ohjelman sisäinen laatu ei pääse rapistumaan. Tämä taas saattaa heijastua velositeetin alenemisena.
Pisteytys: 1p termin määrittelystä, 1p hyvistä ja huonoista puolista.
Backlog
luokalla on liikaa vastuita: storyjen arkistointi JA erilaisten listausformaattien tuottaminen
formaatin tuottamisen "kovakoodaamisen" (if-helvetti) takia laajennettavuus esim. uusien listausformaattien lisääminen vaikeutuu. metodi on myös turhan monella abstraktiotasolla liikkuva
luokalla turha riippuvuus BacklogReader
-luokkaan, testaus vaikeutuu
koodissa toisteisuutta eri kriteerein tapahtuvan storyjen listaamisen koodissa
UserStory
primitive obsession: taskit ja status on "koodattu" stringeinä, se ei ole järkevää. Taskeille tulee todennäköisesti jossain vaiheessa uusia ominaisuuksia (esim taskin estimaatti). Statuksen koodaaminen stringinä altistaa esim kirjoitusvirheille, jos olisi oma tyyppi statusten, esim. enum, tyyppitarkastus hoitaisi virheet. Ratkaisu olisi myös luettavuudeltaan parempi.
pisteytys: 0.5p per identifioitu rike. 4 bongausta riitti täysiin pisteisiin
(c)
Status ja Task omiksi luokikseen:
public enum Status{
TODO, STARTED, WAITING_FOR_TESTS, COMPLETED
}
public class Task {
private String name;
public Task(String name) {
this.name = name;
}
}
public class UserStory {
private int sprint;
private String name;
private int estimate;
private Status status;
private List<Task> tasks;
public UserStory(String name, int sprint, int estimate, Status status, List<Task> tasks) {
this.name = name;
this.sprint = sprint;
this.estimate = estimate;
this.status = status;
this.tasks = tasks;
}
public boolean done() {
return status == Status.COMPLETED;
}
public void changeStatus(Status status) {
this.status = status;
}
public int estimate() {
return estimate;
}
public Status status() {
return status;
}
public String name() {
return name;
}
}
Backlogiin geneerinen metodi, joka palauttaa ehdon toteuttavat storyt. Muut metodit käyttämään tätä. Oleellisesti tässä käytössä strategy-suunnittelumalli.
BacklogReader anntetaan konstruktoriparametrina, eli hyödynnetään riippuvuuksien injektointia.
Metodi export eriytetty omaksi luokaksi joka annetaan metodille export parametrina, tässäkin sovellus strategy-suunnittelumallin käytöstä. Metodi on myös nyt composed method -mallin mukainen, eli ei liiku monella eri abstraktiotasolla.
public class Backlog {
private List<UserStory> stories;
private BacklogReader reader;
public Backlog(BacklogReader reader) {
this.reader = reader;
stories = reader.readFromFile();
}
public List<UserStory> matching(Predicate<UserStory> condition) {
return stories.stream().filter(condition).collect(Collectors.toList());
}
public List<UserStory> completedStories() {
return matching(s -> s.done());
}
public List<UserStory> remainingStories() {
return matching(s -> !s.done());
}
public List<UserStory> epics() {
return matching(s -> s.estimate() > 20);
}
public String export(StoryFormater formater, boolean onlyRemaining) {
Filter filter = onlyRemaining ? s -> !s.done() : s -> true;
String stories = "";
// seuraavan voisi tehdä java8-onlinerillä mutta tällä kertaa perinteinen looppi
for (UserStory story : matching(filter)) {
stories += formater.format(story);
}
return formater.start() + stories + formater.end();
}
}
Formatoinnin hoitava luokkahierarkia:
public abstract class StoryFormater {
public String start(){
return "";
}
public String end(){
return "";
}
public abstract String format(UserStory story);
}
public class TextStoryFormater extends StoryFormater {
@Override
public String format(UserStory story) {
return story.name() + " " + story.estimate() + " " + story.status() + "\n";
}
}
public class XmlStoryFormater extends StoryFormater {
@Override
public String start() {
return "</story>\n";
}
@Override
public String format(UserStory story) {
return "<story>"
+ "<name>" + story.name() + "</name>"
+ "<estimate>" + story.estimate() + "</estimate>"
+ "<status>" + story.status() + "</status>"
+ "<story>\n";
}
@Override
public String end() {
return "</stories>";
}
}
Pisteytys: täysiin pisteisiin riitti järkevällä tavalla tehty refaktorointi Backlog-luokan copy-pastelle ja metodille export. Jos jompi kumpi puuttui, oli maksimi 2 pistettä. Kummankin päävian korjaamattomuus johti siihen että tehtävän maksimi oli 1 piste.