De la part d'un traducteur: Severin Peres a publié pour vous un article sur l'utilisation des principes SOLID dans la programmation. Les informations de l'article seront utiles pour les débutants et les programmeurs expérimentés.Si vous êtes développeur, vous avez probablement entendu parler des principes SOLID. Ils permettent au programmeur d'écrire du code propre, bien structuré et facile à entretenir. Il convient de noter que, dans la programmation, il existe plusieurs approches pour effectuer correctement tel ou tel travail. Différents spécialistes ont des idées et une compréhension différentes de la «bonne façon», tout dépend de l’expérience de chacun. Néanmoins, les idées proclamées dans SOLID sont acceptées par presque tous les représentants de la communauté informatique. Ils sont devenus le point de départ de l'émergence et du développement de nombreuses bonnes pratiques de gestion du développement.
Voyons quels sont les principes SOLIDES et comment ils nous aident.
Skillbox recommande: Cours pratique "Mobile Developer PRO" .
Nous vous rappelons: pour tous les lecteurs de «Habr» - une remise de 10 000 roubles lors de l'inscription à un cours Skillbox en utilisant le code promo «Habr».
Qu'est-ce que SOLID?
Ce terme est une abréviation, chaque lettre du terme est le début du nom d'un certain principe:
Principe de responsabilité exclusive
Le principe de responsabilité unique (SRP) stipule que chaque classe ou module d'un programme ne doit être responsable que d'une partie des fonctionnalités de ce programme. De plus, les éléments de cette responsabilité devraient être attribués à leur classe et non répartis entre les classes non apparentées. Le développeur et évangéliste principal de SRP, Robert S. Martin, décrit la responsabilité comme la cause du changement. Initialement, il a proposé ce terme comme l'un des éléments de son travail, «Principes de conception orientée objet». Le concept comprenait une grande partie du modèle de connectivité précédemment défini par Tom Demarco.
Le concept comprenait également plusieurs concepts formulés par David Parnassus. Les deux principaux sont l'encapsulation et la dissimulation d'informations. Parnassus a soutenu que la division d'un système en modules séparés ne devrait pas être basée sur une analyse des organigrammes ou des flux d'exécution. Chacun des modules doit contenir une solution spécifique qui fournit un minimum d'informations aux clients.
Soit dit en passant, Martin a donné un exemple intéressant avec les cadres supérieurs de l'entreprise (COO, CTO, CFO), chacun utilisant des logiciels spécifiques pour les entreprises avec un objectif différent. En conséquence, n'importe lequel d'entre eux peut mettre en œuvre des modifications dans le logiciel sans affecter les intérêts des autres gestionnaires.
Objet divin
Comme d'habitude, la meilleure façon d'apprendre la SRP est de tout voir en action. Regardons une section d'un programme qui ne respecte PAS le principe de la responsabilité partagée. Il s'agit du code Ruby qui décrit le comportement et les attributs d'une station spatiale.
Consultez l'exemple et essayez de déterminer les éléments suivants:
Les responsabilités des objets déclarés dans la classe SpaceStation.
Ceux qui peuvent être intéressés par le travail de la station spatiale.
class SpaceStation def initialize @supplies = {} @fuel = 0 end def run_sensors puts "----- Sensor Action -----" puts "Running sensors!" end def load_supplies(type, quantity) puts "----- Supply Action -----" puts "Loading
En fait, notre station spatiale n'est pas fonctionnelle (je pense que je ne recevrai pas d'appel de la NASA dans un avenir proche), mais il y a quelque chose à analyser.
Ainsi, la classe SpaceStation a plusieurs responsabilités (ou tâches) différentes. Tous peuvent être divisés en types:
- Capteurs
- approvisionnement (consommables);
- carburant;
- accélérateurs.
Malgré le fait qu'aucun des employés de la station ne soit défini dans la classe, nous pouvons facilement imaginer qui est responsable de quoi. Très probablement, le scientifique contrôle les capteurs, le logisticien est responsable de l'approvisionnement en ressources, l'ingénieur est responsable de l'approvisionnement en carburant et le pilote contrôle les accélérateurs.
Peut-on dire que ce programme n'est pas conforme à SRP? Oui bien sûr. Mais la classe SpaceStation est un «objet divin» typique qui sait tout et fait tout. C'est l'anti-modèle principal de la programmation orientée objet. Pour un débutant, de tels objets sont extrêmement difficiles à entretenir. Jusqu'à présent, le programme est très simple, oui, mais imaginez ce qui se passera si nous ajoutons de nouvelles fonctionnalités. Peut-être que notre station spatiale aura besoin d'un centre médical ou d'une salle de réunion. Et plus il y a de fonctionnalités, plus SpaceStation grandira. Eh bien, puisque cet objet sera connecté à d'autres, l'entretien de l'ensemble du complexe deviendra encore plus compliqué. En conséquence, nous pouvons perturber le travail, par exemple, des accélérateurs. Si un chercheur demande des changements dans le travail avec les capteurs, cela pourrait bien affecter les systèmes de communication de la station.
La violation du principe SRP peut donner une victoire tactique à court terme, mais en fin de compte, nous «perdrons la guerre», il sera très difficile de servir un tel monstre à l'avenir. Il est préférable de diviser le programme en sections de code distinctes, chacune étant responsable de l'exécution d'une opération spécifique. Dans cet esprit, changeons la classe SpaceStation.
Partager la responsabilitéCi-dessus, nous avons identifié quatre types d'opérations contrôlées par la classe SpaceStation. Lors de la refactorisation, nous les garderons à l'esprit. Le code mis à jour correspond mieux à SRP.
class SpaceStation attr_reader :sensors, :supply_hold, :fuel_tank, :thrusters def initialize @supply_hold = SupplyHold.new @sensors = Sensors.new @fuel_tank = FuelTank.new @thrusters = Thrusters.new(@fuel_tank) end end class Sensors def run_sensors puts "----- Sensor Action -----" puts "Running sensors!" end end class SupplyHold attr_accessor :supplies def initialize @supplies = {} end def load_supplies(type, quantity) puts "----- Supply Action -----" puts "Loading
Il y a beaucoup de changements, le programme est maintenant nettement meilleur. Maintenant, notre classe SpaceStation est devenue plutôt un conteneur dans lequel les opérations pour les pièces dépendantes sont lancées, y compris un ensemble de capteurs, un système d'alimentation pour les consommables, un réservoir de carburant et des boosters.
Pour chacune des variables, il existe maintenant une classe correspondante: Capteurs; SupplyHold; FuelTank Propulseurs.
Il existe plusieurs modifications importantes à cette version du code. Le fait est que les fonctions individuelles ne sont pas seulement encapsulées dans leurs propres classes, elles sont organisées de manière à devenir prévisibles et cohérentes. Nous regroupons des éléments de fonctionnalités similaires pour suivre le principe de connectivité. Maintenant, si nous devons changer le principe du système en passant d'une structure de hachage à un tableau, utilisez simplement la classe SupplyHold, nous n'aurons pas à toucher à d'autres modules. Ainsi, si l'officier en charge de la logistique change quelque chose dans sa section, les éléments restants du poste resteront intacts. Dans le même temps, la classe SpaceStation ne sera même pas au courant des changements.
Nos officiers de la station spatiale seront probablement satisfaits des changements, car ils peuvent demander ceux dont ils ont besoin. Notez que le code a des méthodes telles que report_supplies et report_fuel contenues dans les classes SupplyHold et FuelTank. Que se passe-t-il si la Terre demande de changer la façon dont les rapports sont générés? Vous devrez changer les deux classes, SupplyHold et FuelTank. Mais que se passe-t-il si vous devez changer la façon dont vous livrez le carburant et les consommables? Vous devrez peut-être modifier à nouveau toutes les mêmes classes. Et c'est une violation du principe SRP. Corrigeons-le.
class SpaceStation attr_reader :sensors, :supply_hold, :supply_reporter, :fuel_tank, :fuel_reporter, :thrusters def initialize @sensors = Sensors.new @supply_hold = SupplyHold.new @supply_reporter = SupplyReporter.new(@supply_hold) @fuel_tank = FuelTank.new @fuel_reporter = FuelReporter.new(@fuel_tank) @thrusters = Thrusters.new(@fuel_tank) end end class Sensors def run_sensors puts "----- Sensor Action -----" puts "Running sensors!" end end class SupplyHold attr_accessor :supplies attr_reader :reporter def initialize @supplies = {} end def get_supplies @supplies end def load_supplies(type, quantity) puts "----- Supply Action -----" puts "Loading
Dans cette dernière version du programme, les responsabilités ont été réparties en deux nouvelles classes, FuelReporter et SupplyReporter. Ils sont tous deux enfants de la classe Reporter. De plus, nous avons ajouté des variables d'instance à la classe SpaceStation afin d'initialiser la sous-classe nécessaire si nécessaire. Maintenant, si la Terre décide de changer autre chose, alors nous apporterons des changements aux sous-classes, et non à la classe principale.
Bien sûr, certaines classes dépendent toujours les unes des autres. Ainsi, l'objet SupplyReporter dépend du SupplyHold et le FuelReporter dépend du FuelTank. Bien entendu, les boosters doivent être connectés au réservoir de carburant. Mais ici, tout semble logique, et apporter des modifications ne sera pas particulièrement difficile - la modification du code d'un objet n'affectera pas trop l'autre.
Ainsi, nous avons créé un code modulaire où les responsabilités de chacun des objets / classes sont définies avec précision. Travailler avec un tel code n'est pas un problème, sa maintenance sera une tâche simple. L'ensemble de "l'objet divin" que nous avons converti en SRP.
Skillbox recommande: