Λεπτομερής σχεδίαση και κωδικοποίηση

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

Δομημένος προγραμματισμός

Ο νόμος του Demeter

Παράδειγμα:
class Demeter {
        private A a;
        private int myFunc() { /* ... */ }
        public void example (B b) {
                C c = new C();
                int f = myFunc();

                // ...
                b.paramMethod();
                a = new A();
                a.createdMethod();
                c.ownMethod();
        }
}
Σημείωση: Ο νόμος του Demeter (προφ. νταϊμέτρ) (Lieberherr και Holland 1989) έλαβε το όνομά του από το ομώνυμο ερευνητικό έργο, το οποίο με τη σειρά του ονομάστηκε προς τιμή της αρχαίας θεάς Δήμητρας.

Ψευδοκώδικας

Ο ψευδοκώδικας (pseudocode) ή γλώσσα σχεδιασμού προγραμμάτων (program design language (PDL)) ή δομημένα αγγλικά (structured english) συνδυάζει τη δομή μιας γλώσσας προγραμματισμού με την αφηγηματική χρήση της φυσικής γλώσσας.

Παράδειγμα:

PROCEDURE Withdrawal
        DO
                Total requested = Ask ammount()
        WHILE NOT Integral value(Total requested)
        IF Total requested > Balance THEN
                Send complaint letter
                Zero credit limit
                Notify manager
        ELSE
                Remaining = Total requested
                WHILE Remaining > 0
                        Dispose Note
                        Remaining = Remaining - Note value
                END WHILE
        END IF
END PROCEDURE

Διάγραμμα ροής

Το διάγραμμα ροής (flow chart) μας επιτρέπει να παραστήσουμε με λεπτομέρεια τη ροή του προγράμματος. Όταν προγραμματίζουμε δομημένα πρέπει τα τμήματα του προγράμματος να απαρτίζονται απο τα παρακάτω στοιχεία, χωρίς ενδιάμεσες ενώσεις.

Ακολουθία

Απόφαση

Επιλογή

Επανάληψη

Διάγραμμα Nassi-Shneiderman

Τα διαγράμματα Nassi-Shneiderman, (ή N-S, ή Chapin) αποτρέπουν την έκφραση μη δομημένων δομών. Υποστηρίζουν τις παρακάτω δομές.

Ακολουθία

Απόφαση

Επιλογή

Επανάληψη

Πίνακες απόφασης

  • Όταν διαφορετικές ενέργειες του προγράμματος εξαρτώνται μια περίπλοκη σειρά από προϋποθέσεις μπορούμε να εκφράσουμε τη λογική με έναν πίνακα απόφασης (decision table).
  • Σε αυτόν περιλαμβάνουμε όλες τις προϋποθέσεις και σημειώνουμε ποιες ενέργειες θα εκτελεστούν αν ισχύουν κάποιες προϋποθέσεις.
  • Κάθε στήλη του πίνακα είναι ένας διαφορετικός κανόνας.
    Προϋποθέσεις 12345
    Προϋπόθεση 1XXX--
    Προϋπόθεση 2---X-
    Προϋπόθεση 3X----
    Προϋπόθεση 4XX---
    -----
    -----
    Ενέργειες
    Ενέργεια 1 X----
    Ενέργεια 2 -X---
    Ενέργεια 3 --X--
    Ενέργεια 4 ---X-
    Ενέργεια 5 ----X
    Ενέργεια 6 XX--X

    Πρότυπα κωδικοποίησης

    Ο τρόπος της κωδικοποίησης καθορίζεται συχνά από έναν οδηγό ύφους (style guide). Αυτός ορίζει στοιχεία πέρα από αυτά που προσδιορίζει η σύνταξη μιας συγκεκριμένης γλώσσας. Για παράδειγμα το παρακάτω είναι ένα νόμιμο αλλά όχι ευανάγνωστο πρόγραμμα γραμμένο σε C:
    #define O(b,f,u,s,c,a)b(){int o=f();switch(*p++){X u:_ o s b();X c:_ o a b();default:p--;_ o;}}
    #define t(e,d,_,C)X e:f=fopen(B+d,_);C;fclose(f)
    #define U(y,z)while(p=Q(s,y))*p++=z,*p=' '
    #define N for(i=0;i<11*R;i++)m[i]&&
    #define I "%d %s\n",i,m[i]
    #define X ;break;case
    #define _ return
    #define R 999
    typedef char*A;int*C,E[R],L[R],M[R],P[R],l,i,j;char B[R],F[2];A m[12*R],malloc
    (),p,q,x,y,z,s,d,f,fopen();A Q(s,o)A s,o;{for(x=s;*x;x++){for(y=x,z=o;*z&&*y==
    *z;y++)z++;if(z>o&&!*z)_ x;}_	0;}main(){m[11*R]="E";while(puts("Ok"),gets(B)
    )switch(*B){X'R':C=E;l=1;for(i=0;i<R;P[i++]=0);while(l){while(!(s=m[l]))l++;if
    (!Q(s,"\"")){U("<>",'#');U("<=",'$');U(">=",'!');}d=B;while(*F=*s){*s=='"'&&j
    ++;if(j&1||!Q(" \t",F))*d++=*s;s++;}*d--=j=0;if(B[1]!='=')switch(*B){X'E':l=-1
    X'R':B[2]!='M'&&(l=*--C)X'I':B[1]=='N'?gets(p=B),P[*d]=S():(*(q=Q(B,"TH"))=0,p
    =B+2,S()&&(p=q+4,l=S()-1))X'P':B[5]=='"'?*d=0,puts(B+6):(p=B+5,printf("%d\n",S
    ()))X'G':p=B+4,B[2]=='S'&&(*C++=l,p++),l=S()-1 X'F':*(q=Q(B,"TO"))=0;p=B+5;P[i
    =B[3]]=S();p=q+2;M[i]=S();L[i]=l X'N':++P[*d]<=M[*d]&&(l=L[*d]);}else p=B+2,P[
    *B]=S();l++;}X'L':N printf(I)X'N':N free(m[i]),m[i]=0	X'B':_ 0 t('S',5,"w",N
    fprintf(f,I))t('O',4,"r",while(fgets(B,R,f))(*Q(B,"\n")=0,G()))X 0:default:G()
    ;}_ 0;}G(){l=atoi(B);m[l]&&free(m[l]);(p=Q(B," "))?strcpy(m[l]=malloc(strlen(p
    )),p+1):(m[l]=0,0);}O(S,J,'=',==,'#',!=)O(J,K,'<',<,'>',>)O(K,V,'$',<=,'!',>=)
    O(V,W,'+',+,'-',-)O(W,Y,'*',*,'/',/)Y(){int o;_*p=='-'?p++,-Y():*p>='0'&&*p<=
    '9'?strtol(p,&p,0):*p=='('?p++,o=S(),p++,o:P[*p++];}
    
    Οι οδηγοί ύφους έχουν ως στόχο να προλαμβάνουν φαινόμενα σαν το παραπάνω. Για παράδειγμα ο οδηγός ύφους της γλώσσας C "Recommended C Style and Coding Standards" γνωστός ως "Indian Hills Style Guide" περιέχει τα παρακάτω στοιχεία:

    Δομές δεδομένων

    Οι βασικές δομές δεδομένων που θα συναντήσετε είναι: Σύγχρονες γλώσσες όπως η C++ και η Java παρέχουν έτοιμη υποστήριξη για πολλούς από τους παραπάνω τύπους.

    Χρήση βεβαιώσεων

    Προγραμματίζουμε με σιγουριά αν εκφράζουμε τους αλγορίθμους μας με βάση:

    Παράδειγμα

    Είσοδος:
      Α(1..Ν): πίνακας ακεραίων
      N : ακέραιος
    Έξοδος:
      Β(1..Ν): πίνακας ακεραίων
    
    Προϋποθέσεις:
      Ν >= 1
    Μετασυνθήκες:
      Β(1..Ν) περιέχει τις τιμές του Α(1..Ν)
    
    Μεταβλητές
      Ι : Ακέραιος
    
    I := 1
    Όσο Ι <= Ν
      Β(Ι) := Α(Ι)
      Αναλλοίωτη συνθήκη: Β(1..Ι) := Α(1..Ι)
      Συνθήκη σύγκλισης: Ν - Ι
      Ι := Ι + 1
    Τέλος
    
    Σε στρατηγικά σημεία του κώδικα μπορούμε να εκφράζουμε τις παραπάνω έννοιες με τη βοήθεια μιας βεβαίωσης (assertion). Παράδειγμα:
    void
    arraycopy(int a[], int b[], int n)
    {
            assert(n >= 0);
            // ...
    }

    Απλοί κανόνες

    Μερικοί κανόνες (Kernighan και Plauger 1976, Davis 1995) που πρέπει να ακολουθούμε κατά την κωδικοποίηση είναι οι παρακάτω:

    Εσωτερική και εξωτερική τεκμηρίωση

    Παράδειγμα:
    /**
     * A class containing static methods that allow the simple, correct,
     * and efficient procesing of a console application's standard input
     * and output streams.
     * Output is buffered, but automagically flushed, so that prompts in
     * interactive applications work as expected.
     * All errors will terminate the application with an error message.
     * The code was written to provide an easy-to-learn and consistent interface 
     * for learning Java.  It is not intended for production work.
     *
     * @author  Diomidis Spinellis
     * @version $Revision: 1.4 $
     * @see java.io
     * @see java.io.Writer
     * @see java.io.PrintWriter
     */
    public class BIO {
            // Reader and Writer
            private static BufferedReader in = 
                    new BufferedReader(new InputStreamReader(System.in));
            private static PrintWriter out =
                    new PrintWriter(new BufferedWriter(new OutputStreamWriter(System.out)));
            private static boolean needFlush = false;

            // Class initializer
            static {
                    Runtime.getRuntime().addShutdownHook(new Thread() {
                            // Will run when VM shuts down
                            public void run()
                            {
                                    try {
                                            in.close();
                                            out.close();
                                    } catch (Exception e) {
                                    }
                            }
                });
            }
            /**
             * Flush the output stream. If the stream has saved any characters from 
             * the various write() methods in a buffer, write them immediately to 
             * their intended destination.
             * Flushing is automatically performed before reading input from a 
             * source * that could be affected by reading the program's output
             * (e.g. a human).
             */
            public static void flush() { out.flush(); needFlush = false; }
            /** Print the argument of type char on the standard output. */
            public static void print(char x) { out.print(x); needFlush = true; }
            /** Print the argument of type int on the standard output. */
            public static void print(int x) { out.print(x); needFlush = true; }
            /** Print the argument of type boolean on the standard output. */
            public static void print(boolean x) { out.print(x); needFlush = true; }
            /** Print the argument of type double on the standard output. */
            public static void print(double x) { out.print(x); needFlush = true; }
            /** Print the argument of type Object on the standard output. */
            public static void print(Object x) { out.print(x); needFlush = true; }

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

    Ασκήσεις

    1. Ξαναγράψτε μερικά από τα παλιά σας προγράμματα σύμφωνα με τις αρχές του μαθήματος αυτού.
    2. Αξιολογείστε προγράμματα ανοιχτού κώδικα που θα βρείτε στο Internet ως προς τις αρχές κωδικοποίησης που ακολουθούν.
    3. Διαβάστε προσεκτικά έναν πλήρη οδηγό ύφους προγραμματισμού (βλ. βιβλιογραφία).