Table of Contents
definition
DIP in the real world
Display code
最终想法
Home Backend Development PHP Tutorial SOLID: Part 4 - Dependency Inversion Principle

SOLID: Part 4 - Dependency Inversion Principle

Sep 01, 2023 pm 07:49 PM

Single Responsibility (SRP), Open/Closed (OCP), Liskov Substitution, Interface Isolation and Dependency Inversion. Five agile principles that should guide you every time you write code.

It would be unfair to tell you that any one SOLID principle is more important than another. However, perhaps none has such a direct and profound impact on your code than the Dependency Inversion Principle, or DIP for short. If you find other principles difficult to grasp or apply, start with this one and then apply the remaining principles to code that already follows the DIP.

definition

A. High-level modules should not depend on low-level modules. Both should rely on abstractions.

B. Abstractions should not depend on details. The details should depend on the abstraction.

This principle was defined by Robert C. Martin in his book "Agile Software Development, Principles, Patterns, and Practices" and was later republished in the C# version in the book "Agile Principles, Patterns, and Practices in C#" , this is the last principle of the five SOLID agile principles.

DIP in the real world

Before we start coding, I want to tell you a story. At Syneto, we're not always that careful with our code. A few years ago we knew very little and although we tried our best, not all projects were that good. We've been through hell and back, and we've learned a lot through trial and error.

Uncle Bob’s (Robert C. Martin) SOLID principles and architectural simplicity principles were game-changers for us and changed the way we code in ways that are hard to describe. In short, I will try to illustrate some of the key architectural decisions implemented by DIP that had a significant impact on our project.

Most web projects contain three main technologies: HTML, PHP, and SQL. It doesn't matter which specific version of these applications we're talking about or the type of SQL implementation you use. The problem is, the information from the HTML form has to end up in the database somehow. The glue between the two can be provided by PHP.

The most important thing is that these three technologies represent three different architectural layers very well: user interface, business logic and persistence. We'll discuss what these layers mean later. Now, let's focus on some strange but often encountered solutions to making these technologies work together.

Many times I've seen projects use SQL code in PHP tags within HTML files, or PHP code that echoes the HTML page and directly interprets the $_GET or $_POST global variable. But why is this bad?

SOLID: 第四部分 - 依赖倒置原则SOLID: Part 4 - Dependency Inversion PrincipleSOLID: 第四部分 - 依赖倒置原则

The image above represents the original version we described in the previous paragraph. The arrows represent various dependencies and we can conclude that basically everything depends on everything. If we need to make changes to a database table, we may end up editing the HTML file. Or, if we change a field in the HTML, we might end up changing the column names in the SQL statement. Or if we look at the second pattern, if the HTML changes, we most likely need to modify the PHP, or in a very bad case, when we generate all the HTML content from inside the PHP file, we definitely need to change the PHP file to Modify HTML content. So, there is no doubt that the dependencies between classes and modules are tortuous. But it didn't end there. Can store procedures; PHP code in SQL tables.

SOLID: 第四部分 - 依赖倒置原则SOLID: Part 4 - Dependency Inversion PrincipleSOLID: 第四部分 - 依赖倒置原则

In the above schema, the query against the SQL database returns PHP code generated using the data in the table. These PHP functions or classes are executing other SQL queries that return different PHP code, and the loop continues until eventually all the information is fetched and returned... possibly back to the UI.

I know this may sound outrageous to many of you, but if you haven't been involved in a project invented and implemented in this way, you will definitely do so in your future career. Most existing projects, regardless of programming language, are written according to old principles, by programmers who don't care or don't know how to do it better. If you're reading these tutorials, chances are you've already reached a higher level. You're ready, or ready, to respect your profession, embrace your craft, and do it better.

The alternative is to repeat the mistakes of those who came before you and suffer the consequences. At Syneto, when one of our projects reached a state of near unmaintainability due to its old and interdependent architecture and we basically had to abandon it for good, we decided not to go down that path anymore. Since then we have strived to have a clean architecture, properly respecting the SOLID principles and most importantly the dependency inversion principle.

SOLID: 第四部分 - 依赖倒置原则SOLID: Part 4 - Dependency Inversion PrincipleSOLID: 第四部分 - 依赖倒置原则

The magic of this architecture is how the dependencies are pointed:

  • The user interface (in most cases the Web MVC framework) or any other delivery mechanism of the project will depend on the business logic. Business logic is quite abstract. The user interface is very specific. UI is just one detail of a project, and it changes a lot. Nothing should depend on the UI, nothing should depend on your MVC framework.
  • Another interesting observation we can make is that persistence, database, MySQL or PostgreSQL, depends on the business logic. Your business logic is database independent. This allows persistence to be exchanged if you wish. If tomorrow you want to change MySQL to PostgreSQL or just plain text files, you can do that. Of course, you need to implement a specific persistence layer for the new persistence method, but you don't need to modify a single line of code in your business logic. The topic of persistence is explained in more detail in the Evolving to a Persistence Layer tutorial.
  • Finally, on the right side of the business logic, outside of it, we have all the classes that create the business logic classes. These are the factories and classes created by the entry point of our application. Many people tend to think that these belong in business logic, but when they create business objects, their only reason is to do so. They are just classes that help us create other classes. The business objects and logic they provide are independent of these factories. We can provide business logic using different patterns like simple factory, abstract factory, builder or simple object creation. It doesn't matter. Once business objects are created, they can do their job.

Display code

Applying the Dependency Inversion Principle (DIP) at the architectural level is very easy if you respect classic agile design patterns. It's also easy and even fun to practice and give examples in business logic. We will imagine an e-book reader application.

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.";
	}
}
Copy after login

We started developing the e-reader into a PDF reader. So far, so good. We have a PDFReader class that uses PDFBook. The read() function on the reader delegates to the read() method of the book. We just verified this by doing a regular expression check after the critical part of the string returned by PDFBook's reader() method.

Please remember this is just an example. We will not implement reading logic for PDF files or other file formats. That's why our test will only check some basic strings. If we were to write a real application, the only difference would be how we test different file formats. The dependency structure is very similar to our example.

SOLID: 第四部分 - 依赖倒置原则SOLID: Part 4 - Dependency Inversion PrincipleSOLID: 第四部分 - 依赖倒置原则

拥有一个使用 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.";
	}
}
Copy after login

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

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

PHPUnit 3.7.28 由 Sebastian Bergmann 编写。

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

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

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

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

SOLID: 第四部分 - 依赖倒置原则SOLID: Part 4 - Dependency Inversion PrincipleSOLID: 第四部分 - 依赖倒置原则

我们的读者变得更加抽象。更一般。我们有一个通用的 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.";
	}
}
Copy after login

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

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

SOLID: 第四部分 - 依赖倒置原则SOLID: Part 4 - Dependency Inversion PrincipleSOLID: 第四部分 - 依赖倒置原则

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

  • 第一个依赖项从 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.";
	}
}
Copy after login

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

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

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

最终想法

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

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

We should strive to structure our code around these numbers, and there are several techniques that can help us do this. Functions should be a maximum of four lines in length (five lines including the definition line) so that they can fit in our heads at the same time. The depth of indentation does not exceed five layers. Classes with no more than nine methods. Typically five to nine classes of design patterns are used. Our high-level design in the pattern above uses four to five concepts. There are five SOLID principles, each requiring between five and nine sub-concepts/modules/classes to be exemplified. The ideal size for a programming team is five to nine people. The ideal number of teams in a company is five to nine.

As you can see, the magic number seven, plus or minus two, is all around us, so why should your code be any different?

The above is the detailed content of SOLID: Part 4 - Dependency Inversion Principle. For more information, please follow other related articles on the PHP Chinese website!

Statement of this Website
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn

Hot AI Tools

Undresser.AI Undress

Undresser.AI Undress

AI-powered app for creating realistic nude photos

AI Clothes Remover

AI Clothes Remover

Online AI tool for removing clothes from photos.

Undress AI Tool

Undress AI Tool

Undress images for free

Clothoff.io

Clothoff.io

AI clothes remover

Video Face Swap

Video Face Swap

Swap faces in any video effortlessly with our completely free AI face swap tool!

Hot Tools

Notepad++7.3.1

Notepad++7.3.1

Easy-to-use and free code editor

SublimeText3 Chinese version

SublimeText3 Chinese version

Chinese version, very easy to use

Zend Studio 13.0.1

Zend Studio 13.0.1

Powerful PHP integrated development environment

Dreamweaver CS6

Dreamweaver CS6

Visual web development tools

SublimeText3 Mac version

SublimeText3 Mac version

God-level code editing software (SublimeText3)

Explain JSON Web Tokens (JWT) and their use case in PHP APIs. Explain JSON Web Tokens (JWT) and their use case in PHP APIs. Apr 05, 2025 am 12:04 AM

JWT is an open standard based on JSON, used to securely transmit information between parties, mainly for identity authentication and information exchange. 1. JWT consists of three parts: Header, Payload and Signature. 2. The working principle of JWT includes three steps: generating JWT, verifying JWT and parsing Payload. 3. When using JWT for authentication in PHP, JWT can be generated and verified, and user role and permission information can be included in advanced usage. 4. Common errors include signature verification failure, token expiration, and payload oversized. Debugging skills include using debugging tools and logging. 5. Performance optimization and best practices include using appropriate signature algorithms, setting validity periods reasonably,

How does session hijacking work and how can you mitigate it in PHP? How does session hijacking work and how can you mitigate it in PHP? Apr 06, 2025 am 12:02 AM

Session hijacking can be achieved through the following steps: 1. Obtain the session ID, 2. Use the session ID, 3. Keep the session active. The methods to prevent session hijacking in PHP include: 1. Use the session_regenerate_id() function to regenerate the session ID, 2. Store session data through the database, 3. Ensure that all session data is transmitted through HTTPS.

What are Enumerations (Enums) in PHP 8.1? What are Enumerations (Enums) in PHP 8.1? Apr 03, 2025 am 12:05 AM

The enumeration function in PHP8.1 enhances the clarity and type safety of the code by defining named constants. 1) Enumerations can be integers, strings or objects, improving code readability and type safety. 2) Enumeration is based on class and supports object-oriented features such as traversal and reflection. 3) Enumeration can be used for comparison and assignment to ensure type safety. 4) Enumeration supports adding methods to implement complex logic. 5) Strict type checking and error handling can avoid common errors. 6) Enumeration reduces magic value and improves maintainability, but pay attention to performance optimization.

Describe the SOLID principles and how they apply to PHP development. Describe the SOLID principles and how they apply to PHP development. Apr 03, 2025 am 12:04 AM

The application of SOLID principle in PHP development includes: 1. Single responsibility principle (SRP): Each class is responsible for only one function. 2. Open and close principle (OCP): Changes are achieved through extension rather than modification. 3. Lisch's Substitution Principle (LSP): Subclasses can replace base classes without affecting program accuracy. 4. Interface isolation principle (ISP): Use fine-grained interfaces to avoid dependencies and unused methods. 5. Dependency inversion principle (DIP): High and low-level modules rely on abstraction and are implemented through dependency injection.

Explain late static binding in PHP (static::). Explain late static binding in PHP (static::). Apr 03, 2025 am 12:04 AM

Static binding (static::) implements late static binding (LSB) in PHP, allowing calling classes to be referenced in static contexts rather than defining classes. 1) The parsing process is performed at runtime, 2) Look up the call class in the inheritance relationship, 3) It may bring performance overhead.

What is REST API design principles? What is REST API design principles? Apr 04, 2025 am 12:01 AM

RESTAPI design principles include resource definition, URI design, HTTP method usage, status code usage, version control, and HATEOAS. 1. Resources should be represented by nouns and maintained at a hierarchy. 2. HTTP methods should conform to their semantics, such as GET is used to obtain resources. 3. The status code should be used correctly, such as 404 means that the resource does not exist. 4. Version control can be implemented through URI or header. 5. HATEOAS boots client operations through links in response.

How do you handle exceptions effectively in PHP (try, catch, finally, throw)? How do you handle exceptions effectively in PHP (try, catch, finally, throw)? Apr 05, 2025 am 12:03 AM

In PHP, exception handling is achieved through the try, catch, finally, and throw keywords. 1) The try block surrounds the code that may throw exceptions; 2) The catch block handles exceptions; 3) Finally block ensures that the code is always executed; 4) throw is used to manually throw exceptions. These mechanisms help improve the robustness and maintainability of your code.

What are anonymous classes in PHP and when might you use them? What are anonymous classes in PHP and when might you use them? Apr 04, 2025 am 12:02 AM

The main function of anonymous classes in PHP is to create one-time objects. 1. Anonymous classes allow classes without names to be directly defined in the code, which is suitable for temporary requirements. 2. They can inherit classes or implement interfaces to increase flexibility. 3. Pay attention to performance and code readability when using it, and avoid repeatedly defining the same anonymous classes.

See all articles