Όχι, σε καμμία σχεδόν περίπτωση.
Ο σκοπός του transaction είναι να κάνει ένα σύνολο αλλαγές στη βάση σε ένα βήμα το οποίο θα πετύχει ή θα αποτύχει στο σύνολό του. Αν για κάποιο λόγο σταματήσει, οι αλλαγές που έκανε χάνονται. Αν ολοκληρωθεί, οι αλλαγές που έκανε δεν μπορούν να αντιστραφούν. Τα transactions αναγκαστικά κρατάνε όσο το δυνατόν λιγότερο για να μην εμποδίζουν άλλους χρήστες της εφαρμογής ή ακόμα και άλλα connection από την ίδια την εφαρμογή να δουλέψουν με τη βάση. Τέλος, τα transactions δεν κλείνουν από μόνα τους. Πρέπει το πρόγραμμα να δώσει εντολή να κλείσουν.
Ο σκοπός του Undo είναι να αντιστρέφει μία-μία ενέργειες του χρήστη οι οποίες έχουν ήδη ολοκληρωθεί. Οι διαφορές με το transaction είναι πολλές:
- Το transaction αφορά αλλαγές στη βάση, ενώ το Undo αφορά ενέργειες του χρήστη. Οι ενέργειες του χρήστη μπορούν να προκαλέσουν πολλά transactions.
- Το transaction αφορά ενέργειες που γίνονται σε ελάχιστο χρόνο (ms). Το Undo αφορά ενέργειες που μπορούν να απέχουν μεταξύ τους ώρες.
- Το transaction πρέπει να κλείσει με εντολή για να μη χαθούν οι αλλαγές που έκανε. Το Undo θεωρεί ότι οι αλλαγές έχουν αποθηκευτεί ήδη πολύ ώρα πριν εκτελεστεί.
- Το transaction δεσμεύει εγγραφές στη βάση οι οποίες χρησιμοποιούνται απ' όλους τους χρήστες της βάσης. Το Undo (ανάλογα με την υλοποίηση του) δεν δεσμεύει τίποτε.
Γενικά, ο τρόπος με τον οποίο υλοποιείται το Undo πάντως μοιάζει με τον τρόπο λειτουργίας ενός transaction. Κάθε εντολή που εκτελείται αποθηκεύει αρκετά δεδομένα σε ένα Undo log έτσι ώστε να μπορεί αργότερα να να επανέλθει η εφαρμογή στην κατάσταση πριν εκτελεστεί η εντολή. Όταν ο χρήστης ζητάει Undo, η εφαρμογή διαβάζει την τελευταία εντολή από το Undo log και επαναφέρει την εφαρμογή σε αυτή την κατάσταση. Αυτό μπορεί να ακούγεται πολύ νεφελώδες, γιατί το τί αποθηκεύεται και πως εκτελείται η διαδικασία του Undo μπορεί να διαφέρει ΠΟΛΥ και εξαρτάται άμεσα και από την αρχιτεκτονική της εφαρμογής.
Μερικοί τρόποι υλοποίησης:
- Σε κάθε Undo entry αποθηκεύουμε την κατάσταση όλων των αντικειμένων που επηρεάζονται από την ενέργεια (π.χ. με Serialization). Όταν ο χρήστης ζητήσει Undo, κάνουμε deserialize τα αντικείμενα και τα επαναφέρουμε στην κατάσταση που ήταν πριν και το ξανασώζουμε στη βάση. Αν, για παράδειγμα έκανες μεταφορά χρημάτων από ένα λογαριασμό σε ένα άλλο, θα αποθήκευες την κατάσταση και των δύο λογαριασμών πριν εκτελέσεις την μεταφορά.
- Στα συν:
Ακούγεται σχετικά απλό στην υλοποίηση, καθώς δεν απαιτεί ιδιαίτερη φροντίδα για τις ενέργειες.
Φαινομενικά δεν έχει ιδιαίτερες απαιτήσεις στη σχεδίαση της εφαρμογής.
- Στα πλην:
Καταναλώνει πολύ μνήμη, ειδικά αν τα αντικείμενα είναι βαρειά, ή αν οι αλλαγές επηρεάζουν πολλά αντικείμενα.
Απαιτεί την αποθήκευση ολόκληρου του αντικειμένου ακόμη και αν δεν χρειάζεται. Όταν επηρεάζονται πολλά αντικείμενα πρέπει να αποθηκευθούν όλα, με τέτοιο τρόπο ώστε να επανέλθουν όλα σωστά.
Δεν ταιριάζει σε εφαρμογές web καθώς απαιτεί να αποθηκευτούν πολλά δεδομένα για κάθε χρήστη - στη βάση.
Ο τρόπος αυτός δεν είναι άσχημος αν η εφαρμογή μας έχει λίγες οντότητες, αντικείμενα ή πίνακες ή αν θέλουμε λίγα επίπεδα Undo. Σε πιο περίπλοκες εφαρμογές όμως, τα πράγματα αρχίζουν και δυσκολεύουν.
- Σε κάθε Undo entry αποθηκεύουμε μόνο τις παραμέτρους της ενέργειας. Για κάθε ενέργεια ορίζουμε μία άλλη ενέργεια η οποία αντισταθμίζει την πρώτη. Όταν ο χρήστης ζητήσει Undo, εκτελούμε την αντισταθμιστική ενέργεια. Στο παράδειγμα της μεταφοράς χρημάτων, θα αποθηκεύαμε στο undo log μόνο τον κωδικό της μεταφοράς, τον αρχικό και τελικό λογαριασμό και το ποσό μεταφοράς. Σε περίπτωση Undo θα εκτελούσαμε την αντίστροφη μεταφορά χρημάτων.
Συνήθως αυτός ο τρόπος υλοποιείται δημιουργώντας ένα Command αντικείμενο για κάθε ενέργεια που μπορεί να εκτελέσει ο χρήστης με μία μέθοδο Do και μία Undo για να αντιστρέψει την ενέργεια. Η Do αποθηκεύει τις παραμέτρους της ενέργειας στο Undo log και εκτελεί π.χ. τη μεταφορά ενώ η Undo χρησιμοποιεί της παραμέτρους για να εκτελέσει την αντίστροφη μεταφορά. Μπορεί κανείς να αποθηκεύσει το Command object με Serialization στο Undo log χωρίς πρόβλημα καθώς το μέγεθος του είναι μικρό και δεν συνδέεται με άλλα αντικείμενα.
- Στα συν:
Καταναλώνει πολύ λιγότερο χώρο στη μνήμη
Ααποθηκεύεται εύκολα ακόμα και σε βάση
Δεν επηρεάζεται από την πολυπλοκότητα της εφαρμογής.
- Στα πλην:
Η εφαρμογή πρέπει να έχει σχεδιαστεί έτσι ώστε όλες οι ενέργειες του χρήστη να αντιστοιχούν σε Commands. Με άλλα λόγια, η εφαρμογή πρέπει να σχεδιαστεί σωστά (σοφό αυτό).
Τα περισσότερα UI frameworks βασίζονται στην ιδέα του Command τόσο για το Undo όσο και για να αποσυνδέσουν το UI (μενού, κουμπιά κλπ) από τον κώδικα που εκτελεί τις ενέργειες και να αντιστοιχήσουν π.χ. το ίδιο Command σε διαφορετικά κουμπιά, σε διαφορετικές οθόνες και διαφορετικά μενού.
Τέλος, ανεξάρτητα από την υλοποίηση μπορείς να προσθέσεις και Redo φτιάχνοντας ένα Redo log και μεταφέροντας σε αυτό ένα Undo entry κάθε φορά που ο χρήστης ζητάει Undo.
Παναγιώτης Καναβός, Freelancer
Twitter: http://www.twitter.com/pkanavos