Refactoring ("Επαναπαραγοντοποίηση", "Επανακατασκευή") Κώδικα

Posted by Χρίστος Ευαγγέλου Mon, 20 Sep 2004 21:15:48 EEST

Η παρούσα δημοσίευση έχει "υποστεί refactoring."
(βλ. http://www.phigita.net/~christose/blog/55)


Θεώρησα καλό να δημοσιεύσω λίγα πράγματα για το refactoring,
επειδή, κατά τον Ralph Johnson, είναι η "μόνη άμυνα εναντίον της
φθοράς λογισμικού"
(only defense against software decay). Νομίζω
όλοι όσοι συντηρούν λογισμικα συστήματα μπορούν να επωφεληθούν
γνωρίζοντας για το refactoring.

Σημειώστε ότι αποφεύγω να χρησιμοποιήσω Ελληνική ορολογία
επειδή δεν είναι ξεκάθαρο ποιά μετάφραση είναι η πιο σωστή
(προσωπικά, ψηφίζω για "επανακατασκευή κώδικα" - βλ.
http://www.phigita.net/~k2pts/blog/643).

_Τι Είναι Refactoring_

  "Refactoring is the process of changing a software
  system in such a way that it does not alter the external
  behavior of the code yet improves its internal structure."
[1]

Άρα λοιπόν, στόχος του refactoring είναι η βελτίωση της
δομής και οργάνωσης κώδικα
, με απώτερο σκοπό να καταστεί
η συντήρησή του πιο εύκολη
. Ο στόχος του refactoring δεν
είναι η προσθήκη νέων λειτουργιών σε ένα σύστημα
- παρόλο
που ένας από τους στόχους τους οποίους εξυπηρετεί είναι να
κάνει τέτοιες προσθήκες ευκολότερες.

_Πώς Διεξάγεται_

- Προσχεδιάζουμε την αλλαγή που θέλουμε να κάνουμε, ώστε αυτή
να αποτελείται από βήματα ΜΙΚΡΩΝ τροποποιήσεων.

- Διεξαγωγή κάθε τροποίησης.

- Δοκιμή συστήματος αμέσως μετά από κάθε τροποίηση.

Είναι σημαντικό οι τροποιήσεις να είναι μικρές, επειδή όσο
μεγαλύτερες είναι, τόσο μεγαλύτερη είναι και η πιθανότητα να
προκαλέσουν προβλήματα (regression faults). Επίσης, είναι
και πιο δύσκολη η αντιμετώπιση των προβλημάτων αυτών.
Είναι πάρα πολύ σημαντικό να ελέγχεται το σύστημα μετά
από κάθε μικρή τροποίηση, ούτως ώστε να βεβαιωνόμαστε ότι
δεν έχει προκαλέσει νέα προβλήματα και ότι δεν έχει
επηρεάσει την λειτουργία μέρων του συστήματος τα οποία πριν
λειτουργούσαν σωστά.

_Παράδειγμα Refactoring_

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

void main(int argc, char **argv) {       ....        printf("Δώστε αριθμό: ");       do {       sscanf("%d",&n);       if (n <= 0)         printf("Ο αριθμός πρέπει να είναι >0..\n");       } while (n <= 0);   ... } void someFunction(void) {   ...   printf("Δώστε αριθμό: ");   do {       sscanf("%d",&n);       if (n <= 0)         printf("Ο αριθμός πρέπει να είναι >0.\n");   } while (n <= 0);   ... }

Κάτι δεν πάει καλά με αυτό τον κώδικα - κάτι "μυρίζει"
άσχημα. Πράγματι,  βλέπουμε επανάληψη του ιδίου τμήματος
κώδικα:

printf("Δώστε αριθμό: "); do {     sscanf("%d",&n);     if (n <= 0)       printf("Ο αριθμός πρέπει να είναι >0.\n"); } while (n <= 0);

Σαφώς, αυτό το τμήμα κώδικα θα μπορούσε να τοποθετηθεί
σε μια συνάρτηση και να παραμετρικοποιηθεί:

void main(int argc, char **argv) {       ....        n = getNumber("Δώστε αριθμό: ");       ... } void someFunction(void) {   ...   n = getNumber("Δώστε αριθμό: ");   ... } int getNumber(char *message) {   int num;   printf("%s",message);   do {       sscanf("%d",&num);       if (num <= 0)         printf("Ο αριθμός πρέπει να είναι >0.\n");   } while (num <= 0);   return (num); }

Γιατί είναι η παραπάνω τροποποίηση σημαντική; Διοτί
προωθεί την επαναχρησιμοποίηση κώδικα, διευκολύνει
τον εντοπισμό σφαλμάτων (μιας και ο κώδικας έχει
συγκεντρωθεί σε μια συνάρτηση και δεν βρίσκεται διάσπαρτος
σε όλο το σύστημα), και κάνει τον κώδικα πιο ευανάγνωστο
και κατανοητό
.

_Μυρωδιές Κώδικα (Code Smells)_

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

  1. Επαναλαμβανόμενος Κώδικας   2. Μεγάλες Συναρτήσεις / Μεθόδοι   3. Μεγάλες Κλάσεις   4. Μεγάλες Λίστες Παραμέτρων   5. Αλυσιδωτή κλήση μεθόδων     (π.χ. ComplexShape.boundingRectangle.center()       - γιατί όχι απλώς ComplexShape.center();)   6. κλπ

Είμαι βέβαιος ότι μπορείτε να βρείτε αρκετές "μυρωδιές
κώδικα" στο Γούγλη:(http://www.google.com), καθώς επίσης
και τρόπους εξάλειψής τους.

Στο [2] μπορείτε να βρείτε κατάλογο με τροποποιήσεις κώδικα
για refactoring σε C++.

_Χρήσιμες Παραπομπές_

  [1] Refactoring: Improving the Design of Existing Code
  (Martin Fowler, Kent Beck, John Brant, William Opdyke, Don Roberts),
  http://www.amazon.com/exec/obidos/ISBN=0201485672/portlandpatternrA/
  Επίσημος διαδικτυακός χώρος: http://www.refactoring.com

  [2] Στο http://www.refactoring.com/catalog/ υπάρχει κατάλογος με
  τροποποιήσεις κώδικα για refactoring.

3 comments

    Reader's Comments

  1.   Είναι σημαντικό οι τροποιήσεις να είναι μικρές, επειδή όσο μεγαλύτερες είναι, τόσο μεγαλύτερη είναι και η πιθανότητα να προκαλέσουν προβλήματα (regression faults). Επίσης, είναι και πιο δύσκολη η αντιμετώπιση των προβλημάτων αυτών. Είναι πάρα πολύ σημαντικό να ελέγχεται το σύστημα μετά από κάθε μικρή τροποίηση, ούτως ώστε να βεβαιωνόμαστε ότι δεν έχει προκαλέσει νέα προβλήματα και ότι δεν έχει επηρεάσει την λειτουργία μέρων του συστήματος τα οποία πριν λειτουργούσαν σωστά.

    Παλιότερα, είχα "γράψει" για μικρά αναστρέψιμα βήματα στην ανάπτυξη λογισμικού. Πιο συγκεκριμένα και σύμφωνα με το Stefano Mazzochi:

      […] making architecture decisions without continuous reversibility is expensive because design constraints change too much. Those who want to apply hardware engineering practices miserably fail. Open source is here to prove that such a "messy" way to do code is actually the only one that works and scales.

    -- Νεόφυτος Δημητρίου ~k2pts, September 20, 2004

  2. Αν κατάλαβα καλά, όταν ο Mazzochi αναφέρεται σε "continuous reversibility" εννοεί την ευελιξία του κώδικα να προσαρμόζεται ανάλογα με τις ανάγκες του αδιάληπτα μεταβαλλόμενου περιβάλλοντος. Σε αυτό το σημείο νομίζω όλοι συμφωνούν ότι η ευελιξία τροποποίησης (modifiability) είναι ένα σημαντικό χαρακτηριστικό ποιότητας λογισμικού.

    Όσο για το άλλο σημείο το οποίο θίγει, απ'ότι κατάλαβα, και παρακαλώ διορθώστε με αν κάνω λάθος, πιστεύει ότι ο κώδικας δεν πρέπει να είναι καλογραμμένος (The hardest and best the code is, the more harm it creates to the community), διότι αυτό αποθαρρύνει την κοινότητα από το να επιδιώξει να τον καλυτερεύσει. Εγώ νομίζω είναι ακριβώς το αντίθετο: όταν ο κώδικας είναι καλά οργανωμένος και ευκολοκατανόητος, νέα μέλη της κοινότητας μπορούν ευκολότερα να αρχίσουν να δουλεύουν επάνω του, να δουν τους περιορισμούς που έχει και τα σημεία στα οποία χρήζει βελτίωσης. Αντίθετα, όταν ο κώδικας δεν είναι καλά οργανωμένος και τεκμηριωμένος, πιστεύω περιορίζει την δυνατότητα και την ικανότητα νέων μελών να συνεισφέρουν. Εξάλλου, ένα από τα χαρακτηριστικά του καλού κώδικα είναι η ευκολία τροποποίησής του.

    Όσο για το επιχείρημα:

    Open source is here to prove that such a "messy" way to do code is actually the only one that works and scales

    δεν νομίζω ότι είναι ρεαλιστικό. Πολύ απλά, δεν μπορούν να αναπτύσσονται όλα τα λογισμικά συστήματα μέσα σε Open-Source περιβάλλοντα. Στον πραγματικό κόσμο, οι εταιρείες δουλεύουν με αυστηρά χρονοδιαγράμματα, περιορισμένους πόρους και συχνά μέσα σε αυστηρά καθορισμένα πλαίσια εργασίας. Άσε που τίθονται και θέματα ανταγωνισμού, τα οποία περαιτέρω αποθαρρύνουν την δημόσια διάθεση του κώδικα.

    Βεβαίως συμφωνώ ότι η δυναμική της Open-Source κοινότητας είναι μεγαλύτερη από κλειστές κοινότητες: δεν υπάρχει κίνδυνος να "στερέψει" η πηγή ιδεών, και πάντοτε θα υπάρχουν πρόθυμα πνεύματα που θα πάρουν τον κώδικα και θα τον προσαρμόσουν στις εκάστοτε ανάγκες του περιβάλλοντος και των καταστάσεων.

    -- Χρίστος Ευαγγέλου ~christose, September 20, 2004

  3. Νομίζω ότι εννοεί μικρά, αναστρέψιμα, βήματα. Εκείνο το μήνυμα πάρθηκε από μια έντονη συζήτηση για τις μεγάλες αλλαγές που υποβάλλονται σε έργα ελεύθερα λογισμικού και με τις οποίες κανένας δεν συμφωνεί. Ο Steffano υποστήριζε ότι ήταν καλύτερα να είχαν υποβληθεί ως μια σειρά από μικρές αλλαγές για τον απλούστατο λόγο ότι σε περίπτωση δυσλειτουργίας, θα ήταν πιο εύκολο να αναστρέψουν την αλλαγή που έγινε.

    Όσο αφορά το κομμάτι του "κακογραμμένου" κώδικα, εννοεί ότι ο "κακογραμμένος" κώδικας δημιουργεί κοινότητες λογισμικού ακριβώς επειδή οι συμμέτοχοι στο έργο θα νιώθουν την ανάγκη να ρωτήσουν ερωτήσεις για να το καταλάβουν. Δεν θέλω να ισχυριστώ ότι αυτός ο τρόπος ανάπτυξης λογισμικού είναι ο καλύτερος, αλλά είναι ο καλύτερος "ανορθόδοξος" τρόπος ανάπτυξης που γνωρίζω και δουλεύει —τουλάχιστο για έργα ανοικτού λογισμικού.

    -- Νεόφυτος Δημητρίου ~k2pts, September 20, 2004