Προς το περιεχόμενο

Προτεινόμενες αναρτήσεις

Δημοσ.

Το δόγμα "μην χρησιμοποιείτε ποτέ goto" που υποστήριζε ένθερμα και ο Dijkstra είναι λανθασμένο.

Το goto καταδικάστηκε για το μπλέξιμο και στη δυσαναγνωσία που μπορεί να προξενήσει στον κώδικα.

Αλλά αυτό μπορεί να γίνει και με τις κλάσεις - και μάλιστα χειρότερα.

Το goto είναι ακόμη και σήμερα χρήσιμο, κυρίως για έξοδο από εμφωλιασμένα for, while ή δομές if-else

όπως έγραψε και ο M2000.

To χρησιμοποιώ συχνά έτσι και ο κώδικας είναι πολύ πιο σαφής.

 

Γενικά είναι σημαντικότερο ο κώδικας να τρέχει πιο αργά αλλά να έχει εύχρηστη δομή παρά να είναι

βελτιστοποιημένος με σκοτεινά τεχνάσματα.

Όταν θέλεις να γίνει ταχύτερο το πρόγραμμα, το πρώτο που ψάχνεις πάντα είναι ένας πιο γρήγορος αλγόριθμος.

Οι μικρές βελτιστοποιήσεις ("μικρορυθμίσεις") δίνουν ελάχιστη διαφορά και για να γίνουν σωστά μπορεί να

απαιτούν γνώση του hardaware και εργαλεία profiling.

Εκτός από αδρά πράγματα (π.χ. στην προσπέλαση δεδομένων να ακολουθείται το memory pattern ώστε να

διατηρείται η τοπικότητα στις cache μνήμες), δεν γίνεται με το μάτι.

 

-

  • Like 2
  • Απαντ. 505
  • Δημ.
  • Τελ. απάντηση

Συχνή συμμετοχή στο θέμα

Συχνή συμμετοχή στο θέμα

Δημοσιευμένες Εικόνες

Δημοσ.

Πότε όμως θα πρέπει να αρχίσει να με προβληματίζει ?

Όταν θα αρχίσεις να φτιάχνεις και να χρησιμοποιείς σωστά ιεραρχίες κλάσεων και αρχίσεις να σκέφτεσαι με τη λέξη design. Ή ας πούμε όταν θα ξέρεις να εξηγήσεις σε κάποιον άλλο τι είναι το LSP και να δώσεις παραδείγματα του πότε καταπατάται.

 

Το συνάντησα, αλλά μου φάνηκε εύκολα αντιληπτή η χρήση του και για αυτό δεν το ανέφερα εδώ.

Ορίζουμε μια κλάση ως sealed, όταν θέλουμε να αποτρέψουμε τη χρήση της ως base class από derived classes. Δεν μπορεί μια κλάση να κληρονομήσει από τη κλάση sealed.

Αυτό όμως είναι πιο πολύ αποστήθιση παρά κατανόηση. Πότε τη χρησιμοποιούμε; "όταν θέλουμε να αποτρέψουμε τη χρήση της ως base class". ΟΚ, και πότε θέλουμε να αποτρέψουμε τη χρήση της ως base class? :-)

 

Γενικά το πρώτο σημαντικό εμπόδιο που πρέπει να ξεπεράσει οποιοσδήποτε μαθαίνει προγραμματισμό είναι να κάνει το άλμα στη σκέψη του από χειροπιαστή λογική (π.χ. "όταν θέλουμε να αποτρέψουμε τη χρήση") σε αφηρημένη ("όταν η class δεν έχει σχεδιαστεί με σκεπτικό να κάνεις inherit από αυτή").

 

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

 

Χμμμ... Θα είναι καλύτερο να χρησιμοποιούσα τη while επειδή ο βρόχος είναι πιο γρήγορος, γιατί η αναδρομή πρέπει να κάνει εκ νέου κλήση, όπως είχαμε πει νωρίτερα ε ?

 

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

 

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

 

Είναι προτιμότερο να σπαταλήσω τον παραπάνω χρόνο για να κάνω το κώδικα όσο πιο optimized γίνεται, ή είναι καλύτερα να γράψω κώδικα ο οποίος δουλεύει μεν, και είναι εύκολα αναγνώσιμος, αλλά θα μπορούσε να γραφτεί με τρόπο που η εκτέλεση του να είναι αρκετά πιο γρήγορη, για να γλιτώσω χρόνο ?

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

 

Πρακτικά μου είναι από πολύ δύσκολο ως αδύνατο να σκεφτώ περίπτωση όπου κάποιος που δεν είναι ήδη έμπειρος επαγγελματίας θα είχε ουσιαστικό λόγο να κάνει optimization, ενώ από την άλλη όπως είπα ο ευανάγνωστος κώδικας είναι στόχος και παρέχει ωφέλη για όλους, από τον πιο newbie μέχρι τον John Carmack.

 

Εδώ τώρα πάντα βρίσκεται κάποιος να πει ότι άμα δε μάθεις να γράφεις τους αλγορίθμους σωστά (optimized) τότε δε μαθαίνεις, γίνεσαι άσχετος "προγραμματιστής" και τέτοια. Αυτοί που τα λένε αυτά έχουν σίγουρα δίκιο σε ένα πράγμα: είναι ένα αξιόλογο skill να ξέρεις και να μπορείς να γράφεις καλό, γρήγορο κώδικα από την αρχή και αν χρειαστεί μετά να τον κάνεις optimize ακόμα περισσότερο. Αλλά αυτό το skill είναι που ξεχωρίζει τον έμπειρο επαγγελματία από τον έμπειρο guru. Είναι άπειρα σημαντικότερο να γράφει κανείς κατανοητό κώδικα. Το μόνο που είναι πιο σημαντικό από αυτό είναι ο κώδικας να δουλεύει σωστά.

 

Ψάξου αν θέλεις με google στην έκφραση "make it work, make it right, make it fast" -- δεν περιμένω να τη χωνέψεις, αλλά τελείως επιγραμματικά το νόημά της είναι:

Το πρώτο σου μέλημα πρέπει να είναι ο κώδικας να δουλέψει. Στη συνέχεια τον κώδικα που δουλεύει να τον γράψεις όμορφα (χωρίς να χαλάσει φυσικά) και σαν τελευταίο βήμα να τον κάνεις optimize.

 

Δύο λοιπόν σχόλια επί τούτου: α) αν μάθεις να σκέφτεσαι με τη λάθος σειρά προτεραιοτήτων τότε απλά κάποια στιγμή θα πρέπει να ξεμάθεις αυτά που ξέρεις και να τα ξαναμάθεις σωστά, β) στην πράξη το make it fast είτε το κάνεις είτε δεν το κάνεις κανείς δε θα καταλάβει ποτέ τίποτα. Ενώ το make it right θα το καταλάβουν αμέσως όσοι δουλεύουν μαζί σου.

 

Χμ θα το προσπαθήσω.  :P

Αν και ήδη έχω ψιλο-αντιληφθεί κάτι, όπως στο παράδειγμα που έδωσα. Γιατί άμα βάλεις πολλά goto, θα πρέπει να ψάχνεις από δω και από κει κάθε φορά που θες να διαβάσεις ένα κομμάτι, για να βρεις που είναι το σημείο στο οποίο θα σε πάει το goto. Και έτσι ξοδεύεις χρόνο ψάχνοντας από δω και από κει, χρόνο που θα μπορούσες να τον χρησιμοποιήσεις κάνοντας κάτι άλλο. Το οποίο goto είναι απομεινάρι από μια εποχή όπου τα προγράμματα δεν είχαν καν κλάσεις και ήταν ολόκληρα σε ένα κατεβατό / αρχείο, ενώ τώρα έχουμε ποια μεθόδους και κλάσεις, και μπορούμε να πηγαινοφέρνουμε το βήμα της εκτέλεσης πέρα δόθε χωρίς τέτοιους προϊστορικούς τρόπους!

 

Περίπου. Αυτό που έχουμε τώρα και δεν είχαμε παλιά λέγεται "δομημένος προγραμματισμός". Διάβασέ το, είναι ενδιαφέρον και έχει ψωμί να ψαχτείς περισσότερο.

 

Προσωπικά έμαθα προγραμματισμό σε άλλη εποχή με άλλα εργαλεία και π.χ. το while true το είδα μπροστά μου πρώτη φορά περίπου 5 χρόνια αφότου ξεκίνησα. Η όλη λογική του δομημένου προγραμματισμού μου φαινόταν ανάποδη, έπρεπε αντί να κάνω τα goto που έκανα πριν όπως μου άρεσε να κάθομαι να σκέφτομαι πώς θα μπορούσε να γραφτεί το ίδιο πράγμα αλλιώς. Και δε μου άρεζε. Αλλά τελικά αποδείχτηκε πως το goto ήταν "λάθος" και το while true "σωστό" για διάφορους λόγους, και ωφελήθηκα πάρα πάρα πολύ αναπτύσσοντας την ικανότητα να σκέφτομαι χωρίς goto.

 

Υπάρχουν κάποιες ελάχιστες, σπάνιες, περιπτώσεις όπου αν θέλεις να κάνεις κάτι χωρίς goto κολυμπάς λίγο κόντρα στο ρεύμα, αλλά αυτές είναι οι εξαιρέσεις που επιβεβαιώνουν τον κανόνα. Αυτά που λέει ο m2000 και ο smirnov προσωπικά δε με πείθουν καθόλου, και ούτε το θεωρώ τυχαίο πως προς υποστήριξη του goto μιλάνε τα συγκεκριμένα δύο πρόσωπα (ευχαρίστως αν θέλουν να δώσουν συγκεκριμένα παραδείγματα να τα συζητήσουμε). Στην τελική βέβαια όπως και όλα τα πράγματα έτσι και το goto έχει τη χρήση του. Το πρόβλημα είναι πως πολλές φορές νομίζει κανείς ότι "εδώ χρειάζεται" επειδή τυχαίνει να μην ξέρει ή να βαριέται ή οτιδήποτε άλλο να το κάνει χωρίς goto.

 

Τελείως speculative παράδειγμα (και ας μη το πάρει παρακαλώ κανείς σαν "απόδειξη" οποιουδήποτε πράγματος), ο smirnov έγραψε "το goto είναι ακόμη και σήμερα χρήσιμο, κυρίως για έξοδο από εμφωλιασμένα for, while ή δομές if-else". Και δεν αμφιβάλλω ότι σε πολλές γλώσσες, αν βρεθείς να έχεις τα προαναφερθέντα εμφωλιασμένα, το goto είναι βολική και γιατί όχι elegant λύση. Αλλά το point είναι πως να κάνεις ένα βήμα πίσω, θα ήταν πρακτικά πάντα καλύτερο design να μην τα έχεις όλα αυτά in the first place. Οπότε εδώ για μένα το goto είναι μια καλή λύση σε ένα πρόβλημα που θα έπρεπε να μην έχει αφήσει κανείς να εμφανιστεί από την αρχή. Ή με άλλα λόγια, αντί για "καλή λύση" είναι μάλλον "ένδειξη προβλήματος". Ελπίζω έτσι να είναι πιο κατανοητό το τι έχω στο μυαλό μου όταν λέω "μπα δε χρειάζεται".

  • Like 1
Δημοσ. (επεξεργασμένο)

Το παρακάτω απόσπασμα είναι από πραγματικό κώδικα.

Είναι ένας βρόχος  που έχει πολύπλοκες συνθήκες εξόδου και συνέχειας.

 

Όποιος μπορεί ας αναδιατάξει το απόσπασμα ώστε να έχει πιο σαφή ροή εκτέλεσης χωρίς goto.

Οι θέσεις συνέχειας και εξόδου είναι οι exitPaving και continuePaving.

Άντε να δούμε τους δογματικούς στρουκτουραλιστές στην πράξη.....

void pav_main(UMG *umgi)
{
   PAVEDMESH *pvmesh;  
   LOOP      *loop, *currLoop, *startLoop;  
   int        status, isEnd, contSweeping;  
   BGMDATA   *bgd;

  // check that the angle ranges are valid  
  checkRangeOfAngles();

  // allocate and initialise an empty paving mesh
  pvmesh = initPavedMesh();

  // setup the bounding box of this mesh  
  setupAABBofPvMesh(umgi->mesh, pvmesh);
  
  // load and setup the data for the lms control type  
  pvmesh->ctrl = umgi->ctrl;  
  if(umgi->ctrl == LMSC_BKGRND || umgi->ctrl == LMSC_BKGRND_SPOTS)    
     getBkgndMeshData(&(pvmesh->bgd), umgi->bgmesh, umgi->spots);  
  else    
    bgd = NULL;
  
  if(pvmesh->ctrl == LMSC_CONSTANT)    
    pvmesh->uniform_h = umgi->h;

  /* from the coarse mesh,
   - extract the boundary and holes,
   - refine them according to the specified local mesh size values,
   - insert the generated points into the paving mesh */
 
  initFronts(umgi->mesh, umgi->curveset, pvmesh);

  /* main paving procedure of the mesh     
     ---------------------------------
     sweep all the loops of mesh and pave them row by row */

continuePaving:

  clearSmoothBitOfFronts(pvmesh);

  /* take the actual front loop and mark that must be smoothed
    (initially, its smooth flag is off; set it on) */
  loop = pvmesh->CurrLoop;  
  SET_BIT(loop->TypeFlag, TO_BE_SMOOTHED);
  
  /* if the current loop has more than 6 nodes,
     start and continue the paving on it  */
     if(loop->NNodes > 6)
     {
        /* start the row generation and generate the first quad on the loop;
           after calling this function, the current node has moved to the next */
        contSweeping = initRowSweep(pvmesh, loop);

       /* if everything is Ok,
          continue processing the current row */
       while(contSweeping == P_SWEEP_CONTINUE)
       {
          // check if this is the end of the side (this happens when an end-type node is encountered)
          isEnd = (CHK_BIT(loop->CurrNode->TypeFlag, NODE_SIDE)) ? FALSE : TRUE;

         // generate nodes and quads
         status = FaceFactory(pvmesh, loop);

         // if everything is Ok...
         if(status == P_QUAD_ACCEPT && !isEnd)      
         {        
           // ...go to the next node
           moveOnNextNode(loop, FALSE);

           // delete undo data
           EmptyUndoQueue();

           // continue processing the row 
     }
      
     /* stop processing the row */      
    else
        contSweeping = P_SWEEP_STOP;
    }
    /* sweeping of current loop stopped or didn't start;
       either the row was fully paved (end node encountered)
       οr a problem occured (to be resoved later) */

    /* if the current loop has less than 6 nodes (i.e. fully paved), close it */
    if(loop->NNodes < 6)
    {
      /* close the loop appropriately;
         this function also removes the closed loop from the mesh and moves on the next loop;
         so paving continues on the next loop */
      closeLoop(pvmesh, loop);

      // if there are no more fronts, terminate paving
      if(pvmesh->CurrLoop == NULL)
        goto exitPaving;

      // continue paving on the next loop
      goto continuePaving;
    }

    /* the current loop has 6 nodes or more;
       sweep all the loops and adjust them : smoothing, seaming & wedge/tuck insertions;
       ( if the loop has 6 nodes,adjust too ) */
    currLoop = startLoop = pvmesh->CurrLoop;
    do    
    {        
       if(CHK_BIT(currLoop->TypeFlag, TO_BE_SMOOTHED))          
         adjustRow(pvmesh, currLoop);

       currLoop = currLoop->Next;
    }
    while(currLoop != startLoop);
    // continue paving on the next loop
  }

  /* the current loop has 6 nodes or less; close it */  
  else  
  {
    closeLoop(pvmesh, loop);

    // if there are no more fronts, terminate paving 
    if(pvmesh->CurrLoop == NULL)
      goto exitPaving;  
  }

  // move on to the next loop 
  moveOnNextLoop(pvmesh, FALSE);

  goto continuePaving;


  /* end of paving procedure     
     ----------------------- */
exitPaving:

  /* finally smooth the whole mesh */
  smoothInterior(pvmesh, NULL, umgi->smiter, FALSE);

  cleanUpPvMesh(pvmesh);

  /* convert it to MDF mesh */
  umgi->outputmesh = ConvertToMesh(pvmesh);

  if(bgd != NULL)    
  free_BkgndMeshData(bgd);

}

 -

Επεξ/σία από V.I.Smirnov
Δημοσ.

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

 

Τώρα όσον αφορά την ουσία, κάνε αν θες ένα κόπο να αφαιρέσεις τα πάντα όλα που είναι άσχετα με το control flow γιατί δεν έχω διάθεση να σπαταλάω φαιά ουσία διαβάζοντας "isEnd = (CHK_BIT(loop->CurrNode->TypeFlag, NODE_SIDE)) ? FALSE : TRUE;" όταν αυτό θα μπορούσε να μην υπάρχει καν χωρίς να επηρρεάζει καθόλου την κουβέντα μας. Ή τουλάχιστον πόσταρέ το κάπου που δε θα χαλάει το indentation για να φάινεται πώς πάει η δομή.

 

Η υπόσχεσή μου να δούμε πώς αλλιώς γίνεται το control flow ισχύει αλλά ένα μινιμουμ courtesy περιμένω και από απέναντι.

Δημοσ.

Το "δογματικός" δεν αφορά εσένα αλλά όσους επιμένουν τυφλά ότι το goto είναι άχρηστο.

Εξάλλου το ότι η pascal είχε goto ενώ είχε φτιαχτεί αποκλειστικά για να μην έχει, δείχνει κάτι.

 

Το identation είναι άριστο στο Visual studio, εδώ για κάποιον λόγο χαλάει.

 

Τέλος, η πρόκληση είναι κυρίως ρητορική.

Το προκείμενο απόσπασμα δεν μπορεί να δομηθεί καλύτερα,

είναι η κύρια ροή ενός πολύπλοκου αλγόριθμου για meshing, έκτασης πολλών σελίδων.

Αν αφαιρέσεις όλα τα ενδιάμεσα και αφήσεις μόνον ότι κατευθύνει τη ροή εκτέλεσης, θα το δεις μόνος σου.

Και ούτε η χρήση κλάσεων θα το έκανε σαφέστερο -

το αντίθετο, θα κάλυπτε πράγματα που για την κατανόηση της ροής εδώ είναι επιθμητό να φαίνονται...

 

-

Δημοσ.

Τέλος, η πρόκληση είναι κυρίως ρητορική.

Το προκείμενο απόσπασμα δεν μπορεί να δομηθεί καλύτερα

 

Αυτό που λένε με τη μεγάλη μπουκιά το ξέρεις έτσι;

 

I will not enjoy doing this γιατί εκτιμώ το επίπεδό σου γενικότερα, αλλά μου κάνει εντύπωση που λες κάτι τέτοιο ενώ κατά δική σου δήλωση δεν είσαι (δε σε ενδιαφέρει να είσαι) βαθειά χωμένος στον προγραμματισμό.

 

Anyway θα δούμε αν μπορεί να δομηθεί καλύτερα soon enough. Αλλά βάλτο στο pastebin, μη παρακαλάμε τώρα για το formatting.

Δημοσ.

Όπου υπάρχει πολύπλοκη λογική το Goto σώνει. Αλλά σε κάθε περίπτωση ο κώδικας δεν θα γίνεται κατανοητός..παρά μόνο αν κανείς το διαβάσει με προσοχή, και με τον τρόπο του προγραμματιστή που σημαίνει ότι στο μυαλό του έχει όλες (ή όσες καταλαβαίνει) τις δυνατές περιπτώσεις και "βλέπει" τον κώδικα ανά περίπτωση.  Το να νιώθεις το τι κάνει ο κώδικας χωρίς να τρέχει...έχει μεγάλη αξία. Επειδή μάλιστα πολλές φορές είναι δύσκολο, αναγκάζεται κανείς να "τεκμηριώσει" την άποψή του, δηλαδή να δει με κατάλληλες διακοπές τις αλλαγές των τιμών και την πορεία της εκτέλεσης. Αυτό δεν γίνεται όταν δεν υπάρχει πρόβλημα. Και το πρόβλημα μπορεί να είναι σε ένα νούμερο που ποτέ δεν περιμέναμε να ήταν έτσι!

Εδώ θέλω να θυμίσω σε παλιότερους κάποια προγράμματα που έτρεχαν σε windows 98 και όταν έτρεχαν σε XP είχαν χαθεί εικόνες! Το bug προέρχονταν από το hwnd, όπου έδιναν τα Windows, και ενώ στο 98 ήταν θετικό νούμερο...στα XP μπορεί να ήταν αρνητικό, και αν ο προγραμματιστής είχε βάλει το <=0 ...ως λάθος...(ότι δεν θα έπαιρνε "παράθυρο" για να "ζωγραφίσει") τότε το πρόγραμμα δεν θα έφτιαχνε τη εικόνα. Γιατί το νούμερο στα XP έβγαινε αρνητικό; Γιατί ήταν η μετατροπή του Unsign, και έδιναν τα XP νούμερα πάνω από τον μεγαλύτερο θετικό σε sign.

Δημοσ.

Το πρόγραμμα αυτό συνολικά δεν το έχω γράψει εγώ αλλά προγραμματιστές πολύ πιο ειδικοί από εμένα.

Σε μένα ζήτησαν διορθώσεις σε συγκεκριμένα τμήματα.

Όταν τους είπα ότι αυτό είναι μαύρο χάλι, μου έδειξαν τη θεωρία που υλοποιεί.

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

Έκαναν μια σύνοψη του το τι θεωρούν ως πιο σημαντικό να είναι εξωτερικά και τα υπόλοιπα τέθηκαν σε υπορουτίνες και structs.

To να χρησιμοποιήσουν κάποιο desisgn pattern δεν τους βόλευε επειδή θα αποκρύπτονταν πράγματα που ήθελαν να φαίνονται για να έχουν εποπτεία της ροής.

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

Το παρόν ήταν η καλύτερη συμβιβαστική λύση - στην οποία το goto έχει βασικό ρόλο....

 

-

  • Moderators
Δημοσ.

Όταν θα αρχίσεις να φτιάχνεις και να χρησιμοποιείς σωστά ιεραρχίες κλάσεων και αρχίσεις να σκέφτεσαι με τη λέξη design. Ή ας πούμε όταν θα ξέρεις να εξηγήσεις σε κάποιον άλλο τι είναι το LSP και να δώσεις παραδείγματα του πότε καταπατάται.

 

Επειδή δεν ήξερα καν τι ειν' αυτό, είπα να το βάλω μήπως το βρουν κι άλλοι ενδιαφέρον.

Από http://stackoverflow.com/questions/56860/what-is-the-liskov-substitution-principle

 

 

 

A great example illustrating LSP (given by Uncle Bob in a podcast I heard recently) was how sometimes something that sounds right in natural language doesn't quite work in code.

In mathematics, a Square is a Rectangle. Indeed it is a specialization of a rectangle. The "is a" makes you want to model this with inheritance. However if in code you made Square derive from Rectangle, then a Square should be usable anywhere you expect a Rectangle. This makes for some strange behavior.

Imagine you had SetWidth and SetHeight methods on your Rectangle base class; this seems perfectly logical. However if your Rectangle reference pointed to a Square, then SetWidth and SetHeight doesn't make sense because setting one would change the other to match it. In this case Square fails the Liskov Substitution Test with Rectangle and the abstraction of having Square inherit from Rectangle is a bad one.

Δημοσ.

Μπορείς όμως να κάνεις το τετράγωνο τη βάση του παραλληλόγραμμου...Πώς;

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

Δημοσ.

V.I., στον κώδικα που πόσταρες, αν

 

1) μεταξύ των labels continuePaving και exitPaving μπουν όλα σε ένα π.χ. while (1) { ... }

2) όπου έχεις goto continuePaving βάλεις continue (το 2ο και τελευταίο απλά θα γίνει το μουστάκι που κλείνει την while)

3) όπου goto exitPaving γίνει break

 

την ίδια συμπεριφορά δεν θα έχεις;

Δημοσ. (επεξεργασμένο)

Δεν κερδίζεις τίποτε αν αντί goto τεθεί while, continue και break.

Το θέμα δεν είναι να απαλλαγείς από το goto αλλά να γίνει η ροή σαφέστερη,

με όσο το δυνατόν λιγότερες διακλαδώσεις ή/και όσο το δυνατόν λιγότερα If-else.

 

Εν προκειμένω, με τα exitPaving και continuePaving φαίνεται σαφέστερα που είναι η

είσοδος και που η έξοδος, παρά αν βάλεις ένα ακόμη while - ειδικά όταν

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

Τα goto exitPaving και continuePaving  δηλώνουν σαφώς που διακόπτεται ή συνεχίζεται ότι η ροή στον βρόχο,

έχεις καλύτερη εποπτεία. Τα continue/break είναι λιγότερο σαφή. Π.χ. με ένα ξεκάρφωτο break δεν φαίνεται

αμέσως σε ποιό σημείο συνεχίζεται η ροή.

 

Ένα άλλο πιο κοινό παράδειγμα είναι όταν π.χ. έχεις τρία (ή περισσότερα) εμφωλευμένα for και

θέλεις από το εσωτερικό να βγεις έξω όταν ικανοποιείται κάποια συνθήκη.

Με ένα goto τελειώνει το θέμα με απλότητα και σαφήνεια, το έχω κάνει πολλές φορές.

 

-

Επεξ/σία από V.I.Smirnov
Δημοσ.

To goto σε Basic, προ VB είχε απλά ένα νούμερο, αλλά εδώ και καιρό παίρνει ετικέτα, άρα είναι περιγραφικό και δουλεύει τόσο για να επισημαίνει το προορισμό όσο και από που πάμε εκεί. Με την εύρεση του διορθωτή πάμε άμεσα. 

Η χρήση του goto δεν είναι επιδίωξη αλλά αναγκαιότητα. Συνήθως μπαίνει σε spaggeti κώδικα. Αν δεν έμπαινε θα έπρεπε να καλείται κώδικας κάθε φορά και αυτό σημαίνει χώρος στο stack και χρόνος για πέρασμα δεικτών/μεταβλητών. Το goto δεν σε βγάζει από την τρέχουσα διαδικασία. Δεν γίνεται να κάνεις "άλμα" σε κώδικα άλλης διαδικασίας (αυτά μόνο σε Assembly γίνεται, με ό,τι μπορεί να έχει πάρει ο προγραμματιστής...)

Δημοσ.

 

Η υπόσχεσή μου να δούμε πώς αλλιώς γίνεται το control flow ισχύει αλλά ένα μινιμουμ courtesy περιμένω και από απέναντι.

 

Στα γρηγροα που το βλεπω, το exitPaving γινεται ενα break και το continuePaving ενα.... continue

 

Βασικα ειναι πολυ κακο παραδειγμα για goto. 

  • Like 3
Δημοσ.

Όταν θα αρχίσεις να φτιάχνεις και να χρησιμοποιείς σωστά ιεραρχίες κλάσεων και αρχίσεις να σκέφτεσαι με τη λέξη design. Ή ας πούμε όταν θα ξέρεις να εξηγήσεις σε κάποιον άλλο τι είναι το LSP και να δώσεις παραδείγματα του πότε καταπατάται.

 

Έχω καιρό μπροστά μου...

 

 

Αυτό όμως είναι πιο πολύ αποστήθιση παρά κατανόηση. Πότε τη χρησιμοποιούμε; "όταν θέλουμε να αποτρέψουμε τη χρήση της ως base class". ΟΚ, και πότε θέλουμε να αποτρέψουμε τη χρήση της ως base class?  :-)

 

Χμμ... Όταν θα δουλέψουμε μαζί με άλλους ανθρώπους οι οποίοι ίσως να μη χρειάζεται να γνωρίζουν την υλοποίηση του τάδε component, αλλά η δουλειά τους απαιτεί τη χρήση του component, και έτσι 'προστατεύουμε' το κώδικα για να μη τα κάνουν μαντάρα ?

 

 

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

 

Πρακτικά μου είναι από πολύ δύσκολο ως αδύνατο να σκεφτώ περίπτωση όπου κάποιος που δεν είναι ήδη έμπειρος επαγγελματίας θα είχε ουσιαστικό λόγο να κάνει optimization, ενώ από την άλλη όπως είπα ο ευανάγνωστος κώδικας είναι στόχος και παρέχει ωφέλη για όλους, από τον πιο newbie μέχρι τον John Carmack.

 

Εδώ τώρα πάντα βρίσκεται κάποιος να πει ότι άμα δε μάθεις να γράφεις τους αλγορίθμους σωστά (optimized) τότε δε μαθαίνεις, γίνεσαι άσχετος "προγραμματιστής" και τέτοια. Αυτοί που τα λένε αυτά έχουν σίγουρα δίκιο σε ένα πράγμα: είναι ένα αξιόλογο skill να ξέρεις και να μπορείς να γράφεις καλό, γρήγορο κώδικα από την αρχή και αν χρειαστεί μετά να τον κάνεις optimize ακόμα περισσότερο. Αλλά αυτό το skill είναι που ξεχωρίζει τον έμπειρο επαγγελματία από τον έμπειρο guru. Είναι άπειρα σημαντικότερο να γράφει κανείς κατανοητό κώδικα. Το μόνο που είναι πιο σημαντικό από αυτό είναι ο κώδικας να δουλεύει σωστά.

 

Ψάξου αν θέλεις με google στην έκφραση "make it work, make it right, make it fast" -- δεν περιμένω να τη χωνέψεις, αλλά τελείως επιγραμματικά το νόημά της είναι:

Το πρώτο σου μέλημα πρέπει να είναι ο κώδικας να δουλέψει. Στη συνέχεια τον κώδικα που δουλεύει να τον γράψεις όμορφα (χωρίς να χαλάσει φυσικά) και σαν τελευταίο βήμα να τον κάνεις optimize.

 

Δύο λοιπόν σχόλια επί τούτου: α) αν μάθεις να σκέφτεσαι με τη λάθος σειρά προτεραιοτήτων τότε απλά κάποια στιγμή θα πρέπει να ξεμάθεις αυτά που ξέρεις και να τα ξαναμάθεις σωστά, β) στην πράξη το make it fast είτε το κάνεις είτε δεν το κάνεις κανείς δε θα καταλάβει ποτέ τίποτα. Ενώ το make it right θα το καταλάβουν αμέσως όσοι δουλεύουν μαζί σου.

 

Ενδιαφέρον. Λογικό το βρίσκω.

 

 

Περίπου. Αυτό που έχουμε τώρα και δεν είχαμε παλιά λέγεται "δομημένος προγραμματισμός". Διάβασέ το, είναι ενδιαφέρον και έχει ψωμί να ψαχτείς περισσότερο.

 

Προσωπικά έμαθα προγραμματισμό σε άλλη εποχή με άλλα εργαλεία και π.χ. το while true το είδα μπροστά μου πρώτη φορά περίπου 5 χρόνια αφότου ξεκίνησα. Η όλη λογική του δομημένου προγραμματισμού μου φαινόταν ανάποδη, έπρεπε αντί να κάνω τα goto που έκανα πριν όπως μου άρεσε να κάθομαι να σκέφτομαι πώς θα μπορούσε να γραφτεί το ίδιο πράγμα αλλιώς. Και δε μου άρεζε. Αλλά τελικά αποδείχτηκε πως το goto ήταν "λάθος" και το while true "σωστό" για διάφορους λόγους, και ωφελήθηκα πάρα πάρα πολύ αναπτύσσοντας την ικανότητα να σκέφτομαι χωρίς goto.

 

Υπάρχουν κάποιες ελάχιστεςσπάνιες, περιπτώσεις όπου αν θέλεις να κάνεις κάτι χωρίς goto κολυμπάς λίγο κόντρα στο ρεύμα, αλλά αυτές είναι οι εξαιρέσεις που επιβεβαιώνουν τον κανόνα. Αυτά που λέει ο m2000 και ο smirnov προσωπικά δε με πείθουν καθόλου, και ούτε το θεωρώ τυχαίο πως προς υποστήριξη του goto μιλάνε τα συγκεκριμένα δύο πρόσωπα (ευχαρίστως αν θέλουν να δώσουν συγκεκριμένα παραδείγματα να τα συζητήσουμε). Στην τελική βέβαια όπως και όλα τα πράγματα έτσι και το goto έχει τη χρήση του. Το πρόβλημα είναι πως πολλές φορές νομίζει κανείς ότι "εδώ χρειάζεται" επειδή τυχαίνει να μην ξέρει ή να βαριέται ή οτιδήποτε άλλο να το κάνει χωρίς goto.

 

Τελείως speculative παράδειγμα (και ας μη το πάρει παρακαλώ κανείς σαν "απόδειξη" οποιουδήποτε πράγματος), ο smirnov έγραψε "το goto είναι ακόμη και σήμερα χρήσιμο, κυρίως για έξοδο από εμφωλιασμένα for, while ή δομές if-else". Και δεν αμφιβάλλω ότι σε πολλές γλώσσες, αν βρεθείς να έχεις τα προαναφερθέντα εμφωλιασμένα, το goto είναι βολική και γιατί όχι elegant λύση. Αλλά το point είναι πως να κάνεις ένα βήμα πίσω, θα ήταν πρακτικά πάντα καλύτερο design να μην τα έχεις όλα αυτά in the first place. Οπότε εδώ για μένα το goto είναι μια καλή λύση σε ένα πρόβλημα που θα έπρεπε να μην έχει αφήσει κανείς να εμφανιστεί από την αρχή. Ή με άλλα λόγια, αντί για "καλή λύση" είναι μάλλον "ένδειξη προβλήματος". Ελπίζω έτσι να είναι πιο κατανοητό το τι έχω στο μυαλό μου όταν λέω "μπα δε χρειάζεται".

 

 

Κατάλαβα... (Ή νομίζω πως κατάλαβα.)

Thanx για τις μεγάλες και περιεκτικές απαντήσεις!

 

--------------------------------------------------------------------------------------------------------------------------------------------

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

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApplication4
{
    class Program
    {
        static int alfa;
        static int beta;

        #region Main()
        static void Main(string[] args)
        {
            int number = TakeNumbers();
            if (number == 1)
                goto EndOfProgram;
            
            Prosthesi a = new Prosthesi();
            a.TypeMessage();
            a.TypeResult(alfa,beta);

            Afairesi b = new Afairesi();
            b.TypeMessage();
            b.TypeResult(alfa,beta);

            EndOfProgram:
            Console.WriteLine("The program will now end...");
            Console.ReadKey();
        }
        #endregion

        #region TakeNumbers()
        public static int TakeNumbers()
        {
            try
            {
                Console.WriteLine("Enter first number :");
                alfa = int.Parse(Console.ReadLine());

                Console.WriteLine("Enter second number :");
                beta = int.Parse(Console.ReadLine());

                return 0;
            }
            catch (FormatException)
            {
                Console.WriteLine("The number was not in the correct format!");
                return 1;
            }
            catch (OverflowException)
            {
                Console.WriteLine("The number was too large!");
                return 1;
            }
            catch (Exception)
            {
                Console.WriteLine("An unexpected error occured.");
                return 1;
            }

        }
        #endregion
    }

    #region interface IGeneral
    interface IGeneral
    {
        void TypeMessage();
        void TypeResult(int a, int ;
    }
    #endregion

    #region class Prosthesi : IGeneral
    class Prosthesi : IGeneral
    {
        public void TypeMessage()
        {
            Console.WriteLine("This is Prosthesi.");
        }
        public void TypeResult(int a, int 
        {
            int c = a + b;
            Console.WriteLine(c);
        }
    }
    #endregion

    #region class Afairesi : IGeneral
    class Afairesi : IGeneral
    {
        public void TypeMessage()
        {
            Console.WriteLine("This is Afairesi.");
        }
        public void TypeResult(int a, int 
        {
            int c = a - b;
            Console.WriteLine(c);
        }
    }
    #endregion

υ.γ. ξέρω ότι θα μπορούσα την ίδια λειτουργικότητα να την έκανα πιο απλά, σε μια μέθοδο χωρίς interface, κλάσεις, κτλπ, αλλά είναι ένα απ' τα πολλά προγραμματάκια που φτιάχνω συνδυάζοντας νέα πράγματα που μαθαίνω μεταξύ τους για εξάσκηση.

Δημιουργήστε ένα λογαριασμό ή συνδεθείτε για να σχολιάσετε

Πρέπει να είστε μέλος για να αφήσετε σχόλιο

Δημιουργία λογαριασμού

Εγγραφείτε με νέο λογαριασμό στην κοινότητα μας. Είναι πανεύκολο!

Δημιουργία νέου λογαριασμού

Σύνδεση

Έχετε ήδη λογαριασμό; Συνδεθείτε εδώ.

Συνδεθείτε τώρα

  • Δημιουργία νέου...