PHP three layer structure (top)?? Simple three-layer structure
Let's take a simple message board code example and look at the simplest three-layer structure code, as shown in code 1:
Code 1
Appearance Layer Class
Class Lwordhomepage {
Add message
Public function Append ($newLWord) {
Call the intermediate service layer
$serv = new Lwordservicecore ();
$serv->append ($newLWord);
}
};
Intermediate service Layer
Class Lwordservicecore {
Add message
Public function Append ($newLWord) {
Invoking the data access layer
$dbTask = new Lworddbtask ();
$dbTask->append ($newLWord);
}
};
Data Access Layer
Class Lworddbtask {
Add message
Public function Append ($newLWord) {
Data layer code (omitted)
}
};
The execution sequence diagram, 1 shows:
(Fig. 1), simple three-layer structure sequence diagram
The order in which the three-layer structure is called can be visualized visually from the Code and time series diagrams. But the actual development of this simple three-layer structure does not meet the demand! Let's start with the two layers of code, the façade layer and the intermediate service layer. Creating and invoking intermediate service classes in the appearance layer Lwordhomepage class directly using the New keyword is a hard-coded way of Lwordservicecore. In the actual project development process, the appearance layer and the intermediary service layer may be developed by different people, that is, a function module is completed by many people together. and the appearance layer Lwordhomepage class development progress is impossible to wait until the Lwordservicecore class complete development completes only then starts (in other words, the appearance layer cannot wait until the intermediate service layer complete development completes only then starts), this kind of collaboration efficiency is very low! In order for the project to be developed by multiple people at the same time, we want to cut the code design. We can organize a temporary intermediate service class to meet the development progress of the façade layer. When the intermediate service layer is fully developed, replace it ... 2 is shown below:
(Fig. 2), the appearance layer switches between different services
Obviously, to achieve such a requirement, it is very inflexible to use the New keyword directly in the façade layer to create and invoke the Lwordservicecore class! It is difficult to do flexible switching!! We can create a Tempservice class that acts as a temporary implementation of the intermediate service layer. We also need to analyze the two classes of Tempservice and Lwordservicecore, which have the same append function for adding messages, but one is temporary and the other is real. Since the two classes of Tempservice and lwordservicecore have public functions, they should be able to have a common parent class. Consider that there are no other members and attributes for this public ancestor class, so define this common ancestor class as an interface, i.e. ilwordservice! The UML class is shown in Figure 3:
(Figure 3) define and implement the Ilwordservice interface
Tempservice or Lwordservicecore class objects are not directly created in the Lwordhomepage class, and the creation process is given to a factory class Myservicefactory (simple Factory mode). In this way, the Lwordhomepage class in the appearance layer only needs to know the Ilwordservice interface, the appearance layer code does not care about what the intermediary service code is, so it is very good to realize the separation of the appearance layer and the specific service code.
What does it equal? Like two hardware engineers, one is the manufacture of computer graphics cards, one is the manufacture of computer motherboards. The engineer who makes the graphics card can plug the video card into a test circuit to test if the video card is working properly? Similarly, the engineer making the motherboard can also plug the motherboard into another test circuit to test if the motherboard is working properly? By the time the two engineers have finished their work, they will be able to connect the results of their work together. This is a parallel development approach that can save almost half the time. From the point of view of software engineering, we should also consider whether we need to support multiple people to develop at the same time when we design the interface code, so as to improve production efficiency.
According to the UML class diagram (3), we modify the PHP code as shown in code 2:
Code 2, create a message service through the factory and call
Appearance Layer Class
Class Lwordhomepage {
Add message
Public function Append ($newLWord) {
Call Intermediate Service
$serv = Myservicefactory::create ();
Note that this is the operation of the Ilwordservice interface, not the Lwordservice class
$serv->append ($newLWord);
}
};
Message Service Interface
Interface Ilwordservice {
Public function append ($newLWord);
};
Service Factory class
Class Myservicefactory {
Create a message Service
public static function Create () {
if (1) {
Back to intermediate service tier
return new Lwordservicecore ();
} else {
Returns a temporary implementation
return new Tempservice ();
}
}
}
Temporary service class
Class Tempservice implements Ilwordservice {
Add message
Public function Append ($newLWord) {
Temporary code (omitted)
}
};
Intermediate service Layer
Class Lwordservicecore implements Ilwordservice {
Add message
Public function Append ($newLWord) {
Invoking the data access layer
$dbTask = new Lworddbtask ();
$dbTask->append ($newLWord);
}
};
Data Access Layer
Class Lworddbtask {
Add message
Public function Append ($newLWord) {
Data layer code (omitted)
}
};
The timing diagram is shown in Figure 4:
(Figure 4) Create a message service from the factory class
PHP three layer structure (bottom)?? PHP Implements AOP
Let's focus on the middle service layer. Intermediate service layer code is relatively simple, just call the data Access layer code to save the message to the database. As shown in code 1:
Code 1
Intermediate service Layer
Class Lwordservicecore implements Ilwordservice {
Add message
Public function Append ($newLWord) {
Invoking the data access layer
$dbTask = new Lworddbtask ();
$dbTask->append ($newLWord);
}
};
After seeing the message board presentation, the company's product and marketing departments may offer a variety of ideas and needs. For example, they want to judge the user's permissions before adding a message! Only registered users can leave a message! We need to modify the code, as shown in code 2:
Code 2, adding login verification
Intermediate service Layer
Class Lwordservicecore implements Ilwordservice {
Add message
Public function Append ($newLWord) {
if (! ( $userLogin)) {
Prompt user to log in
}
Invoking the data access layer
$dbTask = new Lworddbtask ();
$dbTask->append ($newLWord);
}
};
Marketing department also hope to add a message before the content of the message to check, if the message contains dirty words will not be saved. We continue to modify the code, as shown in code 3:
Code 3, adding profanity Filtering
Intermediate service Layer
Class Lwordservicecore implements Ilwordservice {
Add message
Public function Append ($newLWord) {
if (! ( $userLogin)) {
Prompt user to log in
}
if (Stristr ($newLWord, "SB")) {
contain dirty words, message sent failed
}
Invoking the data access layer
$dbTask = new Lworddbtask ();
$dbTask->append ($newLWord);
}
};
The product department has also put forward new requirements that they want to add to the integration mechanism. In particular, the user every time after the success of the message to the user +5 points. We continue to modify the code, as shown in code 4:
Code 4, adding the message integration mechanism
Intermediate service Layer
Class Lwordservicecore implements Ilwordservice {
Add message
Public function Append ($newLWord) {
if (! ( $userLogin)) {
Prompt user to log in
}
if (Stristr ($newLWord, "SB")) {
contain dirty words, message sent failed
}
Invoking the data access layer
$dbTask = new Lworddbtask ();
$dbTask->append ($newLWord);
Add points to users
$score = Getuserscore ($userName);
$score = $score + 5;
Saveuserscore ($userName, $score);
}
};
Before long, the product department also refined the requirements, they want users to accumulate enough points for each 1000 minutes later, the user upgrade. We continue to modify the code, as shown in code 5:
Code 5, adding user upgrade rules
Intermediate service Layer
Class Lwordservicecore implements Ilwordservice {
Add message
Public function Append ($newLWord) {
if (! ( $userLogin)) {
Prompt user to log in
}
if (Stristr ($newLWord, "fuck")) {
contain dirty words, message sent failed
}
Invoking the data access layer
$dbTask = new Lworddbtask ();
$dbTask->append ($newLWord);
Add points to users
$score = Getuserscore ($userName);
$score = $score + 5;
Saveuserscore ($userName, $score);
Upgrade to users
if ($score% 1000) = = 0) {
$level = Getuserlevel ($userName);
$level = $level + 1;
Saveuserlevel ($userName, $level);
}
}
};
As the demand increases, we need to constantly modify the intermediate service layer code. But you should not be difficult to find, the more the demand for intermediate service layer code is more and more huge! Finally, even if we use the three-layer structure of the development model, it is still not effective to reduce the engineering difficulty! In addition, after modifying the intermediate service code in response to a change in demand, it is necessary to re-test all the code, not to test the new code effectively ...
In fact, let us carefully analyze this message board code, I would like to put forward a main business logic and the concept of sub-business logic. In any case, the contents of the message into the database, which is the backbone of business logic! This is the main business logic! This part has not been modified as the demand increases. As for the permission to check before depositing to the database, to carry out content inspection, after the database to add points to the user, and then to the user upgrade, these are the pre-order work and mop up work, are the business logic! The main business logic is almost constant, and the sub-business logic changes very frequently. To improve the readability and maintainability of your code, we can consider putting these business logic somewhere else and try not to interfere with the main business logic. Main business logic concentrate on doing what you have to do, as for anything else, the main business logic is indifferent! Then our code can be written like this, as shown in code 6:
Code 6, separating the primary business logic from the secondary business logic
Intermediate service Layer
Class Lwordservicecore implements Ilwordservice {
Add message
Public function Append ($newLWord) {
Before adding a message
Beforeappend ($newLWord);
Invoking the data access layer
$dbTask = new Lworddbtask ();
$dbTask->append ($newLWord);
After adding a message
Behindappend ($newLWord);
}
};
We can put the Authority judgment code and message content text filter code into the Beforeappend function, the user integration code into the Behindappend function, so that the sub-business logic from the main business logic code to clean out. The main business logic knows there is a "prelude" function Beforeappend, there is a "epilogue" function Behindappend, but in the Prelude and epilogue functions specifically do something, the main business logic does not know, do not need to know! Of course the actual coding work is not so simple, we have to take into account the product Department and marketing department more requirements change, so it is best to implement a plug-in approach to deal with this change, but only rely on two functions beforeappend and behindappend is not up to this goal ~
To implement plug-ins, you can create an interface! The advantage of using interfaces is that you can isolate definitions and implementations, and also implement polymorphism. We set up a message extension interface ilwordextension, which has two functions beforeappend and behindappend. Permissions check, content check, add points these functions can be regarded as the implementation of the Ilwordextension interface three implementation classes, the main business logic in turn, traversing the three implementation classes to complete the sub-business logic. 1 is shown below:
(Figure 1), adding an extension interface
The checkpowerextension extension class is used as a user right check, the Checkcontentextension extension class is used as message content checking, and the Addscoreextension extension class is used to add points and upgrades to users. The schematic code is shown in code 7:
Code 7, adding an extension interface
Expansion interface
Interface Ilwordextension {
Before adding a message
Public Function Beforeappend ($newLWord);
After adding a message
Public Function Behindappend ($newLWord);
};
Check Permissions
Class Checkpowerextension implements Ilwordextension {
Before adding a message
Public Function Beforeappend ($newLWord) {
To judge user permissions here
}
After adding a message
Public Function Behindappend ($newLWord) {
}
};
Check message text
Class Checkcontentextension implements Ilwordextension {
Before adding a message
Public Function Beforeappend ($newLWord) {
if (Stristr ($newLWord, "SB")) {
throw new Exception ();
}
}
After adding a message
Public Function Behindappend ($newLWord) {
}
};
User points
Class Addscoreextension implements Ilwordextension {
Before adding a message
Public Function Beforeappend ($newLWord) {
}
After adding a message
Public Function Behindappend ($newLWord) {
Give the user points here
}
};
Intermediate service Layer
Class Lwordservicecore implements Ilwordservice {
Add message
Public function Append ($newLWord) {
Before adding a message
$this->beforeappend ($newLWord);
Invoking the data access layer
$dbTask = new Lworddbtask ();
$dbTask->append ($newLWord);
After adding a message
$this->behindappend ($newLWord);
}
Before adding a message
Private Function Beforeappend ($newLWord) {
Get an extended array
$extArray = $this->getextarray ();
foreach ($extArray as $ext) {
Iterate through each extension and call its beforeappend function
$ext->beforeappend ($newLWord);
}
}
After adding a message
Private Function Behindappend ($newLWord) {
Get an extended array
$extArray = $this->getextarray ();
foreach ($extArray as $ext) {
Iterate through each extension and call its behindappend function
$ext->behindappend ($newLWord);
}
}
Gets an array of extensions,
The return value of the function is actually an array of ilwordextension interfaces
Private Function Getextarray () {
Return Array (
Check Permissions
New Checkpowerextension (),
Check content
New Checkcontentextension (),
Extra points
New Addscoreextension (),
);
}
};
If there is a new requirement, we simply add the Ilwordextension implementation class and register it with the Getextarray function. The program has been organized and scalable.
But don't be too busy to be happy, there is a problem in this extensibility. When new requirements are presented, we can add the Ilwordextension implementation class again, which is true. However, registering this new class with the Getextarray function is tantamount to modifying the main business logic code. Can we not modify it? It's not always good to have a new change in demand or to tell the main business logic. Ideally, the main business logic code does not have to be modified after the new extension code is added to the system, because the primary business logic does not know of a new extension at all! To do this we also need to optimize the design scheme, 2 shows:
(Figure 2), adding extended family classes
For the main program that calls the extension (that is, the intermediate service class Lwordservicecore), just let it know that there is a ilwordextension (extension) This thing, it does not need to know Checkpowerextension (check permission extension), Checkcontentextension (check content extension) and addscoreextension (add-on extension) of these three classes. The call procedure for these three classes is moved to the lwordextensionfamily (extended family Class).
Lwordextensionfamily is actually a container class that can store multiple Ilwordextension interface instances, as shown in Figure 2, this container class is not just a ilwordextension interface. It also aggregates multiple instances of the Ilwordextension interface, so it's special! For the Lwordservicecore class, this class knows only the Ilwordextension interface, but does not know that there are three implementation classes for this interface. Exactly the Lwordextensionfamily class implements the Ilwordextension interface, which is well in line with the requirements of the intermediate service class, and this extended family class knows that Ilwordextension has three implementation classes and calls them one at a The lwordextensionfamily code is probably shown in code 8:
Code 8, extended family
Extended family
Class Lwordextensionfamily implements Ilwordextension {
Extended Array
Private $_extensionarray = Array ();
Add extension
Public Function addextension (ilwordextension $extension) {
$this->_extensionarray []= $extension;
}
Before adding a message
Public Function Beforeappend ($newLWord) {
foreach ($this->_extensionarray as $extension) {
$extension->beforeappend ($newLWord);
}
}
After adding a message
Public Function Behindappend ($newLWord) {
foreach ($this->_extensionarray as $extension) {
$extension->behindappend ($newLWord);
}
}
}
Through code 8 It is not difficult to see that the Lwordextensionfamily class, although also implemented the Ilwordextension interface, but it does not do any real operation, but through the loop statement will call the process one by one pass down. To smooth the way of scaling to insert, it is best to create a factory class Myextensionfactory. As shown in code 9:
Code 9
Custom Extension Factory
Class Myextensionfactory {
Create a message extension
public static function Createlwordextension () {
$LWEF = new lwordextensionfamily ();
Add extension
$LWEF->addextension (New Checkpowerextension ());
$LWEF->addextension (New Checkcontentextension ());
$LWEF->addextension (New Addscoreextension ());
return $lwef;
Note that the extended family class object is returned.
Extended family lwordextensionfamily happens to also implement Interface Ilwordextension,
So this is in line with the business logic requirements.
From then on, the business logic can not care about the specific extension objects, as long as you know the extension family can
}
}
The advantage of using the extended factory class is that you can arbitrarily add and remove extension instances, which is a good way to implement pluggable programming. For the Lwordservicecore class to know only one ilwordextension interface, for lwordextensionfamily to know that one by one calls are required for each extension, But how many extensions are given by myextensionfactory. The accountability structure is also very clear. If we make an assumption that the createlwordextension function of the Myextensionfactory class is not to add an extension list by hard coding such as the new keyword, but rather to get an extended list by a more subtle way of reading the configuration file, So is it more convenient and flexible? But this is no longer discussed in this article.
The intermediate service layer obtains a concrete instance of the Ilwordextension interface through the factory class, and then calls its Beforeappend and Behindappend methods. Of course, the intermediate service does not know that the factory class is actually returning a container with multiple ilwordextension instances (because the container also implements the Ilwordextension interface), so the intermediary service does not know that the extension was called by one by one. The complete code is shown in code 10:
Code 10, complete code
Expansion interface
Interface Ilwordextension {
Before adding a message
Public Function Beforeappend ($newLWord);
After adding a message
Public Function Behindappend ($newLWord);
};
Check Permissions
Class Checkpowerextension implements Ilwordextension {
Before adding a message
Public Function Beforeappend ($newLWord) {
To judge user permissions here
}
After adding a message
Public Function Behindappend ($newLWord) {
}
};
Check message text
Class Checkcontentextension implements Ilwordextension {
Before adding a message
Public Function Beforeappend ($newLWord) {
if (Stristr ($newLWord, "fuck"))
throw new Exception ();
}
After adding a message
Public Function Behindappend ($newLWord) {
}
};
User points
Class Addscoreextension implements Ilwordextension {
Before adding a message
Public Function Beforeappend ($newLWord) {
}
After adding a message
Public Function Behindappend ($newLWord) {
Give the user points here
}
};
Extended family
Class Lwordextensionfamily implements Ilwordextension {
Extended Array
Private $_extensionarray = Array ();
Add extension
Public Function addextension (ilwordextension $extension) {
$this->_extensionarray []= $extension;
}
Before adding a message
Public Function Beforeappend ($newLWord) {
foreach ($this->_extensionarray as $extension) {
$extension->beforeappend ($newLWord);
}
}
After adding a message
Public Function Behindappend ($newLWord) {
foreach ($this->_extensionarray as $extension) {
$extension->behindappend ($newLWord);
}
}
}
Custom Extension Factory
Class Myextensionfactory {
Create a message extension
public static function Createlwordextension () {
$LWEF = new lwordextensionfamily ();
Add extension
$LWEF->addextension (New Checkpowerextension ());
$LWEF->addextension (New Checklwordextension ());
$LWEF->addextension (New Addscoreextension ());
return $lwef;
}
}
Intermediate service Layer
Class Lwordservicecore implements Ilwordservice {
Add message
Public function Append ($newLWord) {
Get extension
$ext = Myextensionfactory::createlwordextension ();
$ext->beforeappend ($newLWord);
Invoking the data access layer
$dbTask = new Lworddbtask ();
$dbTask->append ($newLWord);
$ext->behindappend ($newLWord);
}
};
You can see from code 10 that although checkpowerextension, Checkcontentextension, Addscoreextension and Lwordextensionfamily both implement ilwordextension interfaces, but their beforeappend and behindappend function processes are completely different! In particular, the lwordextensionfamily extended family class, which does not have a substantive business logic process, but instead passes the call to each extension in turn. The different implementations of beforeappend and Behindappend functions in specific classes are typical features of object-oriented programming: Polymorphism!
The spread of the secondary business logic into each extension is very similar to the way AOP (Aspect orientedprogramming, aspect-oriented programming) is programmed. Permission checks, content checks, and integrals can be viewed as different facets that intersect with the main business logic, but do not affect the main business logos ... The advantage of this is that the extension code does not interfere with the primary business logic, and we can encode and unit test for an extension, and then insert the extension into the orchestration through the Myextensionfactory factory class. The complete execution procedure is shown in 3:
(Figure 3), execution process