ホームページ  >  記事  >  バックエンド開発  >  SOLID: パート 4 - 依存関係逆転の原則

SOLID: パート 4 - 依存関係逆転の原則

PHPz
PHPzオリジナル
2023-09-01 19:49:061051ブラウズ

単一責任 (SRP)、オープン/クローズド (OCP)、リスコフ置換、インターフェース分離、および 依存関係反転。コードを書くときに常に指針となるアジャイルの 5 つの原則。

ある堅固な原則が他の原則よりも重要であると言うのは不公平でしょう。ただし、おそらく、依存性反転原則 (略して DIP) ほど、コードに直接的かつ重大な影響を与えるものはありません。他の原則を理解したり適用したりするのが難しい場合は、この原則から始めて、残りの原則をすでに DIP に従うコードに適用してください。

###意味###

A. 高レベルのモジュールは低レベルのモジュールに依存すべきではありません。どちらも抽象化に依存する必要があります。

B. 抽象化は詳細に依存すべきではありません。詳細は抽象化に依存する必要があります。

この原則は、Robert C. Martin の著書『アジャイル ソフトウェア開発、原則、パターン、および実践』で定義され、後に書籍『Agile Principles, Patterns, and Practices in C#』の C# バージョンで再公開されました。これは、SOLID アジャイルの 5 つの原則の最後の原則です。

現実世界でのDIP

コーディングを始める前に、話をしたいと思います。 Syneto では、コードを常にそれほど注意深く扱っているわけではありません。数年前、私たちはほとんど何も知らず、最善を尽くしましたが、すべてのプロジェクトがそれほど良いものではありませんでした。私たちは地獄を何度も経験し、試行錯誤を通じて多くのことを学びました。

ボブおじさん (ロバート C. マーティン) の SOLID 原則とアーキテクチャのシンプルさの原則は、私たちにとって大きな変革をもたらし、説明するのが難しい方法でコーディング方法を変えました。つまり、私たちのプロジェクトに大きな影響を与えた、DIP によって実装された主要なアーキテクチャ上の決定のいくつかを説明してみます。

ほとんどの Web プロジェクトには、HTML、PHP、SQL という 3 つの主要なテクノロジが含まれています。ここで話しているアプリケーションの特定のバージョンや、使用する SQL 実装の種類は関係ありません。問題は、HTML フォームからの情報が何らかの方法でデータベースに保存されなければならないことです。この 2 つの間の接着剤は PHP によって提供されます。

最も重要なことは、これら 3 つのテクノロジが、ユーザー インターフェイス、ビジネス ロジック、永続性という 3 つの異なるアーキテクチャ層をよく表しているということです。これらのレイヤーが何を意味するかについては後ほど説明します。ここで、これらのテクノロジーを連携させるための、奇妙な、しかしよく遭遇する解決策に焦点を当てましょう。

HTML ファイル内の PHP タグで SQL コードを使用しているプロジェクトや、HTML ページをエコーし​​て

$_GET

または $_POST グローバルを直接解釈する PHP コードを使用しているプロジェクトを何度も見てきました。変数。しかし、なぜこれが悪いのでしょうか?

SOLID: 第四部分 - 依赖倒置原则SOLID: パート 4 - 依存関係逆転の原則SOLID: 第四部分 - 依赖倒置原则

上の画像は、前の段落で説明した元のバージョンを表しています。矢印はさまざまな依存関係を表しており、基本的にすべてがすべてに依存していると結論付けることができます。データベース テーブルに変更を加える必要がある場合、HTML ファイルを編集することになる場合があります。あるいは、HTML 内のフィールドを変更すると、SQL ステートメント内の列名も変更される可能性があります。 2 番目のパターンを見ると、HTML が変更された場合は、PHP を変更する必要がある可能性が高く、非常に悪いケースでは、PHP ファイル内からすべての HTML コンテンツを生成するときに、必ず PHP を変更する必要があります。ファイルを使用して HTML コンテンツを変更します。したがって、クラスとモジュール間の依存関係が曲がりくねっていることは疑いの余地がありません。しかし、それで終わりではありませんでした。プロシージャ、PHP コードを SQL テーブルに保存できます。

SOLID: 第四部分 - 依赖倒置原则SOLID: パート 4 - 依存関係逆転の原則SOLID: 第四部分 - 依赖倒置原则

上記のスキーマでは、SQL データベースに対するクエリは、テーブル内のデータを使用して生成された PHP コードを返します。これらの PHP 関数またはクラスは、別の PHP コードを返す他の SQL クエリを実行しており、最終的にすべての情報がフェッチされて返され、おそらく UI に返されるまでループが続きます。

これは多くの人にとってとんでもないことのように聞こえるかもしれませんが、この方法で考案され実行されたプロジェクトに参加したことがない場合は、将来のキャリアで間違いなく参加するでしょう。既存のプロジェクトのほとんどは、プログラミング言語に関係なく、古い原則に基づいて、それを気にしない、またはより良くする方法を知らないプログラマーによって書かれています。これらのチュートリアルを読んでいる場合は、すでに高いレベルに到達している可能性があります。あなたは自分の職業を尊重し、自分の技術を受け入れ、それをより良くする準備ができています、または準備ができています。

もう一つの選択肢は、先人たちの間違いを繰り返し、その結果に苦しむことです。 Syneto では、私たちのプロジェクトの 1 つが、古く相互依存したアーキテクチャーのためにほぼ保守不可能な状態に達し、基本的にそれを永久に放棄しなければならなかったとき、もうその道を歩まないことに決めました。それ以来、私たちは SOLID 原則、そして最も重要なことに依存関係逆転の原則を適切に尊重しながら、クリーンなアーキテクチャを実現するよう努めてきました。

SOLID: 第四部分 - 依赖倒置原则SOLID: パート 4 - 依存関係逆転の原則SOLID: 第四部分 - 依赖倒置原则

このアーキテクチャの魅力は、依存関係がどのように示されるかです:

  • ユーザー インターフェイス (ほとんどの場合、Web MVC フレームワーク) またはプロジェクトのその他の配信メカニズムは、ビジネス ロジックによって異なります。ビジネス ロジックは非常に抽象的です。ユーザーインターフェイスは非常に具体的です。 UI はプロジェクトの細部の 1 つにすぎず、大きく変化します。 UI や MVC フレームワークに依存するものは何もありません。
  • もう 1 つの興味深い観察は、永続性、データベース、MySQL または PostgreSQL がビジネス ロジックに依存しているということです。ビジネス ロジックはデータベースに依存しません。これにより、必要に応じて永続性を交換できるようになります。明日、MySQL を PostgreSQL または単なるプレーン テキスト ファイルに変更したい場合は、それを行うことができます。もちろん、新しい永続化メソッド用に特定の永続化レイヤーを実装する必要がありますが、ビジネス ロジック内のコードを 1 行も変更する必要はありません。永続性のトピックについては、「永続層への進化」チュートリアルで詳しく説明されています。
  • 最後に、ビジネス ロジックの右側、その外側には、ビジネス ロジック クラスを作成するすべてのクラスがあります。これらは、アプリケーションのエントリ ポイントによって作成されたファクトリーとクラスです。多くの人は、これらがビジネス ロジックに属するものだと考える傾向がありますが、ビジネス オブジェクトを作成するとき、その目的はそのためだけです。これらは、他のクラスの作成に役立つクラスにすぎません。これらが提供するビジネス オブジェクトとロジックは、これらのファクトリから独立しています。単純なファクトリ、抽象ファクトリ、ビルダー、または単純なオブジェクトの作成など、さまざまなパターンを使用してビジネス ロジックを提供できます。それは問題ではありません。ビジネス オブジェクトが作成されると、そのジョブを実行できるようになります。

表示コード

従来のアジャイル設計パターンを尊重していれば、依存性反転原則 (DIP) をアーキテクチャ レベルで適用するのは非常に簡単です。ビジネス ロジックの例を示して練習することも簡単で楽しいです。電子書籍リーダーアプリを想定してみます。

リーリー

私たちは電子書籍リーダーを PDF リーダーとして開発し始めました。ここまでは順調ですね。 PDFBook を使用する PDFReader クラスがあります。リーダーの read() 関数は、書籍の read() メソッドに委譲します。 PDFBookreader() メソッドによって返された文字列の重要な部分の後で正規表現チェックを行うことで、これを検証しました。

これは単なる例であることに注意してください。 PDF ファイルやその他のファイル形式の読み取りロジックは実装しません。このため、テストではいくつかの基本的な文字列のみをチェックします。実際のアプリケーションを作成する場合、唯一の違いは、さまざまなファイル形式をテストする方法です。依存関係の構造はこの例と非常によく似ています。

SOLID: 第四部分 - 依赖倒置原则SOLID: パート 4 - 依存関係逆転の原則SOLID: 第四部分 - 依赖倒置原则

拥有一个使用 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: パート 4 - 依存関係逆転の原則SOLID: 第四部分 - 依赖倒置原则

我们的读者变得更加抽象。更一般。我们有一个通用的 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: パート 4 - 依存関係逆転の原則SOLID: 第四部分 - 依赖倒置原则

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

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

これらの数値に基づいてコードを構成するように努める必要があります。これを行うのに役立つテクニックがいくつかあります。関数は同時に頭の中に収まるように、最大​​ 4 行 (定義行を含めて 5 行) の長さにする必要があります。くぼみの深さは 5 層を超えません。メソッドが 9 つ以下のクラス。通常、5 ~ 9 クラスのデザイン パターンが使用されます。上記のパターンの高レベルの設計では、4 ~ 5 つのコンセプトが使用されています。 SOLID 原則には 5 つあり、それぞれの原則で 5 ~ 9 つのサブ概念/モジュール/クラスを例示する必要があります。プログラミング チームの理想的な規模は 5 ~ 9 人です。社内の理想的なチーム数は 5 ~ 9 です。

ご覧のとおり、魔法の数字 7 (プラスまたはマイナス 2) は私たちの身の回りに溢れているのに、なぜコードが異なる必要があるのでしょうか?

以上がSOLID: パート 4 - 依存関係逆転の原則の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。