Υλοποίηση εκτενών συστημάτων

Διομήδης Σπινέλλης
Τμήμα Διοικητικής Επιστήμης και Τεχνολογίας
Οικονομικό Πανεπιστήμιο Αθηνών
dds@aueb.gr

Χειρισμός λαθών και εξαιρέσεις

Παράσταση εξαιρέσεων

Παράδειγμα παράστασης εξαιρέσεων

import java.io.*;

public class NonGreekCharacterException extends IOException {
    public NonGreekCharacterException() {}
    public NonGreekCharacterException(String msg) {
        super(msg);
    }
}

Δημιουργία εξαιρέσεων

Στη γλώσσα Java μια εξαίρεση μπορεί να δημιουργηθεί:
  1. Ρητά με την εντολή throw
    if (ammount < 0)
        throw new NegativeAmmountException();
    
  2. Με την κλήση μιας μεθόδου που προκαλεί την εξαίρεση. Για παράδειγμα η μέθοδος print μπορεί να προκαλέσει την NullPointerException.
  3. Με την εκτέλεση κώδικα που δημιουργεί εξαίρεση. Για παράδειγμα ο παρακάτω κώδικας θα δημιουργήσει μια εξαίρεση ArrayIndexOutOfBoundsException.
    int a[] = new int[10];
    a[15] = 42;
    

Χειρισμός εξαιρέσεων

Σε μια μέθοδο της Java μια εξαίρεση μπορούμε:

Τοπικός χειρισμός

Ο τοπικός χειρισμός γίνεται με μπλοκ try/catch/finally:

try {
    // Κώδικας που μπορεί να δημιουργήσει την εξαίρεση
} catch (ExceptionClass_1 e) {
    // Κώδικας που χειρίζεται την ExceptionClass_1
} catch (ExceptionClass_2 e) {
    // Κώδικας που χειρίζεται την ExceptionClass_2
} finally {
    // Κώδικας που εκτελείται πάντα στο τέλος
}

Παράδειγμα από τη μέθοδο readInt της βιβλιοθήκης BIO:

public static int readInt() {
    int i = 0;

    try {
        i = Integer.parseInt(readString());
    } catch (NumberFormatException e) {
        System.err.println("Error reading Integer: " + e);
        System.exit(1);
    }
    return (i);
}

Δήλωση εξαιρέσεων

Αν μια δική μας μέθοδος τότε πρέπει να το δηλώσουμε με τη σύνταξη
myMethodName( /* ... */) throws Exception_1, Exception_2 { }
Παράδειγμα:
public static int parsePositiveInt(String s) throws NumberFormatException {
    int i = parseInt(s);
    if (i < 0)
        throw new NumberFormatException();
}

Ολοκληρωμένο παράδειγμα εξαιρέσεων

class BadArgException extends Throwable {
    public BadArgException() {}
    public BadArgException(String msg) {
        super(msg);
    }
}

class Test {

    /** Verify that the args table is not empty.
     * @throws BadArgException if the table is empty.
     */
    static void verifyArgs(String args[]) throws BadArgException {
        if (args.length == 0)
            throw new BadArgException("Empty table");
    }

    static public void main(String args[]) {
        int exitCode = 0;

        try {
            int i;

            verifyArgs(args);
            for (i = 0; i < args.length; i++)
                System.out.print(args[i]);
            System.out.println();
        } catch (BadArgException e) {
            System.err.println("Bad argument " + e);
            exitCode = 1;
        } finally {
            System.out.println("Argument processing done");
        }
        System.out.println("Program termination");
        System.exit(exitCode);
    }
}

Σχεδιασμός με εξαιρέσεις

Ισχυρισμοί

Ισχυρισμοί στην πράξη

Ισχυρισμοί στη Java

Παράδειγμα χρήσης ισχυρισμών

/*
 * Run With java -ea FindMax
 */

class FindMax {

    /** Return the maximum number in non-empty array v */
    public static int findMax(int v[]) {
        int max = Integer.MIN_VALUE;

        // Precondition: v[] is not empty
        assert v.length > 0 : "v[] is empty";
        // Precondition: max <= v[i] for every i
        for (int i = 0; i < v.length; i++)
            assert max <= v[i] : "Found value < MIN_VALUE";

        // Locate the real maximum value
        for (int i = 0; i < v.length; i++)
            if (v[i] > max)
                max = v[i];

        // Postcondition: max >= v[i] for every i
        for (int i = 0; i < v.length; i++)
            assert max >= v[i] : "Found value > max";
        return max;
    }

    // Test harness
    public static void main(String argv[]) {
        int t[] = new int[5];

        t[0] = 4;
        t[1] = -4;
        t[2] = 145;
        t[3] = 0;
        t[4] = Integer.MIN_VALUE;
        System.out.println("Max value is " + findMax(t));
    }
}

Διεπαφές

Διεπαφές: κανόνες

Υλοποίηση διεπαφών

Διάγραμμα υλοποίησης διεπαφών

Multiple implementation UML diagram

Υλοποίηση πολλαπλών διεπαφών

Μια κλάση μπορεί να υλοποιήσει πολλές διεπαφές.
class Car implements
    VehicleBody,
    InternalCombustionEngine,
    TransmissionSystem,
    BreakSystem,
    Console
{
}

class Truck implements
    VehicleBody,
    InternalCombustionEngine,
    TransmissionSystem,
    BreakSystem,
    Console,
    Container
{
}

Διάγραμμα υλοποίησης πολλαπλών διεπαφών

Multiple inheritance UML diagram

Επέκταση διεπαφών

Διαφορά αφηρημένης κλάσης από μια διεπαφή

Αφηρημένες κλάσεις ή διεπαφές;

Η αρχή της στιβαρότητας

H χρήση ορισμάτων αφηρημένων κλάσεων αλλά η επιστροφή διεπαφών αιτιολογείται από την περίφημη αρχή της στιβαρότητας του Jon Postel (http://en.wikipedia.org/wiki/Robustness_principle):
Να είσαι συντηρητικός σ' αυτά που κάνεις εσύ και ανεκτικός σ' αυτά που δέχεσαι από τους άλλους (Be conservative in what you do, be liberal in what you accept from others).
Η παραπάνω αρχή θεωρείται από πολλούς ο θεμέλιος λίθος λειτουργίας του διαδικτύου.

Αιτιολόγηση χρήσης διεπαφών

Με βάση την αρχή της στιβαρότητας σχεδιάζουμε ως εξής.

Παράδειγμα στιβαρού σχεδιασμού

interface  InternalCombustionEngine  { /* ... */ }

abstract class FourStrokeEngine implements InternalCombustionEngine  { /* ... */ }

class GeneralMotorsLS3 extends FourStrokeEngine { /* ... */ }

class ChevroletCorvette implements InternalCombustionEngine {
    FourStrokeEngine engine;

    /*
     * Receive abstract class so that changes in it can transparently
     * update all classes that extend the abstract class.
     */
    void setEngine(FourStrokeEngine e) { engine = e; }

    // Return interface rather than concrete class to allow widest possible use
    InternalCombustionEngine getEngine() { return engine; }

    public static int main(String[] args) {
        var corvette = new ChevroletCorvette();
        corvette.setEngine(new GeneralMotorsLS3());
        return 0;
    }
}

Πακέτα

Ορισμός κλάσεων σε πακέτα

Χρήση κλάσεων σε πακέτα

Κανόνες χρήσης πακέτων

Απλοποίηση χρήσης στατικών πεδίων

Με την εντολή import static μπορούμε να εισάγουμε στατικές μεθόδους και πεδία ώστε να χρησιμοποιηθούν χωρίς το πρόθεμα της κλάσης.

import static java.lang.Math.*;

class Sqrt {
    public static void main(String args[]) {
        System.out.println(sqrt(2));
    }
}

Άσκηση: παραγωγή και έλεγχος εξαιρέσεων

Άσκηση 6 και 16

Μπορείτε να κατεβάσετε το αντίστοιχο αρχείο και να στείλετε τους βαθμούς σας από τους δεσμούς που βρίσκονται στη σελίδα των ασκήσεων.

Βιβλιογραφία

Περιεχόμενα