Design PatternsThe book introduces design patterns to the software community, which is written by Erich Gamma, Richard Helm, Ralph Johnson, and John vlissides design (commonly known as "Gang of Four"). The core concepts behind the design patterns presented are very simple. After years of software development practice, Gamma and others have discovered patterns of fixed design, like architects designing houses and buildings, to develop templates for the location of a bathroom or the way the kitchen is constructed. Using these templates, or
Design Patternsmeans that better buildings can be designed faster. The same concept applies to software.
Design patterns represent not only a useful way to develop robust software faster, but also a way to encapsulate large ideas in friendly terms. For example, you can say that you are writing a message delivery system that provides loosely coupled, or that you are writing a pattern named Observer .
It is very difficult to demonstrate the value of a pattern with a smaller example. This is often a bit of an overkill, because the pattern actually works in a large code base. This article does not show large applications, so what you need to think about is how to apply the principle of the example in your own large application-not the code that this article demonstrates. This is not to say that you should not use patterns in small applications. Many good applications start with small applications and evolve to large applications, so there is no reason not to build on such solid coding practices.
Now that you understand the design patterns and their usefulness, let's look at five common patterns of PHP V5.
Factory mode
In the first book of design Patterns , many design patterns encourage the use of loose coupling . To understand this concept, let's talk about the arduous journey of many developers working on large systems. A problem occurs when you change a code fragment, and other parts of the system--the ones you thought were completely unrelated--might also have cascading corruption.
The problem is tight coupling . Functions and classes in a part of the system depend heavily on the behavior and structure of functions and classes in other parts of the system. You need a set of patterns that enable these classes to communicate with each other, but do not want to tightly bind them together to avoid interlocking.
In large systems, many of the code relies on a few key classes. There may be difficulties when you need to change these classes. For example, suppose you have a class that reads from a file User
. You want to change it to another class read from the database, but all code references the original class that was read from the file. At this point, it is convenient to use the Factory mode.
A factory pattern is a class that has some methods for creating objects for you. You can use the factory class to create objects without using them directly new
. This way, if you want to change the type of object you are creating, you can simply change the factory. All code that uses the factory is automatically changed.
Listing 1 shows a sample column for the factory class. The server side of the equation consists of two parts: a database and a set of PHP pages that allow you to add feedback, request a feedback list, and get articles related to specific feedback.
Listing 1. factory1.php
<?phpinterface iuser{ function GetName ();} Class User implements iuser{public function __construct ($id) {} public function GetName () { return "Ja CK "; }} Class userfactory{public static function Create ($id) { return new User ($id);} } $uo = userfactory::create (1); Echo ($uo->getname (). " \ n ");? >
IUser
The interface defines what action the user object should perform. IUser
implementation is called User
, the UserFactory
factory class creates the IUser
object. This relationship can be represented by the UML in Figure 1.
Figure 1. Factory classes and their associated Iuser interfaces and user classes
If you use the php
interpreter to run this code on the command line, you will get the following result:
% PHP factory1.php jack%
The test code requests the object to the factory User
and outputs getName
The result of the method.
There is a factory pattern variant that uses the factory method. These public static methods in the class construct objects of that type. This method is useful if it is important to create objects of this type. For example, suppose you need to create an object first, and then set many properties. This version of the factory pattern encapsulates the process in a single location, so that you don't have to copy complex initialization code or paste the copied code around the code base.
Listing 2 shows an example of using the factory method.
Listing 2. factory2.php
<?phpinterface iuser{ function GetName ();} Class User implements iuser{public static function Load ($id) { return new User ($id); } public static function Create () { return new User (null); } Public function __construct ($id) {} public function GetName () { return "Jack"; }} $uo = User::load (1); Echo ($uo->getname (). " \ n ");? >
This piece of code is much simpler. It has only one interface IUser
and one class that implements this interface User
. User
the class has two static methods for creating objects. This relationship can be represented by the UML in Figure 2.
Figure 2. Iuser interface and user class with factory method
Running the script on the command line produces the same result as in Listing 1, as follows:
% PHP factory2.php jack%
As mentioned above, sometimes such patterns seem to be overqualified in small environments. However, it is best to learn this solid coding format for use in projects of any size.
Single-element mode
Some application resources are exclusive because there is only one resource of this type. For example, a connection to a database through a database handle is exclusive. You want to share a database handle in your application because it is a cost when keeping a connection open or closed, especially if you are getting a single page.
Single-element mode can meet this requirement. If the application contains and contains only one object at a time, then the object is a single element (Singleton). The code in Listing 3 shows a single element of the database connection in PHP V5.
Listing 3. singleton.php
<?phprequire_once ("db.php"); class databaseconnection{public static function get () { static $db = null ; if ($db = = null) $db = new DatabaseConnection (); return $db; } Private $_handle = null; Private Function __construct () { $dsn = ' Mysql://root:[email protected]/photos '; $this->_handle =& db::connect ($DSN, Array ()); } Public function handle () { return $this->_handle; }} Print ("Handle =". Databaseconnection::get ()->handle (). " \ n ");p rint (" Handle = "). Databaseconnection::get ()->handle (). " \ n ");? >
This code displays DatabaseConnection
a single class named. You cannot create a self DatabaseConnection
because the constructor is private. But with static get
methods, you can get and get only one DatabaseConnection
object. This code is shown in UML 3.
Figure 3. Database connection single Element
The handle
database handle returned by the method is the same between two calls, which is the best proof. You can see this by running code on the command line.
% php singleton.php Handle = object ID #3Handle = Object ID #3%
The two handles returned are the same object. If you use a database connection single element throughout your application, you can reuse the same handle anywhere.
You can use global variables to store database handles, but the method works only for smaller applications. In larger applications, you should avoid using global variables and use objects and methods to access resources.
Observer pattern
The Observer pattern gives you another way to avoid tight coupling between components. The pattern is very simple: an object is made observable by adding a method that allows another object, the observer to register itself. When an observable object changes, it sends the message to the registered observer. These observers use this information to perform operations that are independent of observable objects. The result is that objects can talk to each other without knowing why.
A simple example is a list of users in the system. The code in Listing 4 shows a list of users that will send out a message when the user is added. This list can be observed by the log watcher who sends the message when the user is added.
Listing 4. observer.php
<?phpinterface iobserver{ function onChanged ($sender, $args);} Interface iobservable{ function addobserver ($observer);} Class UserList implements iobservable{ Private $_observers = Array (); Public Function Addcustomer ($name) { foreach ($this->_observers as $obs) $obs->onchanged ($this, $ name); } Public Function Addobserver ($observer) { $this->_observers []= $observer; }} Class Userlistlogger implements iobserver{public function onChanged ($sender, $args) { echo ("' $args ' Added to User list\n ");} } $ul = new UserList (), $ul->addobserver (New Userlistlogger ()), $ul->addcustomer ("Jack"); >
This code defines four elements: two interfaces and two classes. An IObservable
interface defines an object that can be observed, UserList
implements the interface, and registers itself as observable. IObserver
The list defines how the method can be used to become an observer and UserListLogger
implement an IObserver
interface. These elements are shown in UML in Figure 4.
Figure 4. Observable list of users and user list event log programs
If you run it on the command line, you will see the following output:
% php observer.php ' Jack ' added to user list%
Test the code creation UserList
and UserListLogger
Add the observer to it. Then add a consumer and notify you of this change UserListLogger
.
UserList
It is critical to realize that you do not know what the log program will do. There may be one or more listeners that perform other actions. For example, you might have an observer who sends a message to a new user and welcomes the new user to use the system. The value of this approach is to UserList
ignore all objects that depend on it, and it focuses on the work of maintaining a list of users and sending messages as the list changes.
This mode is not limited to in-memory objects. It is the basis of a database-driven message query system used in larger applications.
Command chain mode
The command chain pattern is based on loosely coupled topics, sending messages, commands, and requests, or sending arbitrary content through a set of handlers. Each handler will decide for itself whether it can handle the request. If it can, the request is processed and the process stops. You can add or remove handlers for the system without affecting other handlers. Listing 5 shows an example of this pattern.
Listing 5. chain.php
<?phpinterface icommand{function OnCommand ($name, $args);} Class commandchain{Private $_commands = Array (); Public Function AddCommand ($cmd) {$this->_commands []= $cmd; The Public Function RunCommand ($name, $args) {foreach ($this->_commands as $cmd) {if ($cmd->oncomma nd ($name, $args)) return; }}}class UserCommand implements icommand{public function OnCommand ($name, $args) {if ($name! = ' AddUser ') ret Urn false; Echo ("UserCommand handling ' addUser ' \ n"); return true; }}class Mailcommand implements icommand{public function OnCommand ($name, $args) {if ($name! = ' mail ') return FA Lse Echo ("Mailcommand handling ' mail ' \ n"); return true; }} $cc = new Commandchain (), $CC->addcommand (New UserCommand ()), $CC->addcommand (New Mailcommand ()); $cc RunCommand (' AddUser ', null); $cc->runcommand (' mail ', null);?
This code defines ICommand
the class that maintains the list of objects CommandChain
. Two classes can implement ICommand
an interface-one that responds to a request for a message and another that responds to the addition of a user. Figure 5 shows the UML.
Figure 5. Command chain and its related commands
If you run a script that contains some test code, you get the following output:
% PHP chain.php usercommand handling ' addUser ' Mailcommand handling ' mail '%
The code first creates the CommandChain
object and adds an instance of the two command object to it. Then run two commands to see who responded to these commands. If the name of the command matches UserCommand
or MailCommand
, the code fails and no action occurs.
When creating an extensible schema for processing requests, the command chain pattern is valuable and can be used to solve many problems.
Policy mode
The last design pattern we've talked about is the strategy model. In this mode, the algorithm is extracted from the complex class and can therefore be easily replaced. For example, if you want to change the way a page is arranged in a search engine, the policy mode is a good choice. Think about several parts of the search engine--part of the traversal page, one for each page, and one for the sorted results. In a complex example, these parts are in the same class. By using the policy mode, you can put the arrangement part in another class to change the way the page is arranged without affecting the rest of the search engine's code.
As a simpler example, listing 6 shows a user list class that provides a way to find a group of users based on a set of Plug and Play policies.
Listing 6. strategy.php
<?phpinterface istrategy{function Filter ($record);} Class Findafterstrategy implements istrategy{private $_name; Public function __construct ($name) {$this->_name = $name; The Public Function filter ($record) {return strcmp ($this->_name, $record) <= 0; }}class Randomstrategy implements istrategy{Public function filter ($record) {return rand (0, 1) >= 0.5; }}class userlist{Private $_list = Array (); Public function __construct ($names) {if ($names! = null) {foreach ($names as $name) {$this ->_list []= $name; }}} Public function Add ($name) {$this->_list []= $name; The Public function find ($filter) {$recs = array (); foreach ($this->_list as $user) {if ($filter->filter ($user)) $recs []= $user; } return $recs; }} $ul = new UserList (Array ("Andy", "Jack", "Lori", "Megan")), $f 1 = $ul->find (new Findafterstrategy ("J"));p Rint_ R ($f 1); $f 2 = $Ul->find (New Randomstrategy ());p Rint_r ($f 2);? >
This code is shown in UML 6.
Figure 6. List of users and policies for selecting users
UserList
The class is a wrapper for the package name array. It implements the find
method, which uses one of several strategies to select a subset of these names. These policies IStrategy
are defined by the interface, which has two implementations: one randomly selects the user and the other selects all subsequent names according to the specified name. When you run the test code, you get the following output:
% PHP strategy.php Array ( [0] = Jack [1] = Lori [2] = Megan) Array ( [0] = Andy [1] => ; Megan)%
The test code runs the same user list for two policies and displays the results. In the first case, the policy looks for J
any names that are lined up, so you'll get Jack, Lori, and Megan. The second strategy randomly selects the name, each time producing a different result. In this case, the result is Andy and Megan.
The strategy model is ideal for complex data management systems, or data processing systems, which require greater flexibility in the way they are filtered, searched, or processed.
Conclusion
This article describes just a few of the most common design patterns used in PHP applications. More design patterns are demonstrated in the design mode book. Don't give up because of the mystery of the architecture. Patterns are a great idea for any programming language, any skill level.
Five common PHP Design patterns