Maison >développement back-end >tutoriel php >SOLID : Partie 4 - Principe d'inversion de dépendance

SOLID : Partie 4 - Principe d'inversion de dépendance

PHPz
PHPzoriginal
2023-09-01 19:49:061108parcourir

Responsabilité unique (SRP), Ouvert/Fermé (OCP), Substitution Liskov, Isolation d'interface et Inversion de dépendance. Cinq principes agiles qui devraient vous guider à chaque fois que vous écrivez du code.

Il serait injuste de vous dire qu’un principe SOLIDE est plus important qu’un autre. Cependant, aucun n’a peut-être un impact aussi direct et profond sur votre code que le principe d’inversion de dépendance, ou DIP en abrégé. Si vous trouvez d'autres principes difficiles à comprendre ou à appliquer, commencez par celui-ci, puis appliquez les principes restants au code qui suit déjà le DIP.

Définition

A. Les modules de haut niveau ne doivent pas dépendre des modules de bas niveau. Les deux devraient s’appuyer sur des abstractions.

B. Les abstractions ne devraient pas dépendre des détails. Les détails doivent dépendre de l'abstraction.

Ce principe a été défini par Robert C. Martin dans son livre « Agile Software Development, Principles, Patterns, and Practices » et ensuite republié en version C# dans le livre « Agile Principles, Patterns, and Practices in C# », qui est le dernier principe des cinq principes agiles SOLID.

DIP dans le monde réel

Avant de commencer à coder, je veux vous raconter une histoire. Chez Syneto, nous ne sommes pas toujours très prudents avec notre code. Il y a quelques années, nous en savions très peu et même si nous avons fait de notre mieux, tous les projets n'étaient pas aussi bons. Nous avons traversé l'enfer et en sommes revenus, et nous avons beaucoup appris par essais et erreurs.

Les principes SOLID et les principes de simplicité architecturale de l'oncle Bob (Robert C. Martin) ont changé la donne pour nous et ont changé notre façon de coder d'une manière difficile à décrire. En bref, je vais essayer d'illustrer certaines des décisions architecturales clés mises en œuvre par DIP qui ont eu un impact significatif sur notre projet.

La plupart des projets Web contiennent trois technologies principales : HTML, PHP et SQL. Peu importe la version spécifique de ces applications dont nous parlons ou le type d'implémentation SQL que vous utilisez. Le problème est que les informations du formulaire HTML doivent se retrouver d’une manière ou d’une autre dans la base de données. Le lien entre les deux peut être fourni par PHP.

Le plus important est que ces trois technologies représentent très bien trois couches architecturales différentes : l'interface utilisateur, la logique métier et la persistance. Nous discuterons plus tard de la signification de ces couches. Concentrons-nous maintenant sur quelques solutions étranges mais souvent rencontrées pour faire fonctionner ces technologies ensemble.

Plusieurs fois, j'ai vu des projets utiliser du code SQL dans des balises PHP à l'intérieur de fichiers HTML, ou du code PHP faisant écho à la page HTML et interprétant directement les variables globales $_GET$_POST. Mais pourquoi est-ce mauvais ?

SOLID: 第四部分 - 依赖倒置原则SOLID : Partie 4 - Principe dinversion de dépendanceSOLID: 第四部分 - 依赖倒置原则

L'image ci-dessus représente la version originale que nous avons décrite dans le paragraphe précédent. Les flèches représentent diverses dépendances et nous pouvons conclure que fondamentalement tout dépend de tout. Si nous devons apporter des modifications à une table de base de données, nous pouvons finir par éditer le fichier HTML. Ou, si nous modifions un champ dans le code HTML, nous pourrions finir par modifier les noms de colonnes dans l'instruction SQL. Ou si nous regardons le deuxième modèle, si le HTML change, nous devrons très probablement modifier le PHP, ou dans un très mauvais cas, lorsque nous générons tout le contenu HTML à partir du fichier PHP, nous devrons certainement changer le fichier PHP pour modifier le contenu HTML. Il ne fait donc aucun doute que les dépendances entre classes et modules sont tortueuses. Mais cela ne s'est pas arrêté là. Peut stocker des procédures ; du code PHP dans des tables SQL.

SOLID: 第四部分 - 依赖倒置原则SOLID : Partie 4 - Principe dinversion de dépendanceSOLID: 第四部分 - 依赖倒置原则

Dans le schéma ci-dessus, la requête sur la base de données SQL renvoie le code PHP généré à l'aide des données de la table. Ces fonctions ou classes PHP exécutent d'autres requêtes SQL qui renvoient du code PHP différent, et la boucle continue jusqu'à ce que toutes les informations soient récupérées et renvoyées... éventuellement vers l'interface utilisateur.

Je sais que cela peut paraître scandaleux pour beaucoup d’entre vous, mais si vous n’avez pas été impliqué dans un projet inventé et mis en œuvre de cette manière, vous le ferez certainement dans votre future carrière. La plupart des projets existants, quel que soit le langage de programmation, sont écrits sur la base d'anciens principes, par des programmeurs qui s'en moquent ou ne savent pas comment mieux faire les choses. Si vous lisez ces tutoriels, il y a de fortes chances que vous ayez déjà atteint un niveau supérieur. Vous êtes prêt, ou prêt, à respecter votre profession, à adopter votre métier et à le faire mieux.

L’alternative est de répéter les erreurs de ceux qui vous ont précédé et d’en subir les conséquences. Chez Syneto, lorsqu’un de nos projets a atteint un état presque impossible à maintenir en raison de son architecture ancienne et interdépendante et que nous avons dû l’abandonner définitivement, nous avons décidé de ne plus emprunter cette voie. Depuis, nous nous efforçons d'avoir une architecture propre qui respecte correctement les principes SOLID et surtout le principe d'inversion de dépendances.

SOLID: 第四部分 - 依赖倒置原则SOLID : Partie 4 - Principe dinversion de dépendanceSOLID: 第四部分 - 依赖倒置原则

La magie de cette architecture réside dans la façon dont les dépendances sont pointées :

  • L'interface utilisateur (dans la plupart des cas le framework Web MVC) ou tout autre mécanisme de livraison du projet dépendra de la logique métier. La logique métier est assez abstraite. L'interface utilisateur est très spécifique. L'interface utilisateur n'est qu'un détail d'un projet et elle change beaucoup. Rien ne devrait dépendre de l'interface utilisateur, rien ne devrait dépendre de votre framework MVC.
  • Une autre observation intéressante que nous pouvons faire est la persistance, la base de données, MySQL ou PostgreSQL, selon la logique métier. Votre logique métier est indépendante de la base de données. Cela permet d'échanger la persistance si vous le souhaitez. Si demain vous souhaitez remplacer MySQL par PostgreSQL ou simplement par des fichiers texte brut, vous pouvez le faire. Bien entendu, vous devez implémenter une couche de persistance spécifique pour la nouvelle méthode de persistance, mais vous n'avez pas besoin de modifier une seule ligne de code dans votre logique métier. Le sujet de la persistance est expliqué plus en détail dans le didacticiel Évolution vers une couche de persistance.
  • Enfin, sur le côté droit de la logique métier, en dehors de celle-ci, nous avons toutes les classes qui créent les classes de logique métier. Ce sont les usines et classes créées par le point d’entrée de notre application. Beaucoup de gens ont tendance à penser que ceux-ci appartiennent à la logique métier, mais lorsqu’ils créent des objets métier, leur seule raison est de le faire. Ce ne sont que des classes qui nous aident à créer d'autres classes. Les objets métier et la logique qu'ils fournissent sont indépendants de ces usines. Nous pouvons fournir une logique métier en utilisant différents modèles comme une usine simple, une usine abstraite, un constructeur ou une création d'objet simple. Cela n'a pas d'importance. Une fois les objets métier créés, ils peuvent faire leur travail.

Afficher le code

Appliquer le principe d'inversion de dépendance (DIP) au niveau architectural est très simple si vous respectez les modèles de conception agiles classiques. Il est également facile et même amusant de pratiquer et de donner des exemples de logique métier. Nous allons imaginer une application de lecture de livres électroniques.

class Test extends PHPUnit_Framework_TestCase {

	function testItCanReadAPDFBook() {
		$b = new PDFBook();
		$r = new PDFReader($b);

		$this->assertRegExp('/pdf book/', $r->read());
	}

}

class PDFReader {

	private $book;

	function __construct(PDFBook $book) {
		$this->book = $book;
	}

	function read() {
		return $this->book->read();
	}

}

class PDFBook {

	function read() {
		return "reading a pdf book.";
	}
}

Nous avons commencé à développer la liseuse en un lecteur PDF. Jusqu'ici, tout va bien. Nous avons une vérification d'expression régulière pour vérifier cela en utilisant la partie clé de la chaîne renvoyée par la méthode PDFBookPDFReader 类。读者上的 read() 函数委托给本书的 read() 方法。我们只是通过在 PDFBookreader().

N'oubliez pas que ce n'est qu'un exemple. Nous n'implémenterons pas de logique de lecture pour les fichiers PDF ou d'autres formats de fichiers. C'est pourquoi notre test ne vérifiera que quelques chaînes de base. Si nous devions écrire une vraie application, la seule différence résiderait dans la manière dont nous testons les différents formats de fichiers. La structure des dépendances est très similaire à notre exemple.

SOLID: 第四部分 - 依赖倒置原则SOLID : Partie 4 - Principe dinversion de dépendanceSOLID: 第四部分 - 依赖倒置原则

拥有一个使用 PDF 书籍的 PDF 阅读器对于有限的应用程序来说可能是一个合理的解决方案。如果我们的范围是编写一个 PDF 阅读器,仅此而已,那么它实际上是一个可以接受的解决方案。但我们想编写一个通用的电子书阅读器,支持多种格式,其中我们第一个实现的版本是 PDF。让我们重命名我们的读者类。

class Test extends PHPUnit_Framework_TestCase {

	function testItCanReadAPDFBook() {
		$b = new PDFBook();
		$r = new EBookReader($b);

		$this->assertRegExp('/pdf book/', $r->read());
	}

}

class EBookReader {

	private $book;

	function __construct(PDFBook $book) {
		$this->book = $book;
	}

	function read() {
		return $this->book->read();
	}

}

class PDFBook {

	function read() {
		return "reading a pdf book.";
	}
}

重命名没有功能上的反作用。测试仍在通过。

测试于下午 1:04 开始...

PHPUnit 3.7.28 由 Sebastian Bergmann 编写。

时间:13 毫秒,内存:2.50Mb

好的(1 个测试,1 个断言)

进程已完成,退出代码为 0

但它具有严重的设计效果。

SOLID: 第四部分 - 依赖倒置原则SOLID : Partie 4 - Principe dinversion de dépendanceSOLID: 第四部分 - 依赖倒置原则

我们的读者变得更加抽象。更一般。我们有一个通用的 EBookReader,它使用非常具体的书籍类型 PDFBook。抽象取决于细节。我们的书是 PDF 类型这一事实应该只是一个细节,任何人都不应该依赖它。

class Test extends PHPUnit_Framework_TestCase {

	function testItCanReadAPDFBook() {
		$b = new PDFBook();
		$r = new EBookReader($b);

		$this->assertRegExp('/pdf book/', $r->read());
	}

}

interface EBook {
	function read();
}

class EBookReader {

	private $book;

	function __construct(EBook $book) {
		$this->book = $book;
	}

	function read() {
		return $this->book->read();
	}

}

class PDFBook implements EBook{

	function read() {
		return "reading a pdf book.";
	}
}

反转依赖关系最常见、最常用的解决方案是在我们的设计中引入一个更抽象的模块。 “OOP 中最抽象的元素是接口。因此,任何其他类都可以依赖于接口并且仍然遵循 DIP”。

我们为读者创建了一个界面。该接口名为 EBook ,代表 EBookReader 的需求。这是尊重接口隔离原则 (ISP) 的直接结果,该原则提倡接口应反映客户端需求的理念。接口属于客户端,因此它们的命名反映了客户端需要的类型和对象,并且它们将包含客户端想要使用的方法。 EBookReader 使用 EBooks 并拥有 read() 方法是很自然的。

SOLID: 第四部分 - 依赖倒置原则SOLID : Partie 4 - Principe dinversion de dépendanceSOLID: 第四部分 - 依赖倒置原则

我们现在有两个依赖项,而不是单个依赖项。

  • 第一个依赖项从 EBookReader 指向 EBook 接口,并且它是类型用法。 EBookReader 使用 EBooks
  • 第二个依赖项不同。它从 PDFBook 指向相同的 EBook 接口,但它是类型实现。 PDFBook 只是 EBook 的一种特殊形式,因此实现了该接口来满足客户的需求。

不出所料,该解决方案还允许我们将不同类型的电子书插入阅读器。所有这些书籍的唯一条件是满足 EBook 接口并实现它。

class Test extends PHPUnit_Framework_TestCase {

	function testItCanReadAPDFBook() {
		$b = new PDFBook();
		$r = new EBookReader($b);

		$this->assertRegExp('/pdf book/', $r->read());
	}

	function testItCanReadAMobiBook() {
		$b = new MobiBook();
		$r = new EBookReader($b);

		$this->assertRegExp('/mobi book/', $r->read());
	}

}

interface EBook {
	function read();
}

class EBookReader {

	private $book;

	function __construct(EBook $book) {
		$this->book = $book;
	}

	function read() {
		return $this->book->read();
	}

}

class PDFBook implements EBook {

	function read() {
		return "reading a pdf book.";
	}
}

class MobiBook implements EBook {

	function read() {
		return "reading a mobi book.";
	}
}

这又将我们引向开闭原则,并且圆圈是闭合的。

依赖倒置原则是引导或帮助我们尊重所有其他原则的原则。尊重 DIP 将:

  • 几乎迫使您遵守 OCP。
  • 允许您分离职责。
  • 让你正确使用子类型。
  • 为您提供隔离界面的机会。

最终想法

就是这样。我们完了。所有有关 SOLID 原理的教程均已完成。对我个人来说,发现这些原则并在实施项目时牢记它们是一个巨大的变化。我完全改变了我对设计和架构的思考方式,我可以说从那时起我从事的所有项目都变得更加容易管理和理解。

我认为 SOLID 原则是面向对象设计最基本的概念之一。这些概念必须指导我们使我们的代码变得更好,并使我们作为程序员的生活变得更加轻松。设计良好的代码更容易让程序员理解。计算机很聪明,无论代码多么复杂,它们都可以理解。另一方面,人类能够在活跃、专注的头脑中保存的事物数量有限。更具体地说,此类事物的数量是神奇数字七、正负二。

Nous devons nous efforcer de structurer notre code autour de ces chiffres, et il existe plusieurs techniques qui peuvent nous aider à y parvenir. Les fonctions doivent avoir une longueur maximale de quatre lignes (cinq lignes en incluant la ligne de définition) afin qu'elles puissent tenir dans nos têtes en même temps. La profondeur d'indentation ne dépasse pas cinq couches. Cours avec pas plus de neuf méthodes. Généralement, cinq à neuf classes de modèles de conception sont utilisées. Notre conception de haut niveau dans le modèle ci-dessus utilise quatre à cinq concepts. Il existe cinq principes SOLID, chacun nécessitant entre cinq et neuf sous-concepts/modules/classes pour être illustrés. La taille idéale d’une équipe de programmation est de cinq à neuf personnes. Le nombre idéal d’équipes dans une entreprise est de cinq à neuf.

Comme vous pouvez le constater, le chiffre magique sept, plus ou moins deux, est tout autour de nous, alors pourquoi votre code devrait-il être différent ?

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn