Comprendre le code propre sur Android

En guise d'introduction, je voudrais rappeler la citation de l' oncle Bob
Vous lisez cet article pour deux raisons. Le premier - vous êtes programmeur, le second - vous voulez être le meilleur programmeur.
Imaginez que vous êtes dans une bibliothèque et que vous cherchez des livres. Si la bibliothèque est triée, a des catégories de livres, vous trouverez rapidement celui dont vous avez besoin. De plus, la décoration intérieure et l'architecture fraîches rendront votre séjour dans cette bibliothèque très confortable pour vous.

Comme pour l'écriture de livres, si vous voulez créer quelque chose de génial, vous devez savoir comment écrire et organiser votre code. Si vous avez des membres de l'équipe ou quelqu'un d'autre qui a votre code (obsolète), ils ont juste besoin de voir les noms de variables ou les packages ou les classes, et ils comprendront immédiatement. Ils n'ont pas besoin de dire «E ** l» dans ce code et de le recommencer à zéro.

Qu'est-ce qu'un code propre?




La traduction
Cher programmeur:

Quand j'ai écrit ce code, seul Dieu et moi savions comment cela fonctionne!
Maintenant, seul Dieu le sait!

Avant d'essayer d'optimiser cette routine et cela ne mènera pas au succès (le plus probable), veuillez augmenter le compteur de temps pour avertir le prochain développeur

Nombre total d'heures passées: 567

Comme vous pouvez le voir, il ne suffit pas de terminer le développement plus rapidement. Si à l'avenir ils ne peuvent pas comprendre ce code, il deviendra également une dette technique.

Votre code est dans un état «pur» si chaque membre de votre équipe peut le comprendre. Le code propre peut être lu et amélioré par tout développeur autre que l'auteur d'origine. La compréhension s'accompagne de lisibilité, de mutabilité, d'extensibilité et de maintenabilité.

Dois-je m'en occuper?


La raison pour laquelle vous devez vous soucier de la propreté de votre code est que vous décrivez vos pensées à d'autres développeurs. C'est pourquoi vous devez vous assurer que votre code est plus élégant, simple et lisible.

Signes de code propre


  • Votre code doit être élégant: votre code doit vous faire sourire comme une boîte à musique bien faite ou une voiture bien conçue
  • Votre code doit être concentré: chaque fonction, chaque classe, chaque module implémente une tâche, qui ne reste pas du tout distraite et non polluée par les détails environnants.
  • Le code ne contient pas de doublons
  • Réussir tous les tests avec succès
  • Réduction du nombre d'entités, de méthodes, de classes, etc.

La différence entre un bon programmeur et un professionnel est qu'un programmeur professionnel comprend que la compréhensibilité du code est primordiale. Un professionnel utilise ce pouvoir pour écrire du code que tout le monde comprend - Robert C. Martin

Écrivez les noms conscients


Choisir un bon nom peut prendre beaucoup de temps, mais cela économisera plus tard. Le nom d'une variable, d'une fonction, d'une classe doit répondre à toutes les questions importantes. Il devrait vous expliquer pourquoi il existe, pourquoi il est nécessaire et comment l'utiliser. Si le nom nécessite un commentaire, le nom ne révèle pas son but

Exemple simple

// Bad variables naming var a = 0 // user ages var w = 0 // user weight var h = 0 // user height // Bad functions naming fun age() fun weight() fun height() // Bad classes naming to get user data class UserInfo() // Best practices varibales naming var userAge = 0 var userWeight = 0 var userHeight = 0 // Best practices functions naming fun setUserAge() fun setUserWeight() fun setUserHeight() // Best practices classes naming to get user data class Users() 

Noms de classe


Les classes et les objets doivent être des noms, tels que Custom, WikiPage, Account et AddressParser. Évitez les mots comme Manager, Processor, Data ou Info. N'oubliez pas non plus que le nom de la classe ne doit pas être un verbe.

Noms des méthodes


Les noms de méthode doivent être des verbes, par exemple postPayment, deletePage ou save. Les modificateurs d'accès, les prédicats doivent être nommés par leur valeur et avec le préfixe get, défini et selon la norme JavaBean.

Avant de continuer, faites une petite pause, faites le plein de café et de biscuits.



OK, passons maintenant aux principes SOLIDES.

Écrire du code adhérant aux principes SOLID


Ces principes ont été développés par Uncle Bob, SOLID est une abréviation qui décrit un ensemble de principes conçus pour écrire du bon code.

Principe (s) de responsabilité unique


Cela signifie que chaque classe ne devrait avoir qu'une seule responsabilité. Il ne devrait jamais y avoir plus d'une raison pour changer une classe. Pas besoin de tout ajouter à votre classe, simplement parce que vous pouvez le faire. Divisez les grandes classes en plus petites et évitez les classes divines.

Un exemple:

Nous avons un RecyclerView.Adapter avec une logique métier à l'intérieur de onBindViewHolder

 class MyAdapter(val friendList: List<FriendListData.Friend>) : RecyclerView.Adapter<CountryAdapter.MyViewHolder>() { inner class MyViewHolder(view: View) : RecyclerView.ViewHolder(view) { var name: TextView = view.findViewById(R.id.text1) var popText: TextView = view.findViewById(R.id.text2) } override fun onBindViewHolder(holder: MyViewHolder, position: Int) { val friend = friendList[position] val status = if(friend.maritalStatus == "Married") { "Sold out" } else { "Available" } holder.name.text = friend.name holder.popText.text = friend.email holder.status.text = status } override fun getItemCount(): Int { return friendList.size } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder { val view = LayoutInflater.from(parent.context).inflate(R.layout.item_friendlist, parent, false) return MyViewHolder(view) } } 

Cela rend RecyclerView.Adapter non seulement responsable car il contient la logique métier à l'intérieur de onBindViewHolder. Cette méthode est uniquement responsable de l'insertion de données dans la vue.

Le principe d'ouverture / fermeture (O)


Les entités logicielles doivent être ouvertes pour l'expansion, mais fermées pour la modification. Cela signifie que si vous développez la classe A et vos collègues voudront changer la fonction à l'intérieur de cette classe. Ils peuvent facilement le faire en développant cette classe sans changer la classe elle-même.
Un exemple simple est la classe RecyclerView.Adapter. Vous pouvez facilement le développer et créer votre propre adaptateur avec un comportement non standard sans modifier le RecyclerView.Adapter lui-même.

 class FriendListAdapter(val friendList: List<FriendListData.Friend>) : RecyclerView.Adapter<CountryAdapter.MyViewHolder>() { inner class MyViewHolder(view: View) : RecyclerView.ViewHolder(view) { var name: TextView = view.findViewById(R.id.text1) var popText: TextView = view.findViewById(R.id.text2) } override fun onBindViewHolder(holder: MyViewHolder, position: Int) { val friend = friendList[position] holder.name.text = friend.name holder.popText.text = friend.email } override fun getItemCount(): Int { return friendList.size } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder { val view = LayoutInflater.from(parent.context).inflate(R.layout.item_friendlist, parent, false) return MyViewHolder(view) } } 

Le principe de substitution de Barbara Lisk (L)


La classe enfant doit compléter le parent et ne pas la modifier. Cela signifie que la sous-classe doit remplacer les méthodes parentes qui ne violent pas les fonctionnalités de cette classe parente. Par exemple, nous créons une interface de classe qui a un écouteur onClick () et ensuite vous appliquez l'écouteur dans MyActivity et lui donnez une action Toast lorsque onClick () est appelé.

 interface ClickListener { fun onClick() } class MyActivity: AppCompatActivity(), ClickListener { //........ override fun onClick() { // Do the magic here toast("OK button clicked") } } 

Principe de séparation des interfaces


Ce principe stipule que le client ne doit pas dépendre de méthodes qu'il n'utilise pas.
Cela signifie que si vous voulez écrire la classe A et y ajouter la fonctionnalité d'une autre classe B. Il n'est pas nécessaire de redéfinir toutes les classes A à l'intérieur de la classe B.

Exemple: dans notre activité, nous devons implémenter SearchView.OnQueryTextListener (), mais nous n'avons besoin que de la méthode onQuerySubmit ().

 mSearchView.setOnQueryTextListener(object : SearchView.OnQueryTextListener{ override fun onQueryTextSubmit(query: String?): Boolean { // Only need this method return true } override fun onQueryTextChange(query: String?): Boolean { // We don't need to implement this method return false } }) 

Comment fait-on cela? C'est facile! Créez simplement un rappel et une classe étendant SearchView.OnQueryTextListener ()

 interface SearchViewQueryTextCallback { fun onQueryTextSubmit(query: String?) } class SearchViewQueryTextListener(val callback: SearchViewQueryTextCallback): SearchView.OnQueryTextListener { override fun onQueryTextSubmit(query: String?): Boolean { callback.onQueryTextSubmit(query) return true } override fun onQueryTextChange(query: String?): Boolean { return false } } 

Et donc nous l'ajoutons à notre vue

 val listener = SearchViewQueryTextListener( object : SearchViewQueryTextCallback { override fun onQueryTextSubmit(query: String?) { // Do the magic here } } ) mSearchView.setOnQueryTextListener(listener) 

Ou alors en utilisant la fonction d'extension dans Kotlin

 interface SearchViewQueryTextCallback { fun onQueryTextSubmit(query: String?) } fun SearchView.setupQueryTextSubmit (callback: SearchViewQueryTextCallback) { setOnQueryTextListener(object : SearchView.OnQueryTextListener{ override fun onQueryTextSubmit(query: String?): Boolean { callback.onQueryTextSubmit(query) return true } override fun onQueryTextChange(query: String?): Boolean { return false } }) } 

 val listener = object : SearchViewQueryTextCallback { override fun onQueryTextSubmit(query: String?) { // Do the magic here } } mSearchView.setupQueryTextSubmit(listener) 

Principe d'inversion de dépendance


Dépendance à l'égard des abstractions, sans dépendance à quelque chose de spécifique.
La définition de l'inversion de la dépendance à l'égard de l'oncle Bob se compose de deux concepts.

Les modules de niveau supérieur ne doivent pas dépendre de modules de niveau inférieur. Les deux doivent être liés à des abstractions. Les abstractions ne devraient pas dépendre des détails. Les détails doivent dépendre des abstractions. Les modules de haut niveau qui implémentent une logique complexe devraient être facilement réutilisables sans modification des modules de niveau inférieur. Pour ce faire, vous devez saisir une abstraction qui sépare les modules des niveaux supérieur et inférieur les uns des autres.

Un exemple simple de ce modèle MVP. Vous disposez d'un objet d'interface qui vous aide à communiquer avec des classes spécifiques. Ce que cela signifie - les classes d'interface utilisateur (activité / fragment) n'ont pas besoin de connaître la mise en œuvre réelle des méthodes du présentateur. Ainsi, si vous apportez des modifications à l'intérieur du présentateur, les classes d'interface utilisateur n'ont pas à se soucier de ces modifications.

Jetons un coup d'oeil à un exemple

 interface UserActionListener { fun getUserData() } class UserPresenter : UserActionListener() { // ..... override fun getUserData() { val userLoginData = gson.fromJson(session.getUserLogin(), DataLogin::class.java) } // ..... } 

Et maintenant sur l'activité

 class UserActivity : AppCompatActivity() { //..... val presenter = UserPresenter() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // Activity doesn't need to know how presenter works // for fetching data, it just know how to call the functions // So, if you add method inside presenter, it won't break the UI. // even the UI doesn't call the method. presenter.getUserData() } //.... } 

De cette façon, nous créons une interface qui résume l'implémentation du présentateur, et notre classe view conserve une référence à PresenterInterface.

Source: https://habr.com/ru/post/fr443662/


All Articles