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

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

Δημοσ.

Πώς ακριβώς θα μπορούσα να υλοποιησω events σε PHP? Το observer pattern θα ήταν μία σωστή λύση? Επίσης διάβασα ότι αυτο μπορεί να γίνει και με decorators αλλά δεν είμαι σίγουρος πόσο σωστό είναι.

 

Γενικά αυτό που θέλω να κάνω είναι οταν σε ένα σημείο κώδικα κάνω raise ένα event, όπου θα κάνει wrap μερικά data, κάπου αλλού στον κώδικα (μπορεί σε παραπάνω από ένα σημεία) να γίνεται catch αυτό το event και να εκτελούνται ενέργειες πάνω σε αυτά τα data.

Δημοσ.

Για event driven καταστάσεις υπάρχουν γενικά μιλώντας τρεις συνηθισμένες περιπτώσεις (αυτό εδώ τώρα δεν είναι γραμμένο στις πλάκες του Μωυσή, είναι δική μου περίληψη του σκηνικού):

  1. Observer
  2. Mediator
  3. Pub/sub

Observer

Το κλασικό σενάριο, ένα αντικείμενο έχει μια method με την οποία μπορείς να του πεις όταν γίνει το Α κάλεσε το Β (ή πολλά Β). Απλό λιτό κατανοητό γνωστά χαρακτηριστικά και προβλήματα (που σχεδόν σίγουρα δε θα σε ενδιαφέρουν στην PHP). Μπορείς να το κάνεις manually, ντεμεκ για "πιο στάνταρ" μπορείς να το κάνεις υλοποιώντας SplSubject και SplObserver αλλά προσωπικά τα θεωρώ next to useless. Γενικά δεν είναι για περίπλοκες καταστάσεις γιατί η πολυπλοκότητα του τι γίνεται αυξάνεται εκθετικά όσο περισσότερο το χρησιμοποιείς και καλή τύχη στο debugging μετά. Αλλά nothing wrong with it για κάτι απλό λιτό κατανοητό.

 

Mediator

Πιο advanced κατάσταση γιατί εκτός από το "source" και το/τα "sink" του κάθε event έχεις και ένα ενδιάμεσο component να κάνει τον τροχονόμο, observers και subjects δεν ξέρουν ο ένας για τον άλλο. Αυτό μεταφέρει την πολυπλοκότητα στον τροχονόμο αλλά τουλάχιστον έτσι ξέρεις που να κοιτάξεις, και ο τροχονόμος δεν κάνει ταυτόχρονα άλλα πράγματα οπότε είναι κάπως πιο sane. Επίσης εφόσον κεντροποιείς το event dispatching μπορείς να βάλεις και έξτρα features που θα είναι αυτόματα διαθέσιμα παντού. Για μη εκπαιδευτικούς σκοπούς θα έλεγα κατευθείαν βάζεις Symfony Event Dispatcher (είναι αυτόνομο component, απλά composer require). Αυτή είναι η λύση που θα πρότεινα ως default αν δεν είχα κανένα δεδομένο για το τι πας να κάνεις.

 

Pub/sub

Ακόμα πιο γενική μορφή του mediator, όπου πλέον αντί για "events" έχεις "messages". Η διαφορά σε σχέση με mediator είναι κυρίως ότι εδώ ο "mediator" δεν είναι ένα απλό αντικείμενο αλλά ολόκληρο combo από infrastructure, με σκοπό να έχουμε απομακρυσμένους listeners, καλύτερο scaling και γενικά πράγματα που δε θα σε απασχολήσουν ποτέ σε μια "απλή εφαρμογή".

 

Suggested reading για pub/sub αλλά το προτείνω και πιο γενικά γιατί θα μάθεις πράγματα το κλασικό paper The Many Faces of Pub/Sub.

Δημοσ.

Το event που θα έχω επί της ουσίας θα είναι ένα message του στυλ ClientNameChanged και θα έχει public fields όπως ID, newName κτλ.

 

Αυτό το event μόλις γίνει raise (π.χ από το domain) θα γίνεται catch από διάφορους handlers και ο κάθε handler θα κάνει κάτι συγκεκριμένο (p.x ένας θα κάνει τις αλλαγές στην βάση, άλλος θα καλεί κάποιο domain service κτλ).

Θα δω αν αυτό μπορεί να γίνει με observer αλλιώς θα πάω στις επόμενες λύσεις.

Δημοσ.

Δε νομίζω ότι μπορείς να το κάνεις με observer αυτό. Πώς θα γίνει δηλαδή; Το observable θα είναι ο client, και κάθε φορά που θα δημιουργείται ένα instance θα πρέπει με κάποιο τρόπο να κάνεις attach επάνω του ένα μάτσο handlers για να γίνεται αυτό που περιγράφεις; Και μάλιστα με τρόπο που δεν είναι bad engineering?

 

Mediator θες, οπότε

 composer require symfony/event-dispatcher
  • Like 1
Δημοσ.

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

Δημοσ.

Το αν είναι ο mediator η κατάλληλη λύση για την περίπτωση συζητείται.

 

Αλλά υποθέτοντας ότι είναι (και σίγουρα αν όχι εδώ τότε είναι σε άλλες περιπτώσεις), τι ακριβώς είναι αυτό που σε ξενίζει; If I had to guess θα έλεγα ότι αρκετές φορές αυτά τα dependencies είναι κρυμμένα ως static calls, που ναι μεν σου αφήνουν τον constructor όμορφο αλλά είναι άπειρα χειρότερα όσον αφορά testability/maintainability.

Δημοσ.

Μα αν ο dispatcher ειναι p.x constructor dependency δεν μπορεί να σου προκαλέσει πρόβλημα σε ένα repository όταν θα πας να κάνεις getById(id)??

Διότι πλέον  το repository θα πρέπει να γνωρίζει για ένα dependency που του είναι τελείως άχρηστο, απλά και μόνο για να το περάσει στον constructor τοu Client. Εκτός και αν κάτι δεν εχω καταλάβει καλά όσον αφορά το repository.

Δημοσ.

Όχι απαραίτητα.

 

Αν είναι καλά φτιαγμένα τα πράγματα το βήμα όπου ένα μάτσο scalars μετατρέπεται σε αντικείμενο που επιστρέφουμε για χρήση (συνήθως το συναντάς ως "hydration") γίνεται από αυτόνομο component, οπότε μπορείς να περάσεις το dependency στο hydrator και ο hydrator να καλέσει σωστά τον constructor.

 

Άλλο πράγμα που μπορεί να γίνει είναι να βάλεις τα περι dispatcher πάνω σε μια class ObservableWidgetDecorator η οποία θα παίρνει dependency και τον dispatcher και το σκέτο Widget (και τα δυο θα κάνουν implement WidgetInterface) και θα προωωθεί getters/setters στο Widget ενώ ταυτόχρονα κανονίζει και τα events.

 

Άλλο ακόμα πράγμα είναι να κάνεις το Widget entity observable (λύση #1 που λέγαμε στην αρχή) και να φτιάξεις κάποιο model που μοντελοποιεί τη διαδικασία "widget under modification" και αναλαμβάνει να κάνει hook up event handlers όπου χρειάζεται (όλα τα instances που εμπλέκονται θα είναι κάποιου είδους dependencies, μάλλον method arguments).

 

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

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

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

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

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

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

Σύνδεση

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

Συνδεθείτε τώρα
  • Δημιουργία νέου...