Develop 7 object-oriented habits in PHP

Source: Internet
Author: User
If you are not planning to create an application using the OO principle, use the object-oriented (OO) language features of PHP, these seven habits will help you begin to convert between process programming and OO programming. In the early days of PHP programming, PHP code was essentially process-oriented. The feature of process code is to use process to build application blocks. A process provides a certain degree of reuse by allowing calls between processes.
However, without object-oriented language structures, programmers can still introduce OO features into PHP code. This is a bit difficult and makes the code difficult to read, because it is a hybrid example (containing a pseudo OO design process language ). Use OO construction in PHP code-for example, defining and using classes, building relationships between inherited classes, and defining interfaces-you can easily build code that conforms to excellent OO practices.
Although there are not many modular pure process designs that run well, the advantages of OO design are in maintenance. Because most of the lifecycle of a typical application is maintained, code maintenance is an important part of the application lifecycle. In addition, code maintenance is easy to forget during the development process. If there is competition in application development and deployment, long-term maintainability may be placed in a secondary position.
Modularization-one of the main features of an excellent OO design-can help with such maintenance. Modularization will help encapsulate changes so that applications can be expanded and modified more easily over time.
In general, although there are more than seven habits of building OO software, following these seven habits can make the code conform to the basic OO design standards. They will provide you with a stronger foundation on which to build more OO habits and build software that can be easily maintained and expanded. These habits target several main features of modularity. For more information about language-independent OO design advantages, see references.
Seven excellent php oo habits include:
Be humble.
Be a good neighbor.
Avoid seeing Medusa.
Use the weakest link.
You are rubber; I am glue.
Restrict propagation.
Consider the usage mode.
Be humble
To be modest, avoid exposing yourself in class implementation and function implementation. Hiding your information is a basic habit. If you cannot develop the habit of hiding implementation details, it will be difficult to develop any other habits. Information hiding is also called encapsulation.
Public fields are a bad habit for many reasons. The most important reason is that you do not have the proper choice in implementing changes. Use the OO concept to isolate changes, while encapsulation plays an indispensable role in ensuring that changes are essentially not viral (viral) changes. A viral change is a small change at the beginning-for example, an array that saves three elements is changed to an array that contains only two elements. Suddenly, you find that you need to change more and more code to adapt to the negligible changes.
One simple way to start hiding information is to keep the fields private and expose them using public access methods, just like the windows in your home. The whole wall is not open to the outside, but only one or two windows are opened (I will introduce more information about the access method in "good habits: using public access methods ).
In addition to allowing your implementations to be hidden after the changes, using public access methods rather than directly exposing fields will allow you to build on the basis of the basic implementation, the method overwrites the implementation of the access method to execute behavior slightly different from that of the parent method. It also allows you to construct an abstract implementation so that the actual implementation is delegated to override the basic implementation class.
Bad habit: publish public fields
In the example of bad code in listing 1, the field of the Person object is directly exposed as a public field rather than an access method. Although this behavior is very attractive, especially for lightweight data objects, it will limit you.
Listing 1. bad habits of making public fields public

The code is as follows:


Class Person
{
Public $ prefix;
Public $ givenName;
Public $ familyName;
Public $ suffix;
}
$ Person = new Person ();
$ Person-> prefix = "Mr .";
$ Person-> givenName = "John ";
Echo ($ person-> prefix );
Echo ($ person-> givenName );
?>


If the object has any changes, all code that uses the object also needs to be changed. For example, if someone's teaching name, last name, and other names are encapsulated in the PersonName object, you need to modify all the code to adapt to the change.
Good habit: use public access
By using good OO habits (see listing 2), the same object now has private fields rather than public fields, the get and set public methods called access methods are used to cautiously disclose private fields to the outside world. These access methods now provide a public method for getting information from the PHP class, so that when the implementation changes, the need to change all the code using the class is likely to become smaller.
Listing 2. good habits of using public access methods

The code is as follows:


Class Person
{
Private $ prefix;
Private $ givenName;
Private $ familyName;
Private $ suffix;
Public function setPrefix ($ prefix)
{
$ This-> prefix = $ prefix;
}
Public function getPrefix ()
{
Return $ this-> prefix;
}
Public function setGivenName ($ gn)
{
$ This-> givenName = $ gn;
}
Public function getGivenName ()
{
Return $ this-> givenName;
}
Public function setFamilyName ($ fn)
{
$ This-> familyName = $ fn;
}
Public function getFamilyName ()
{
Return $ this-> familyName;
}
Public function setSuffix ($ suffix)
{
$ This-> suffix = $ suffix;
}
Public function getSuffix ()
{
Return $ suffix;
}
}
$ Person = new Person ();
$ Person-> setPrefix ("Mr .");
$ Person-> setGivenName ("John ");
Echo ($ person-> getPrefix ());
Echo ($ person-> getGivenName ());
?>


At first glance, this code may do a lot of work, and in fact it may be more of the work at the front end. However, it is usually very cost-effective to use good OO habits in the long run, because it will greatly consolidate future changes.
In the code version shown in listing 3, I have changed the internal implementation to use the joined array of the name component. Ideally, I want to handle errors and check whether the elements exist more carefully, however, the purpose of this example is to show the extent to which the code using my class does not need to be changed-the code is not aware of the class change. Remember that the reason for adopting OO habits is to carefully encapsulate changes so that the code is more scalable and easier to maintain.
Listing 3. Another example using different internal implementations

Code

The code is as follows:


Class Person
{
Private $ personName = array ();
Public function setPrefix ($ prefix)
{
$ This-> personName ['prefix'] = $ prefix;
}
Public function getPrefix ()
{
Return $ this-> personName ['prefix'];
}
Public function setGivenName ($ gn)
{
$ This-> personName ['givenname'] = $ gn;
}
Public function getGivenName ()
{
Return $ this-> personName ['givenname'];
}
/* Etc ...*/
}
/*
* Even though the internal implementation changed, the code here stays exactly
* The same. The change has been encapsulated only to the Person class.
*/
$ Person = new Person ();
$ Person-> setPrefix ("Mr .");
$ Person-> setGivenName ("John ");
Echo ($ person-> getPrefix ());
Echo ($ person-> getGivenName ());
?>



Be a good neighbor
When constructing a class, it should correctly handle its own errors. If this class does not know how to handle errors, these errors should be encapsulated in a format that the caller understands. In addition, avoid returning empty objects or objects with invalid states. In many cases, you only need to check the parameters and throw a specific exception to indicate the cause of invalid parameters. When you develop this habit, it can help you-and people who maintain code or use objects-save a lot of time.
Bad habit: do not handle errors
Consider the example shown in listing 4. this example will accept some parameters and return the Person object filled with some values. However, the parsePersonName () method does not verify whether the provided $ val variable is null, whether it is a zero-length string, or whether the string uses an unresolvable format. The parsePersonName () method does not return the Person object, but returns null. Administrators or programmers who use this method may feel very troublesome-at least they now need to set breakpoints and Debug PHP scripts.

Listing 4. do not throw or handle bad habits

The code is as follows:


Class PersonUtils
{
Public static function parsePersonName ($ format, $ val)
{
If (strpos (",", $ val)> 0 ){
$ Person = new Person ();
$ Parts = split (",", $ val); // Assume the value is last, first
$ Person-> setGivenName ($ parts [1]);
$ Person-> setFamilyName ($ parts [0]);
}
Return $ person;
}
}




The parsePersonName () method in listing 4 can be modified to initialize the Person object outside the if condition to ensure that a valid Person object is always obtained. However, what you get is the Person without the set attribute, which still does not improve your predicament.
Good habit: each module handles its own errors
Do not let the caller guess it out of thin air, but verify the parameters in advance. If an unset variable cannot generate valid results, check the variable and throw InvalidArgumentException. If the string cannot be empty or must be in a specific format, check the format and throw an exception. Listing 5 explains how to create exceptions and new conditions in the parsePerson () method that demonstrates some basic verification.

Listing 5. good habits of throwing errors

The code is as follows:


Class InvalidPersonNameFormatException extends LogicException {}

Class PersonUtils
{
Public static function parsePersonName ($ format, $ val)
{
If (! $ Format ){
Throw new InvalidPersonNameFormatException ("Invalid PersonName format .");
}
If ((! Isset ($ val) | strlen ($ val) = 0 ){
Throw new InvalidArgumentException ("Must supply a non-null value to parse .");
}

}
}
?>




The ultimate goal is to allow people to use your class without understanding how it works. If the method they are using is incorrect or is not used as expected, you do not need to guess why they cannot work. As a good neighbor, you need to know that the person who reused your class has no special function, so you need to solve the issue of speculation.

Avoid seeing Medusa
When I first understood the concept of OO, I was very skeptical about whether the interface was actually helpful. My colleague gave me an example. if I don't use an interface, I can see Medusa's header. In Greek mythology, Medusa is a female with snake hair. Anyone who looks at her will turn into a rock. Pael Hughes, who killed Medusa, watched her shadow on shield to avoid turning it into a stone and confrontation with her.
The interface is the mirror for Medusa. When you use a specific implementation, the code must also be changed as the implementation code changes. Direct use of the implementation will limit your selection, because you have essentially converted the class into a "stone ".
Bad habit: Do not use interfaces
Listing 6 shows an example of adding a Person object to a database. It will get the Person's name and return the matched Person object in the database.

Listing 6. bad habits of not using interfaces

The code is as follows:


Class DBPersonProvider
{
Public function getPerson ($ givenName, $ familyName)
{
/* Go to the database, get the person ...*/
$ Person = new Person ();
$ Person-> setPrefix ("Mr .");
$ Person-> setGivenName ("John ");
Return $ person;
}
}
/* I need to get person data ...*/
$ Provider = new DBPersonProvider ();
$ Person = $ provider-> getPerson ("John", "Doe ");
Echo ($ person-> getPrefix ());
Echo ($ person-> getGivenName ());
?>




Before the environment changes, the code that installs the Person in the database can run normally. For example, loading a Person from a database may apply to applications of the first version, but for the second version, you may need to add the function of loading a Person from the Web service. In fact, this class has become "stone", because it is directly using the implementation class and the changes that can be made now are very limited.
Good habit: use interfaces
Listing 7 shows a sample code that is not changed after the user's new method is loaded. This example shows an interface named PersonProvider, which declares a single method. If any code uses PersonProvider, the code cannot directly use the implementation class. Instead, it uses PersonProvider like an actual object.

Listing 7. good habits of using interfaces

The code is as follows:


Interface PersonProvider
{
Public function getPerson ($ givenName, $ familyName );
}
Class DBPersonProvider implements PersonProvider
{
Public function getPerson ($ givenName, $ familyName)
{
/* Pretend to go to the database, get the person ...*/
$ Person = new Person ();
$ Person-> setPrefix ("Mr .");
$ Person-> setGivenName ("John ");
Return $ person;
}
}
Class PersonProviderFactory
{
Public static function createProvider ($ type)
{
If ($ type = 'database ')
{
Return new DBPersonProvider ();
} Else {
Return new NullProvider ();
}
}
}
$ Config = 'database ';
/* I need to get person data ...*/
$ Provider = PersonProviderFactory: createProvider ($ config );
$ Person = $ provider-> getPerson ("John", "Doe ");
Echo ($ person-> getPrefix ());
Echo ($ person-> getGivenName ());
?>



When using interfaces, try to avoid directly referencing the implementation class. On the contrary, the use of external content can provide the correct implementation. If your class is loaded based on some logic implementation, it still needs to get the definition of all implementation classes, and that does not achieve any effect.
You can use the Factory mode to create an instance that implements the interface implementation class. According to the conventions, the factory method starts with "create" and returns the interface. It can obtain necessary parameters for your factory to calculate which implementation class should be returned.
In listing 7, the createProvider () method only obtains $ type. If $ type is set to database, the factory returns the DBPersonProvider instance. Any new implementation of the personnel installed in the database does not require any changes in the class using the factory and interface. DBPersonProvider implements the PersonProvider interface and has the actual implementation of the getPerson () method.

Use the weakest link
Loose coupling of modules is a good thing; it is one of the attributes that allow you to encapsulate changes. The other two habits-"exercise caution" and "avoid seeing Medusa"-help you build loosely coupled modules. To implement loosely coupled classes, you can develop the habit of reducing class dependencies.
Bad habit: tightly coupled
In listing 8, reducing dependencies does not mean reducing the dependencies of clients that use objects. Instead, this example shows how to reduce the dependency with the correct class and minimize the dependency.

Listing 8. bad habits of tightly coupled Address

The code is as follows:


Require_once "./AddressFormatters. php ";
Class Address
{
Private $ addressLine1;
Private $ addressLine2;
Private $ city;
Private $ state; // or province...
Private $ postalCode;
Private $ country;
Public function setAddressLine1 ($ line1)
{
$ This-> addressLine1 = $ line1;
}
/* Accessors, etc ...*/
Public function getCountry ()
{
Return $ this-> country;
}
Public function format ($ type)
{
If ($ type = "inline "){
$ Formatter = new InlineAddressFormatter ();
} Else if ($ type = "multiline "){
$ Formatter = new MultilineAddressFormatter ();
} Else {
$ Formatter = new NullAddressFormatter ();
}
Return $ formatter-> format ($ this-> getAddressLine1 (),
$ This-> getAddressLine2 (),
$ This-> getCity (), $ this-> getState (), $ this-> getPostalCode (),
$ This-> getCountry ());
}
}
$ Addr = new Address ();
$ Addr-> setAddressLine1 ("123 Any St .");
$ Addr-> setAddressLine2 ("Ste 200 ");
$ Addr-> setCity ("Anytown ");
$ Addr-> setState ("AY ");
$ Addr-> setPostalCode ("55555-0000 ");
$ Addr-> setCountry ("US ");
Echo ($ addr-> format ("multiline "));
Echo ("\ n ");
Echo ($ addr-> format ("inline "));
Echo ("\ n ");
?>




The code for calling the format () method on the Address object may look great-this code uses the Address class to call format () and complete it. On the contrary, the Address class is not so lucky. It needs to understand the various formatting methods used for correct formatting, which may make the Address object unavailable to others, especially when others are not interested in format () when the formatting method class is used in the method. Although the code using Address does not have many dependencies, the Address class has a lot of code, and it may be just a simple data object.
The Address class is closely coupled with the implementation class that knows how to format the Address object.
Good habit: loose coupling between objects
When building an excellent OO design, you must consider the concept of Separation of Concerns (SoC. SoC refers to trying to detach objects by actually following the content to reduce coupling. In the original Address class, it must focus on how to format. This may not be an excellent design. However, the Address class should consider each part of the Address, and some formatting method should focus on how to format the Address correctly.
In listing 9, the code for formatting the address is moved to the interface, implementation class, and factory-to form the habit of "using interfaces. Now, the AddressFormatUtils class is responsible for creating the formatting method and formatting the Address. Any other object can now use Address without worrying about obtaining the definition of the formatting method.

Listing 9. good habits of loose coupling between objects

The code is as follows:


Interface AddressFormatter
{
Public function format ($ addressLine1, $ addressLine2, $ city, $ state,
$ PostalCode, $ country );
}
Class MultiLineAddressFormatter implements AddressFormatter
{
Public function format ($ addressLine1, $ addressLine2, $ city, $ state,
$ PostalCode, $ country)
{
Return sprintf ("% s \ n % s, % s \ n % s ",
$ AddressLine1, $ addressLine2, $ city, $ state, $ postalCode, $ country );
}
}
Class InlineAddressFormatter implements AddressFormatter
{
Public function format ($ addressLine1, $ addressLine2, $ city, $ state,
$ PostalCode, $ country)
{
Return sprintf ("% s, % s, % s ",
$ AddressLine1, $ addressLine2, $ city, $ state, $ postalCode, $ country );
}
}
Class AddressFormatUtils
{
Public static function formatAddress ($ type, $ address)
{
$ Formatter = AddressFormatUtils: createAddressFormatter ($ type );
Return $ formatter-> format ($ address-> getAddressLine1 (),
$ Address-> getAddressLine2 (),
$ Address-> getCity (), $ address-> getState (),
$ Address-> getPostalCode (),
$ Address-> getCountry ());
}
Private static function createAddressFormatter ($ type)
{
If ($ type = "inline "){
$ Formatter = new InlineAddressFormatter ();
} Else if ($ type = "multiline "){
$ Formatter = new MultilineAddressFormatter ();
} Else {
$ Formatter = new NullAddressFormatter ();
}
Return $ formatter;
}
}
$ Addr = new Address ();
$ Addr-> setAddressLine1 ("123 Any St .");
$ Addr-> setAddressLine2 ("Ste 200 ");
$ Addr-> setCity ("Anytown ");
$ Addr-> setState ("AY ");
$ Addr-> setPostalCode ("55555-0000 ");
$ Addr-> setCountry ("US ");
Echo (AddressFormatUtils: formatAddress ("multiline", $ addr ));
Echo ("\ n ");
Echo (AddressFormatUtils: formatAddress ("inline", $ addr ));
Echo ("\ n ");
?>




Of course, the disadvantage is that as long as the mode is used, it usually means that the number of artifacts (classes, files) will increase. However, reducing the maintenance in each class can make up for this defect, and even reduce the number of artifacts when obtaining the correct reusability.


You are rubber; I am glue
OO designs with high cohesion are centralized and organized into relevant modules. Understanding the "focus" is important for deciding how to closely connect functions and classes.
Bad habit: reduce cohesion
When the design cohesion is low, it cannot organize classes and methods well. The term spaghetti code is generally used to describe classes and methods bundled together with low cohesion. Listing 10 provides examples of pasta-style code. The general Utils class uses many different objects and has many dependencies. It performs many operations, so it is difficult to achieve reuse.

List 10. bad habits of reducing cohesion

The code is as follows:


Class Utils
{
Public static function formatAddress ($ formatType, $ address1,
$ Address2, $ city, $ state)
{
Return "some address string ";
}
Public static function formatPersonName ($ formatType, $ givenName,
$ FamilyName)
{
Return "some person name ";
}
Public static function parseAddress ($ formatType, $ val)
{
// Real implementation wocould set values, etc...
Return new Address ();
}
Public static function parseTelephoneNumber ($ formatType, $ val)
{
// Real implementation wocould set values, etc...
Return new TelephoneNumber ();
}
}
?>




Good habit: use high cohesion
High cohesion refers to grouping associated classes and methods together. If both methods and classes have a high degree of cohesion, you can easily break down the entire group without affecting the design. A design with high cohesion will provide the opportunity to reduce coupling. Listing 11 shows two methods that are better organized into the class. The AddressUtils class contains methods used to process the Address class, showing the high cohesion between the methods related to the Address. Similarly, PersonUtils will include methods specifically used to process Person objects. The coupling of these two new classes with highly cohesive methods is very low because they can be used completely independently.

Listing 11. good habits of high cohesion

The code is as follows:


Class AddressUtils
{
Public static function formatAddress ($ formatType, $ address1,
$ Address2, $ city, $ state)
{
Return "some address string ";
}
Public static function parseAddress ($ formatType, $ val)
{
// Real implementation wocould set values, etc...
Return new Address ();
}
}
Class PersonUtils
{
Public static function formatPersonName ($ formatType, $ givenName,
$ FamilyName)
{
Return "some person name ";
}
Public static function parsePersonName ($ formatType, $ val)
{
// Real implementation wocould set values, etc...
Return new PersonName ();
}
}
?>




Restricted propagation
I often mention to members of my software team (where I am a technical director or architect) that the biggest enemy of OO language is copying and pasting. When used in the absence of a pre-OO design, no operations are as destructive as they are between classes. At any time, if you want to copy code from a class to the next class, stop and consider how to use a class hierarchy to take advantage of similar or identical features. In most cases, after using a good design, you will find that it is completely unnecessary.
Bad habit: Do not use class hierarchies
Listing 12 shows a simple example of partial classification. They start with repeated fields and methods-in the long run, it is not conducive to application changes. If the Person class has a defect, the Employee class may also have a defect because it seems that the implementation is replicated between two classes.

Listing 12. bad habits of not using hierarchies

The code is as follows:


Class Person
{
Private $ givenName;
Private $ familyName;
}
Class Employee
{
Private $ givenName;
Private $ familyName;
}
?>




Inheritance is a difficult habit to start with, because the analysis of building a correct inheritance model usually takes a lot of time. In turn, it takes only a few seconds to build a new implementation using the Ctrl + C and Ctrl + V keys. However, the saved time is usually quickly offset in the maintenance phase, because the application will actually spend a lot of maintenance.
Good habit: Use inheritance
In listing 13, the new Employee class extends the Person class. It inherits all common methods and does not implement them again. In addition, listing 13 shows the usage of abstract methods, shows how to put basic functions into the base class, and how to prevent implementation classes from using specific functions.

Listing 13. good habits of inheritance

The code is as follows:


Abstract class Person
{
Private $ givenName;
Private $ familyName;
Public function setGivenName ($ gn)
{
$ This-> givenName = $ gn;
}
Public function getGivenName ()
{
Return $ this-> givenName;
}
Public function setFamilyName ($ fn)
{
$ This-> familyName = $ fn;
}
Public function getFamilyName ()
{
Return $ this-> familyName;
}
Public function sayHello ()
{
Echo ("Hello, I am ");
$ This-> introduceSelf ();
}
Abstract public function introduceSelf ();
}
Class Employee extends Person
{
Private $ role;
Public function setRole ($ r)
{
$ This-> role = $ r;
}
Public function getRole ()
{
Return $ this-> role;
}
Public function introduceSelf ()
{
Echo ($ this-> getRole (). "". $ this-> getGivenName ()."".
$ This-> getFamilyName ());
}
}
?>




Use mode
Design Pattern refers to the common interaction between objects and methods, and time proves that it can solve some problems. When you consider using design patterns, you need to understand how classes interact. It is a simple way to build classes and their interactions, without repeating the mistakes of others and benefiting from proven designs.
Bad habit: Consider an object at a time
In fact, there is no proper code example to demonstrate how to consider the use mode (although there are a variety of good examples that can be implemented in the display mode ). However, you know that you can only consider one object at a time when the following conditions are met:
The object model is not designed in advance.
Start to write the implementation of a single method without removing most of the models.
In the conversation, you 'd rather talk about implementation than the design pattern name.
Good habit: add objects in the mode at the same time
Generally, you are considering the usage mode when performing the following operations:
Build classes and their interaction operations in advance.
Apply classes by mode.
Use mode names, such as Factory, Singleton, and Facade.
Remove most of the models and start adding implementations.
Bytes ---------------------------------------------------------------------------------------------------------------------------
Conclusion
Developing good OO habits in PHP will help you build more stable, easier to maintain, and more scalable applications. Remember:
Be cautious.
Be a good neighbor.
Avoid seeing Medusa.
Use the weakest link.
You are rubber, I am glue.
Restrict propagation.
Consider the usage mode.
When you develop and apply these habits, you may be surprised to find that the quality of your applications is greatly improved.
Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.