Imaginez un livre dans lequel il n'y a pas de division en chapitres, mais tout va sans décomposition logique et sémantique, un livre où il n'y a pas de paragraphes, pas de points et de virgules, un livre où la première ligne raconte une chose, la seconde une autre, la troisième encore première chose.
Présenté?
Pourriez-vous comprendre de quoi parle le livre?
À quelle vitesse pourriez-vous trouver le passage qui vous intéresse?
Votre code, ainsi que le contenu du livre, doivent être structurés de manière à ce que le code soit facile à lire et à en transmettre le sens.
Dans cet article, je vais montrer des exemples d'organisation de code dans lesquels les classes auront la même séquence de blocs principaux et leur répartition.
Pour plus de commodité, j'utiliserai le mot classe (classe), mais j'impliquerai n'importe quel type de type (classe, struct, énum).
Grâce à l'application de ces conseils, votre code deviendra lisible, ce qui à l'avenir lui fournira commodité et rapidité de travail.
Bien sûr, les conseils décrits peuvent être mis à niveau à votre guise, en respectant les principes de base.
Tout d'abord, comparons le même code de deux manières.
Un exemple d'une classe en désordre:
| final class MessyViewController: UIViewController { |
| |
| private let userService = UserService() |
| var userID: String? |
| private var userList: [User]? |
| |
| @IBOutlet private weak var searchBar: UISearchBar! |
| |
| weak var delegate: SomeDelegate? |
| |
| @IBAction private func cancelButtonPressed(_ sender: UIBarButtonItem) { |
| dismiss(animated: true, completion: nil) |
| } |
| |
| override func viewDidLoad() { |
| super.viewDidLoad() |
| // Do any additional setup after loading the view. |
| navigationController?.navigationBar.backgroundColor = .red |
| navigationItem.title = "Some" |
| } |
| @IBOutlet private weak var tableView: UITableView! |
| } |
Ce code est similaire à un vidage de méthodes, de variables et de sorties, dans lequel tout se confond, il est difficile de comprendre à quoi il se réfère et à quel endroit quoi chercher.
Un exemple de classe pure:
| final class CleanViewController: UIViewController { |
| |
| // MARK: - IBOutlets |
| |
| @IBOutlet private weak var searchBar: UISearchBar! |
| @IBOutlet private weak var tableView: UITableView! |
| |
| // MARK: - Public Properties |
| |
| var userID: String? |
| weak var delegate: SomeDelegate? |
| |
| // MARK: - Private Properties |
| |
| private let userService = UserService() |
| private var userList: [User]? |
| |
| // MARK: - Lifecycle |
| |
| override func viewDidLoad() { |
| super.viewDidLoad() |
| |
| setupNavigationBar() |
| } |
| |
| // MARK: - Private Methods |
| |
| private func setupNavigationBar() { |
| navigationController?.navigationBar.backgroundColor = .red |
| navigationItem.title = "Some" |
| } |
| |
| // MARK: - IBActions |
| |
| @IBAction private func cancelButtonPressed(_ sender: UIBarButtonItem) { |
| dismiss(animated: true, completion: nil) |
| } |
| |
| } |
Ligne vide 38 - indenter une ligne de la dernière méthode afin que vous puissiez voir où se termine le dernier crochet de fermeture de la méthode et où se termine la classe.
La même fonctionnalité est montrée dans les deux exemples, mais la différence est que la deuxième option a une structure claire, grâce à laquelle la logique est plus évidente, le code est facile à lire, vous pouvez trouver rapidement ce que vous recherchez dedans, et en plus, c'est juste agréable de le regarder.
Principes de base pour la formation d'une structure de classe pure:
- Utilisez toujours // MARK: -
- Donnez les noms des étiquettes et définissez leur priorité
- Transformer la logique des méthodes du cycle de vie en méthodes distinctes
- Nous utilisons l' extension pour implémenter les protocoles
- Sélectionner des éléments logiquement liés
- Nous supprimons les inutilisés
- Automatiser la routine
1. Utilisez toujours // MARK: -
Pour faciliter la lecture, le livre est divisé en chapitres, et il sera plus confortable pour nous de travailler si nous créons une table des matières en utilisant
// MARK: - .
Cette étiquette se démarque non seulement de l'ensemble du code, mais crée également automatiquement une table des matières - elle met en évidence les sections du code en gras dans la liste des éléments de ce fichier.
Vous pouvez afficher la table des matières d'un fichier en cliquant sur le bouton après la flèche droite (>) tout en haut du fichier après le nom de ce fichier ou ctr + 6 (menu des éléments du document).2. Nous donnons les noms des étiquettes et nous établissons leur séquence
Vous trouverez ci-dessous les principales étiquettes pour diviser le code en blocs connectés logiquement et leur séquence:
| // MARK: - IBOutlets |
| |
| // MARK: - Public Properties |
| |
| // MARK: - Private Properties |
| |
| // MARK: - Initializers |
| |
| // MARK: - Lifecycle |
| |
| // MARK: - Public Methods |
| |
| // MARK: - Private Methods |
| |
| // MARK: - IBActions |
Lorsque vous utilisez cette méthode de regroupement, on peut facilement naviguer dans le code de n'importe quelle classe.
3. Extraire la logique des méthodes du cycle de vie dans des méthodes distinctes
La logique à l'intérieur des méthodes de cycle de vie de ViewController doit être placée dans des méthodes distinctes, même si vous devez créer une méthode avec une ligne de code. Aujourd'hui est un, et demain est dix.
| ❌ NOT Preferred |
| |
| override func viewDidLoad() { |
| super.viewDidLoad() |
| |
| navigationController?.navigationBar.backgroundColor = .red |
| someButton.layer.cornerRadius = 10 |
| someButton.layer.masksToBounds = true |
| navigationItem.title = "Some" |
| print("Some") |
| } |
| |
| |
| ✅ Preferred |
| |
| // MARK: - Lifecycle |
| |
| override func viewDidLoad() { |
| super.viewDidLoad() |
| |
| setupNavigationBar() |
| setupSomeButton() |
| printSome() |
| } |
| |
| |
| // MARK: - Private Methods |
| |
| private func setupNavigationBar() { |
| navigationController?.navigationBar.backgroundColor = .red |
| navigationItem.title = "Some" |
| } |
| |
| private func setupSomeButton() { |
| someButton.layer.cornerRadius = 10 |
| someButton.layer.masksToBounds = true |
| } |
| |
| private func printSome() { |
| print("Some") |
| } |
Du fait que les détails de mise en œuvre sont externalisés vers des méthodes tierces, la logique du cycle de vie devient plus claire.
4. Utilisez l'extension pour implémenter les protocoles
Sortez l'implémentation du protocole dans l'extension marquée
// MARK: - SomeProtocol :
| ❌ NOT Preferred |
| |
| final class CleanViewController: UIViewController, UITableViewDataSource, UITableViewDelegate { |
| |
| // all methods |
| } |
| |
| |
| ✅ Preferred |
| |
| final class CleanViewController: UIViewController { |
| |
| // class stuff here |
| |
| } |
| |
| |
| // MARK: - Table View Data Source |
| extension CleanViewController: UITableViewDataSource { |
| |
| func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { |
| |
| return userList?.count ?? 0 |
| } |
| |
| func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { |
| |
| let cell = UITableViewCell() |
| return cell |
| } |
| |
| } |
Cette étiquette contiendra tout ce qui concerne ce protocole - tout ce qui est seulement ici et pas besoin d'aller ailleurs, sinon les méthodes et les propriétés du protocole seront dispersées dans toute la classe.
5. Sélectionnez les éléments logiquement liés
Pour augmenter la visibilité, il est nécessaire de sélectionner des éléments logiquement liés à l'aide d'une ligne vide:
| ❌ NOT Preferred |
| |
| private func showActivityIndicator(on viewController: UIViewController) { |
| activityIndicator.center = viewController.view.center |
| loadingView.backgroundColor = #colorLiteral(red: 0, green: 0, blue: 0, alpha: 1) |
| loadingView.alpha = 0.5 |
| activityIndicator.hidesWhenStopped = true |
| activityIndicator.style = .whiteLarge |
| loadingView.center = viewController.view.center |
| loadingView.clipsToBounds = true |
| loadingView.layer.cornerRadius = 15 |
| viewController.view.addSubview(loadingView) |
| viewController.view.addSubview(activityIndicator) |
| activityIndicator.startAnimating() |
| } |
| |
| |
| ✅ Preferred |
| |
| private func showActivityIndicator(on viewController: UIViewController) { |
| activityIndicator.center = viewController.view.center |
| activityIndicator.hidesWhenStopped = true |
| activityIndicator.style = .whiteLarge |
| |
| loadingView.center = viewController.view.center |
| loadingView.backgroundColor = #colorLiteral(red: 0, green: 0, blue: 0, alpha: 1) |
| loadingView.alpha = 0.5 |
| loadingView.clipsToBounds = true |
| loadingView.layer.cornerRadius = 15 |
| |
| viewController.view.addSubview(loadingView) |
| viewController.view.addSubview(activityIndicator) |
| |
| activityIndicator.startAnimating() |
| } |
6. Nous supprimons les éléments inutilisés
Ne laissez pas de commentaires inutiles (par défaut), de méthodes vides ou de fonctionnalités mortes - cela obstrue le code. Faites attention à la classe AppDelegate, vous y trouverez probablement des méthodes vides avec des commentaires à l'intérieur.
| ❌ NOT Preferred |
| |
| @UIApplicationMain |
| class AppDelegate: UIResponder, UIApplicationDelegate { |
| |
| var window: UIWindow? |
| |
| |
| func application( |
| _ application: UIApplication, |
| didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { |
| // Override point for customization after application launch. |
| return true |
| } |
| // |
| // func someFunc() { |
| // print("Some") |
| // } |
| |
| func applicationWillResignActive(_ application: UIApplication) { |
| // Sent when the application is about to move from active to inactive state. This can occur for certain |
| //types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits |
| //the application and it begins the transition to the background state. |
| // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. |
| } |
| |
| |
| ✅ Preferred |
| |
| @UIApplicationMain |
| class AppDelegate: UIResponder, UIApplicationDelegate { |
| |
| var window: UIWindow? |
| |
| |
| func application( |
| _ application: UIApplication, |
| didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { |
| |
| return true |
| } |
| |
| } |
7. Automatisez la routine
Pour éviter d'écrire manuellement dans chaque classe
// MARK: - SomeMark , utilisez l'
extrait de code .
Nous écrivons une étiquette, la sélectionnons, puis l'éditeur -> Créer un extrait de code, lui donnons un nom et l'appelons raccourci.// MARQUE: - Bonus
- Marquez la classe avec le mot clé final si cette classe n'a pas d'enfants - le projet se compile plus rapidement et le code s'exécute plus rapidement.
- Marquez les propriétés, les points de vente et les méthodes avec le mot-clé privé - ils ne seront disponibles qu'à l'intérieur de la classe et ne seront pas dans la liste publique des propriétés et des méthodes s'ils n'y sont pas nécessaires.
Je vous souhaite plein succès dans le développement d'applications et laissez votre classe devenir plus propre!
// MARQUE: - Aide à la rédaction d'un articleSergey Pchelyakov
Alexey Pleshkov
AlekseyPleshkov// MARQUE: - LiensStyle de code de Ray Wenderlich