Anteckningar från föreläsning 9

Detta är den programkod vi skrev -- anteckningarna är gjorda bara för att vara ett stöd för minnet för dem som var där, och därför inte nödvändigtvis fullständiga eller ens sammanhängande. Betydligt mer hjälp finns att hämta i kompendiet.

OH-bilder finns här.

Vi började med att titta lite på den konto-klass som vi skrev förra gången, denna gång lät vi några av metoderna returnera värden:

class Account {

    private int accountNumber;
    private double balance;

    public Account  (int accNo) {
        accountNumber = accNo;
    }

    public int getAccountNumber() {
        return accountNumber;
    }

    public double getBalance() {
        return balance;
    }

    public double deposit(double amount) {
        if (amount < 0) {
            amount = 0;
        }
        balance += amount;
        return amount;
    }

    public double withdraw(double amount) {
        if (amount < 0) {
            amount = 0;
        }
        if (amount > balance) {
            amount = balance;
        }
        balance -= amount;
        return amount;
    }
}

Jag försökte förklara vad som egentligen händer när vi skapar ett konto, och ritade (ungefär) följande figur:

Därefter började vi skissa på en Bank-klass, och kom först fram till att vi ville använda en lista med konton i den:

class Bank {

    private List<Account> accounts = new ArrayList<Account>();

    public Bank  () {    // egentligen onödig
    }
}

Vi ritade en figur som visar en bank med några konton (det är ungefär denna slags figur ni skall rita i inlämningsuppgift 3):

Vi skrev sedan några av metoderna i klassen Bank:

class Bank {

    private List<Account> accounts = new ArrayList<Account>();

    public Bank  () {    // egentligen onödig
    }

    public boolean createAccount(int accNo) {
        if (hasAccount(accNo)) {
            return false;
        }
        accounts.add(new Account(accNo));
        return true;
    }

    boolean hasAccount(int accNo) {
        if (findAccount(accNo) == null) {
            return false;
        } else {
            return true;
        }
    }

    private Account findAccount(int accNo) {
        for (Account acc : accounts) {
            if (acc.getAccountNumber() == accNo) {
                return acc;
            }
        }
        return null;
    }

    public double deposit(int accNo, double amount) {
        Account acc = findAccount(accNo);
        if (acc == null) {
            return -1;
        }
        return acc.deposit(amount);
    }
    
    // ... mer kommer strax ...
}

Dessa metoder skulle vi kunna anropa från ett huvudprogram i stil med:

class BankProgram {

    public static void main(String[] args) {
        new BankProgram().run();
    }

    Bank bank = new Bank();

    void run() {
        while (true) {
            int command = menuChoice();
            switch (command) {
            case 1:
                createAccount();
                break;
            case 2:
                removeAccount();
                break;
            case 3:
                makeDeposition();
                break;
            // ... etc ...
            }
        }
    }

    void createAccount() {
        int accNo = Keyboard.nextInt("Ange kontonummer: ");
        if (bank.createAccount(accNo)) {
            System.out.println("OK");
        } else {
            System.out.println("Det gick inte att skapa kontot");
        }
    }

    // ... etc ...
}

Vi kan testa vårt program genom att köra bank-programmet ovan, mata in värden och se att vi får rätt resultat. Men en bättre lösning är att skriva ett program som testar både klassen Account och klassen Bank -- det finns en sådan klass här (det var den vi använde under föreläsningen).

För att kunna kompilera test-programmet var jag tvungen att deklarera samtliga Bank-metoder som skall skrivas (dvs de som anropas från test-programmet), och för enkelhets skull lät jag dem först returnera 0, eller något liknande:

class Bank {

    ... som tidigare ...

    public double getBalance(int accNo) {
        return 0;    // för att kunna kompilera testprogrammet
    }

    public double withdraw(int accNo, double amount) {
        return 0;    // för att kunna kompilera testprogrammet
    }
}

När vi kunde kompilera testprogrammet körde vi det, och det talade om för oss att vi inte fick ut rätt saldo när vi testade det (naturligtvis, eftersom vi bara returnerar 0) -- jag kompletterade därför metoden getBalance:

class Bank {

    ... som ovan ...

    public double getBalance(int accNo) {
        Account acc = findAccount(accNo);
        if (acc == null) {
            return Double.NEGATIVE_INFINITY;
        }
        return acc.getBalance();
    }

    public double withdraw(int accNo, double amount) {
        return 0;    // för att kunna kompilera testprogrammet
    }
}

När vi nu körde testerna fick vi veta att uttagen inte fungerade (återigen naturligtvis, eftersom vi ännu inte skrivit den) -- vi skrev därför den:

class Bank {

    ... som ovan ...

    public double withdraw(int accNo, double amount) {
        Account acc = findAccount(accNo);
        if (acc == null) {
            return Double.NEGATIVE_INFINITY;
        }
        return acc.withdraw(amount);
    }
}

När vi nu körde programmet gick samtliga tester igenom. Detta sätt att programmera -- dvs att först skriva ett test-program och sedan skriva det egentliga programmet -- kallas ibland test driven design. När vi skriver test-programmet upptäcker vi hur vi vill kunna anropa de ingående klasserna, och det hjälper oss att designa dem bättre. Dessutom får vi ett sätt att automatiskt kontrollera att vårt program hela tiden fungerar (även om vi skulle göra ändringar i det).

Om det är något du undrar över så är du hjärtligt välkommen till tisdagens extragenomgång (se schemat på kurshemsidan).