Affichage des articles dont le libellé est SOLID. Afficher tous les articles
Affichage des articles dont le libellé est SOLID. Afficher tous les articles

mercredi 18 décembre 2013

Formation Qualité Logicielle

Voici un support de formation Qualité Logicielle qui aborde de nombreux points déjà abordés dans ce blog.

Dilbert by Scott Adams

Dans l'ordre, j'ai choisi de présenter :
  1. Les principales mécaniques de Refactoring, sauvagement appelé `Réusinage de code` en français
  2. Les fameux principes SOLID
  3. Un peu de programmation fonctionnelle avec une courte démonstration de LINQ (un cours entier consacré à LINQ suivra)
  4. L'analyse statique de code qui devient très à la mode, notamment grâce au changement de job d'Eric Lippert, l'un des top users de StackOverflow, ancien programmeur sur le compilateur C#, qui travail maintenant pour Coverity, éditeur d'un logiciel d'analyse statique
  5. Pour finir, de la programmation par contrat qui en est à ces débuts (façon de parler, c'est juste que ce n'est pas natif C# encore, le principe existe depuis 1985)
Cette présentation est à destination des programmeurs de tout niveaux, que ce soit pour renforcer des connaissances ou pour apprendre à mieux développer.
Pour certains points, il est nécessaire d'avoir des bases en programmation orienté objet tout de même.
La présentation est parsemée d'images et de citations qui je l'espère aide à garder la lecture agréable et pas trop universitaire.




mercredi 17 juillet 2013

Inversion des dépendances (SOLID 5/5)

Ce post fait partie de la série S.O.L.I.D.

Cette série s'achève sur le principe d'inversion des dépendances.

Inversion des dépendances... Déjà inversion, on ne l'utilise pas souvent, dépendance pas tellement plus, mais les deux ensemble, c'est un comble.

Est-ce que tu souderais directement ta lampe à la prise électrique du mur ?
Ce principe dit que les hauts modules ne doivent pas dépendre des bas modules, que les deux doivent dépendre des abstractions. Et en plus, les abstractions ne doivent pas dépendre des détails, mais que les détails doivent dépendre des abstractions. Pfiou...

En fait, ça dit d'utiliser des interfaces et des classes abstraites. Le plus possible. Tout en respectant les 4 premiers principes. Cela clôt un peu le puzzle SOLID. Chaque principe s'imbriquant les uns dans les autres.

Imaginons que vous voulez commander une bière. Comme d'habitude, vous allez dans votre bar favori et vous commandez la dite bière. Puis vous avez un algorithme pour la boire. Le plus immédiat serait d'écrire :

public void DrinkBeer(FavoriteBar bar)
{
    Beer beer = bar.OrderBeer();
    this.RaiseElbow();
    this.Drink(beer);
}

Ok, super, vous avez votre méthode pour boire une bière uniquement dans votre bar favori. Donc si demain vous souhaitez tester un nouveau bar, c'est mort. Il vas falloir modifier votre algorithme, et ça c'est mal (principe d'ouvert fermé). Il faut dépendre des abstractions ! Au lieu de prendre en paramètre une classe spécifique, prenons plutôt une abstraction : une interface !

Abstrayons notre bar :

interface IBar
{
    Beer OrderBeer();
}
Champion de France du lever de coude

class FavoriteBar : IBar

Maintenant, on peut commander et boire de n'importe quel bar :

public void DrinkBeer(IBar bar)
{
    Beer beer = bar.OrderBeer();
    this.RaiseElbow();
    this.Drink(beer);
}

Bon, notre interface IBar qui contient juste de quoi boire une bière manque de généricité. Si demain on veut boire un pastis, comment on fait ? Générisons ! Et au passage, on ségrégationne un peu mieux notre interface pour la rendre moins global. Pour cela il suffit de la nommer correctement. Un bar fait plein de choses, mais ici, seule la caractéristique 'commande de boisson' nous intéresse :

interface IOrderableBeverage
{
    Beverage Order(BeverageType type);
}
class FavoriteBar : IOrderableBeverage

public void DrinkBeer(IBar bar)
{
    this.Drink(bar, BeverageType.Beer);
}
public void Drink(IBar bar, BeverageType type)
{
    Beverage beverage = bar.Order(type);
    this.RaiseElbow();
    this.Drink(beverage);
}

C'est beau, ça respecte plein de principe SOLID, on est fier, on est devenu un meilleur programmeur, félicitons-nous ! A votre santé !



lundi 15 juillet 2013

Ségrégation des interfaces (SOLID 4/5)

Ce post fait partie de la série S.O.L.I.D.

L'avant dernier principe SOLID est la ségrégation des interfaces. Si le terme séparation est connu, le terme ségrégation l'est un peu moins.

Wiktionnaire nous dit :
Action par laquelle on met quelqu’un ou quelque chose à part, on le sépare d’un tout, d’une masse.
C'est très précis, il faut mettre à part les interfaces.

Tu veux que je branche ça. Où ?
L'idée est que le client qui va utiliser vos interfaces ne se retrouvent pas avec des interfaces à 150 méthodes, parce que la classe sous-jacente contient 150 méthodes. Chaque interface doit avoir un but unique, et l'on retrouve le premier principe de responsabilité unique.

Prenons une pizzeria. Une pizzeria fait des pizzas, les livre, encaisse votre argent, paye ses impôts, paye ses employés, subit un contrôle sanitaire de temps en temps.

Imaginons que vous voulez commander une pizza à une pizzeria qui hérite de l'interface IPizzeria.

Supposons l'interface suivante :

interface IPizzeria
{
    Pizza Order(PizzaType type);
    void Deliver(Pizza pizza, Location address);
    void Pay(decimal dollar);
    void PayTaxes(decimal dollar);
    void PayEmployees(List<Employee> employees);
    void HealthControl();
}


Vous, client, vous souhaitez appeler la pizzeria pour commander la pizza choisi par l'utilisateur :

public Pizza OrderPizza(IPizzeria pizzeria)
{
    PizzaType type = (PizzaType)comboBoxPizzaType.SelectedItem;
    return pizzeria.Order(type);
}

Là tout se passe bien. Mais comme l'interface n'a pas été ségrégationnée, un risque aurait été d'appeler le contrôle sanitaire.

public Pizza OrderPizza(IPizzeria pizzeria)
{
    PizzaType type = (PizzaType)comboBoxPizzaType.SelectedItem;
    pizzeria.HealthControl();
}

Vous imaginez le bazar si à chaque commande client les inspecteurs se pointent ?
Outre le faite de se tromper de méthode, le principe de ségrégation permet un découpage net et précis du code. Certes, votre objet fait plein de choses. Ce n'est pas la peine que le monde entier soit au courant.

Vous client, vous ne devez voir que ce qui vous intéresse de la pizzeria :

interface IOrderablePizza
{
    Pizza Order(PizzaType type);
    void Deliver(Pizza pizza, Location address);
    void Pay(decimal dollar);
}

D'un autre côté, les inspecteurs sanitaires se foutent royalement que vous livrez des pizzas. Ce qui les intéresse, c'est la caractéristique entreprise de la pizzeria :

interface IBusiness
{
    void PayTaxes(decimal dollar);
    void PayEmployees(List<Employee> employees);
    void HealthControl();
}

Notre pizzeria finit par hériter de ces deux caractéristiques : c'est une entreprise (paye des impôts, des employés, subit des contrôles), qui fait des pizzas (cuisine, livraison).

class DaEnzoPizza : IOrderablePizza, IBusiness
{
}

vendredi 12 juillet 2013

Une femme à découvrir, Barbara Liskov (SOLID 3/5)

Ce post fait partie de la série S.O.L.I.D.

Nous arrivons au numéro 3 des 5 principes SOLID, le bien nommé principe de substitution de Liskov !

Barbara Liskov est une informaticienne ! Oui ça existe. Son principe est le suivant :

Si q(x) est une propriété démontrable pour tout objet x de type T, alors q(y) est vraie pour tout objet y de type S tel que S est un sous-type de T.

A copier 100 fois. Donc vous aurez compris que c'est le plus violent des cinq principes. Mais c'est pas si compliqué. En résumé, ça dit que si ça marche pour MaClass, ça marche aussi pour MaClassDerivé.

Si ça ressemble à un canard, caquette comme un canard, mais a besoin de piles, tu as probablement la mauvaise abstraction

Ça ne s'applique déjà que dans le cas ou 2 classes existent, et l'une hérite de l'autre.

Imaginons une classe Canard, et créons une sous classe CanardEnPlastique.
Si on décide que Canard peut voler, alors le code suivant est valide :

Canard couin = new Canard();
couin.EnvoleToi();

Par contre le programme suivant lance une Exception CanardEnPlastiqueNeVolePasException :

Canard couin = new CanardEnPlastique();
couin.EnvoleToi();

On ne peut pas substituer un canard en plastique à un vrai canard, c'est trop différent. On ne respecte pas le principe de substitution de Liskov. Pourtant tu l'as déjà fait dans ton code, on l'a tous fait. Et c'est mal. La prochaine fois que tu hérites d'une classe, penses à couin couin. Ne lance pas d'exception quand tu surcharges un comportement. Tout ce que fait la super classe, ta sous classe doit être au pire capable d'autant, c'est le minimum. L'héritage n'est pas fait pour limiter les capacités des classes mères, mais au contraire d'augmenter leurs capacités.

On peut voir ça comme des contraintes. Ce qui rentre dans la sous classe doit être au pire moins contraignant que ce qui peut rentrer dans la super classe. La sous classe doit être moins chiante. Si tu peux rentrer des pommes dans la super classe, la sous classe pourra elle prendre tout les fruits.

Inversement, tout ce qui sort doit être plus contraignant. Si la super classe sort des Girafes, la sous classe pourra sortir des bébés girafes par exemple. Par contre elle pourra pas sortir des éléphants. Si vous aimez le formalisme, l'explication de Wikipédia est beaucoup moins drôle:
  • Les préconditions ne peuvent pas être renforcées dans une sous-classe. Cela signifie que vous ne pouvez pas avoir une sous-classe avec des préconditions plus fortes que celles de sa superclasse.
  • Les postconditions ne peuvent pas être affaiblies dans une sous-classe. Cela signifie que vous ne pouvez pas avoir une sous-classe avec des postconditions plus faibles que celles de sa superclasse.
Gardons en tête qu'une sous-classe doit ajouter des fonctionnalités à la super-classe, et en aucun cas en limiter le comportement.

Sous-classer le canard en lui ajoutant la caractéristique plastique, c'est lui enlever la possibilité de voler. C'est pas SOLID !

mercredi 10 juillet 2013

L’ovoïde à face centré (SOLID 2/5)

Ce post fait partie de la série S.O.L.I.D.

Dans la série des principes SOLID, voici le numéro 2 : le principe d'ouvert/fermé.
Le nom est très mal choisi, mais c'est ancré dans le marbre, il va falloir faire avec.
La définition nous dit :

Ouvert à l'extension, fermé à la modification

La chirurgie à poumons ouverts n'est pas nécessaire quand on veut mettre un manteau

Quand tu crées une classe, tu dois être capable d'étendre son fonctionnement sans avoir à constamment la modifier. C'est pour ça que l'on parle d'extension. Le plus évident des exemples est le bon vieux fichier de paramétrage. Tu crées ta classe pour qu'elle soit paramétrable.

Exemple : la classe qui génère le rapport Excel doit pouvoir prendre en paramètre des couleurs qui pourront être choisies quand tu créeras une instance de classe. Il ne faut pas qu'à chaque fois que le client souhaite rajouter un bleu un peu moins bleu, il faille aller taper dans le code.

Donc on générise. Il faut que ta classe soit générique, tout le temps, c'est la base.
On n'écrit pas :

NuclearBomb nuclearBomb = new NuclearBomb();
nuclearBomb.LaunchOnBagdad();

Il faut génériser !

Town bagdad = new Town();
NuclearBomb nuclearBomb = new NuclearBomb();
nuclearBomb.Launch(bagdad);

Sinon si demain, on veux rayer de la carte Le Caire, il vas falloir ouvrir le code de le classe NuclearBomb pour ajouter la méthode LaunchOnLeCaire. Et ça ce n'est pas acceptable si l'on veut appliquer les principes SOLID. Surtout que toucher au code d'une bombe nucléaire, c'est très, très dangereux. La moindre erreur, le fil rouge au lieu du fil bleu, et tout explose !
La méthode Launch devient donc générique, elle est applicable à n'importe quel ville, alors que la méthode LaunchOnBagdad n'était pas générique, elle ne s'appliquait qu'a une seule ville. Plus besoin de toucher au code de la bombe, ouf ! L'Oncle Sam va pouvoir dormir sur ces deux oreilles.

C'est pour ça que l'on parle d'ouverture => On veut pouvoir étendre les fonctionnements de ce qui existe, par exemple en créant des sous-classes, ou en héritant d'interface.
Et on parle de fermeture => On veut pouvoir étendre les fonctionnements de ce qui existe, sans toucher au code déjà écrit.


mardi 9 juillet 2013

De la solidité que diable ! (SOLID 1/5)

Ce post fait partie de la série S.O.L.I.D.

Aujourd'hui un poste purement informatique, qui te fera passer Kant pour de la lecture de 6e.

Ces dernières années, le développement informatique est devenu plus abouti, plus mature, plus solide, plus SOLID. SOLID, comme le mot convient bien, autant en français qu'en anglais, ça tombe bien. Donc oui, fini les constructions logiciels qui s’effondrent dés qu'on pose un pied dessus. Un ensemble de règles a vu le jour pour faire des constructions logiciels fiables et robustes. Ça a l'air compliqué au départ, mais c'est pas si sorcier, même toi tu vas y arriver.

SOLID est un acronyme de cinq lettres, chacune définissant un principe. Attention c'est hard :
SOLID, le développement logiciel n'est pas une jeu de Jenga

Donnez moi un S (comme single), le principe de responsabilité unique.

Le principe veut que chaque classe, chaque objet, ne serve qu'à un unique but. Fini les classes qui font des centaines et des centaines de lignes. Quand tu crées ta classe, pose toi la question : à quoi va-t-elle servir. Tu dois avoir une et une seule notion en tête. Je veux modéliser un canard. Je crée la classe Canard. A quoi va-t-elle servir ? A modéliser Canard.

Je vais créer la classe ExcelIO. A quoi va-t-elle servir ? A extraire un fichier excel pour les rapports annuels, et à lire les fichiers d'imports des commerciaux. BIIIIIP, FAUX, ERREUR. Deux notions, deux classes. Demain le rapport annuel changera, pas le fichier d'import. La classe qui sert à lire les fichiers d'import va être modifiée sans raison valable, et c'est une erreur.

On arrive à la notion sœur : un objet doit avoir une et une seule raison de changer.

Ce n'est pas parce que tu peux le faire que tu dois le faire
Les raisons de ce principe, c'est d'éviter les effets de bords. Quand le client viendra t’engueuler parce que ses imports ne fonctionnent plu, alors que seule la fonction du rapport annuel devait changer, tu comprendras ta douleur.
Toucher du code, c'est dangereux, et invariablement, ça crée des bugs, donc autant toucher le moins de code possible lors d'un changement.

L'autre utilité de ce principe, c'est d'avoir des classes plus courtes, plus restreintes, avec moins de code. Donc plus compréhensible par tes collègues, et par le toi du futur.

Fini les classes couteaux suisses, qui démarrent le moteur, remplissent la piscine et font le café.
On les connait bien, ces classes qui accèdent à l'interface utilisateur, parsent les données d'entrée, interrogent la base de données, interprètent les résultats, sortent des rapport avec agrégations des résultats. NON, STOP. Ton fichier .php qui fait tout en même temps, c'est fini.

On parle aussi de `god-like object`, des objets dieux, omniscient qui connaissent tout sur tout. On les appelle souvent Manager. ExcelManager, FormManager, etc... C'est un `code-smell`, en gros, quand tu vois une classe qui s'appelle machinChoseManager, ou quand tu t’apprêtes à la créer, STOP, ça pue. Lève les mains de ton clavier, et réfléchis.

On découpe on découpe on découpe. Chacun ses responsabilités, tu verras, tu me remercieras dans 6 mois.