Τέσσερις κανόνες απλούστερου σχεδιασμού λογισμικού iOS

Στα τέλη της δεκαετίας του 1990, παράλληλα με την ανάπτυξη Extreme Programming, ο διάσημος προγραμματιστής λογισμικού Kent Beck κατέληξε σε έναν κατάλογο κανόνων για απλό σχεδιασμό λογισμικού.

Σύμφωνα με τον Kent Beck, ένας καλός σχεδιασμός λογισμικού:

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

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

Εκτελεί όλες τις δοκιμές

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

Δυστυχώς, στο δοκιμαστικό σύμπαν ανάπτυξης του iOS αποφεύγονται οι περισσότερες φορές ... Αλλά για να δημιουργήσουμε ένα καλά σχεδιασμένο λογισμικό, πρέπει πάντα να γράφουμε Swift κώδικα με το testability στο μυαλό.

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

Αρχή της ενιαίας ευθύνης (SRP)

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

Ας δώσουμε ένα παράδειγμα κάποιου κώδικα που είναι πραγματικά δύσκολο να δοκιμαστεί και μετά από αυτό το refactor χρησιμοποιώντας SRP. Στη συνέχεια, συζητήστε πώς έκανε τον κώδικα δοκιμασμένο.

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

Ο κώδικας για αυτήν την εφαρμογή μοιάζει με τους εξής:

Πώς μπορούμε να δοκιμάσουμε αυτόν τον κώδικα; Τι πρέπει πρώτα να δοκιμάσουμε; Είναι σωστή η τιμή έκπτωσης; Πώς μπορούμε να χλευάζουμε τα γεγονότα πληρωμής για να ελέγξουμε την έκπτωση;

Η σύνταξη δοκιμασιών για αυτή την τάξη θα ήταν περίπλοκη, θα έπρεπε να βρούμε έναν καλύτερο τρόπο να την γράψουμε. Λοιπόν, πρώτα να λύσουμε το μεγάλο πρόβλημα. Πρέπει να ξεδιπλώσουμε τις εξαρτήσεις μας.

Βλέπουμε ότι έχουμε λογική για τη φόρτωση του προϊόντος μας. Έχουμε Εκδηλώσεις Πληρωμής που καθιστούν το χρήστη επιλέξιμο για έκπτωση. Έχουμε Εκπτώσεις, έναν υπολογισμό έκπτωσης και ο κατάλογος συνεχίζεται.

Ας προσπαθήσουμε λοιπόν απλά να τις μεταφράσουμε σε κώδικα Swift.

Δημιουργήσαμε ένα PaymentManager που διαχειρίζεται τη λογική μας που σχετίζεται με τις πληρωμές, και το Separate PriceCalculator ότι είναι εύκολο να ελεγχθεί. Επίσης, ένας φορτωτής δεδομένων που είναι υπεύθυνος για την αλληλεπίδραση δικτύου ή βάσης δεδομένων για τη φόρτωση των προϊόντων μας.

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

Επομένως, ο ελεγκτής προβολής πληρωμής μπορεί να έχει τα εξής χαρακτηριστικά:

Μπορούμε να γράψουμε τώρα δοκιμές όπως

  • testCalculatingFinalPriceWithoutCoupon
  • testCalculatingFinalPriceWithCoupon
  • testCouponExists

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

Ενεση εξάρτησης

Η δεύτερη αρχή είναι η ένεση εξάρτησης. Και είδαμε από τα παραπάνω παραδείγματα ότι χρησιμοποιήσαμε ήδη ένεση εξάρτησης στους αρχικοποιητές αντικειμένων.

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

Μια καλή τεχνική είναι η δημιουργία πρωτοκόλλων για τα αντικείμενά μας και η παροχή συγκεκριμένης εφαρμογής από το πραγματικό και το ψεύτικο αντικείμενο, όπως τα εξής:

Τώρα μπορούμε εύκολα να αποφασίσουμε ποια κατηγορία θέλουμε να εισάγουμε ως εξάρτηση.

Η στενή σύζευξη δυσκολεύει να γράψει δοκιμές. Έτσι, ομοίως, όσο περισσότερες δοκιμές γράφουμε, τόσο περισσότερο χρησιμοποιούμε αρχές όπως DIP και εργαλεία όπως ένεση εξάρτησης, διεπαφές και αφαίρεση για να ελαχιστοποιήσουμε τη σύζευξη.

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

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

Δεν περιέχει επικάλυψη

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

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

Ας υποθέσουμε ότι έχουμε στην εφαρμογή μας μια τυπική ενότητα συνομιλίας. Μια νέα απαίτηση έρχεται και τώρα θέλουμε να εφαρμόσουμε έναν νέο τύπο συνομιλίας - μια ζωντανή συνομιλία. Μια συνομιλία που θα περιέχει μηνύματα με μέγιστο αριθμό χαρακτήρων 20 και αυτή τη συζήτηση θα εξαφανιστεί όταν απορρίψουμε την προβολή συνομιλίας.

Αυτή η συζήτηση θα έχει τις ίδιες απόψεις με την τρέχουσα συζήτηση, αλλά θα έχει μερικούς διαφορετικούς κανόνες:

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

2. Τα μηνύματα συνομιλίας πρέπει να είναι σύντομα, όχι περισσότερο από 20 χαρακτήρες για το μήνυμα.

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

Ας υποθέσουμε ότι χρησιμοποιούμε αρχιτεκτονική MVP και χειριζόμαστε τη λογική για την αποστολή μηνυμάτων συνομιλίας στον παρουσιαστή μας. Ας προσπαθήσουμε να προσθέσουμε νέους κανόνες για τον νέο τύπο chat που ονομάζεται ζωντανή συνομιλία.

Μια αφηρημένη εφαρμογή θα ήταν η εξής:

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

Αυτό είναι όπου χρησιμοποιείται το πρότυπο πρότυπο. Το Πρότυπο Πρότυπο χρησιμοποιείται όταν χρειαζόμαστε πολλαπλές υλοποιήσεις ενός αλγορίθμου. Το πρότυπο ορίζεται και στη συνέχεια βασίζεται σε περαιτέρω παραλλαγές. Χρησιμοποιήστε αυτήν τη μέθοδο όταν οι περισσότερες υποκατηγορίες πρέπει να εφαρμόσουν την ίδια συμπεριφορά.

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

Τώρα μπορούμε να κάνουμε τον παρουσιαστή μας σύμφωνο με το IChatPresenter

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

Τώρα μπορούμε να προσφέρουμε Δημιουργία αντικειμένων που συμμορφώνονται με τις φάσεις του παρουσιαστή και να διαμορφώνουν αυτές τις λειτουργίες με βάση τις ανάγκες τους.

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

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

Εκφραστικός

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

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

Ονομασία

Αριθμός ένα πράγμα που κάνει τον κώδικα πιο εκφραστικό - και είναι ονομασία. Είναι σημαντικό να γράψετε ονόματα που:

  • Αποκαλύψτε την πρόθεση
  • Αποφύγετε την παραπληροφόρηση
  • Είναι εύκολο να αναζητηθούν

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

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

Χρησιμοποιώντας SRP

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

Δοκιμασία γραφής

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

Ελαχιστοποιήστε τον αριθμό των κατηγοριών και μεθόδων

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

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

Μια καλή ιδέα στο iOS είναι να κόψουμε τις κλήσεις διαμόρφωσης που συνήθως κάνουμε στις λειτουργίες viewDidLoad ή viewDidAppear.

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

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

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

Υπάρχουν πολλοί τρόποι για να κάνετε UIViewControllers μικρό μου προτίμηση είναι να χρησιμοποιήσετε μια αρχιτεκτονική που έχει καλύτερο διαχωρισμό των ανησυχιών κάτι σαν VIPER ή MVP, αλλά αυτό δεν σημαίνει ότι δεν μπορούμε να το κάνουμε καλύτερα σε MVC μήλο καλύτερα επίσης.

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

Μερικά πράγματα που μπορούν απλά να αποφευχθούν χωρίς δικαιολογία στους ελεγκτές προβολής είναι:

  • Αντί να γράφετε απευθείας κώδικα δικτύου, θα πρέπει να υπάρχει ένα NetworkManager μια κλάση που να είναι υπεύθυνη για κλήσεις δικτύου
  • Αντί να χειριζόμαστε δεδομένα σε ελεγκτές προβολής, μπορούμε απλά να δημιουργήσουμε ένα DataManager μια τάξη που είναι υπεύθυνη γι 'αυτό.
  • Αντί να παίζουμε με συμβολοσειρές UserDefaults στο UIViewController μπορούμε να δημιουργήσουμε μια πρόσοψη πάνω από αυτό.

Συμπερασματικά

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

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

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

Εάν έχετε οποιεσδήποτε ερωτήσεις ή σχόλια, μπορείτε να αφήσετε ένα σημείωμα εδώ ή να μου στείλετε email στο arlindaliu.dev@gmail.com.