Arrêtez de développer n'importe comment ! - Principe Ouvert / Fermé (2/5)

En programmation informatique, SOLID est un acronyme représentant cinq
principes de base pour la programmation orientée objet. Ces cinq principes sont censés apporter une ligne directrice permettant le développement de logiciels plus fiables, plus robustes, plus maintenables, plus extensibles et plus testables.

Aujourd'hui nous nous intéressons au «O» de SOLID :

  • Open Close Principle ou « principe Ouvert / Fermé » qui stipule qu'une classe doit être ouverte à l'extension et fermée aux modifications. En d'autres termes, il doit être possible de facilement enrichir les capacités d'un objet sans que cela implique de modifier le code source de sa classe. Des patrons de conception comme la Stratégie ou le Décorateur répondent à ce principe en favorisant la composition d'objets plutôt que l'héritage.

Exercice

Tous les exercices sont faisables entièrement en ligne depuis site de Kotlin :

Kotlin Playground: Edit, Run, Share Kotlin Code Online

🏠 Pour illustrer ce principe, nous allons prendre l'exemple d'une maison connectée. Nous allons devoir créer un élément essentiel : un interrupteur.

  • Prenons donc notre interrupteur qu'on nommera Button :
class Button() {
    fun click() {
       
    }
}

📝 Complétez le code pour qu'aux cliques du bouton on puisse afficher un texte :

println("Clic")

Pensez que vous allez aussi devoir simuler le clique de l'utilisateur.

👉 Question 1

  • Imaginons que nous voulons maintenant un second bouton qui fasse « clac ». Comment faire ?
  • Quelle remarque pouvez-vous faire si nous voulons créer un 3ᵉ bouton ?
  • Finalement, nous voulons que notre bouton «clic» fasse «clouc» que devez-vous faire ? Faites le parallèle avec le principe Ouvert / Fermé

Continuons donc avec notre histoire de bouton, nous allons, pour répondre à ce fameux principe Ouvert / Fermé, créer une interface :

interface OnClickListener {
    fun onClick()
}

💡 À noter que nous avons créé une interface fonctionnelle, cela nous servira pour simplifier la syntaxe lors de son appel. Nous verrons plus en détails ce type d'interface sur le cours dédié au principe de ségrégation des interfaces.

📝 Créez une classe OnButtonClicked qui implémente l'interface OnClickListener faites en sorte que la fonction onClick affiche :

class OnButtonClicked(): OnClickListener {
	[...] fun [...] {
    	println("Clouc")
    }
}

👉 Question 2

  • Comment faire, pour que la fonction click de la classe Button utilise (ait en paramètre) la classe OnButtonClicked ?
class Button() {
    fun click(listener: [...]) {
       [...].onClick()
    }
}
  • Je voudrais en utilisant cette méthode pouvoir avoir deux boutons : un qui fait «clac» et l'autre «clic», trouvez une solution qui permettrait de ne pas avoir à refaire plusieurs classes Button.

💡 Indice :

fun setOnClickListener(listener : OnClickListener) {
    this.onClicklistener = listener
}

📝  Transformez l'interface OnClickListener en interface fonctionnelle

👉 Question 3

  • En utilisant les interfaces fonctionnelles trouvez un moyen de supprimer les classes comme celle de OnButtonClicked.

💡 Indice le résultat final doit être de la forme :

val button = Button()
button.setOnClickListener({
    println("clac")
})
button.click()

👉 Question 4

  • Reprenez les questions n°1 en appliquant ce que vous venez de faire.
  • En quoi les interfaces fonctionnelles servent-elles le principe Ouvert / Fermé ?