Develop 7 object-oriented good habits _php skills in PHP

Source: Internet
Author: User
Tags error handling inheritance object model php class php programming sprintf

In the early days of PHP programming, PHP code was inherently limited to process-oriented. The process code is characterized by the use of procedures to build application blocks. Procedures provide some degree of reuse by allowing calls between procedures.
However, without object-oriented language constructs, 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 mixed paradigm (a process language that contains pseudo OO design). Using OO constructs in PHP code-such as the ability to define and use classes, the ability to build relationships between using inherited classes, and the ability to define interfaces-makes it easier to build code that conforms to excellent OO practices.
Although not much modular pure process design works well, the benefits of OO design are maintained. Because most of the lifecycle of a typical application is spent on maintenance, code maintenance is an important part of the application lifecycle. and code maintenance is easy to forget during the development process. If there is competition in application development and deployment, long-term maintainability may be relegated to a relatively minor position.
Modularity-One of the key features of excellent OO design-can help with this maintenance. Modularity will help encapsulate changes so that applications can be extended and modified more easily over time.
Overall, although there are more than 7 habits of building OO software, following the 7 habits here can make code conform to basic OO design standards. They will provide you with a firmer foundation on which to build more OO habits and build software that can be easily maintained and extended. These habits are designed to address several key features of modularity. For more information on the advantages of language-independent OO design, see resources.
7 Excellent PHP OO habits include:
Remain humble.
Be a good neighbour.
Avoid seeing Medusa.
Take advantage of the weakest link.
You are an eraser, I am a glue.
Restrict propagation.
Consider usage patterns.
stay humble.
Staying humble means avoiding exposing yourself in class implementations and function implementations. Hiding your information is a basic habit. If you can't get into the habit of hiding the details, it's hard to develop any other habits. Information hiding is also known as encapsulation.
There are a number of reasons why it is a bad habit to expose public fields directly, and the most important reason is that you don't have the choice to implement the changes. Isolate changes using OO concepts, while encapsulation plays an integral role in ensuring that changes are not inherently viral (viral) changes. A viral change is a small change at first-such as changing an array that holds three elements to an array that contains only two elements. Suddenly, you find that you need to change more and more code to accommodate changes that are supposed to be trivial.
An easy way to start hiding information is to keep the fields private and expose them using public access methods, just like windows in your home. Instead of opening the entire wall to the outside, open only one or two windows (I'll introduce more information about the access method in "Good habits: using Public access Methods").
In addition to allowing your implementation to be hidden after a change, using a public access method rather than directly exposing the field will allow you to build on the base implementation, by overriding the implementation of the access method to perform a slightly different behavior from the parent method. It also allows you to build an abstract implementation that delegates the actual implementation to the class that overrides the basic implementation.
bad habits: exposing public fields
In the bad code example in Listing 1, the field of the person object is exposed directly to the public field rather than to the access method. While this behavior is tempting, especially for lightweight data objects, it will limit you.
Listing 1. The bad habit of exposing public fields

Copy Code code as follows:

<?php
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 needs to be changed as well. For example, if a person's Christian name, surname, and other names are encapsulated in a PersonName object, you need to modify all the code to accommodate the change.
good habits: Using public access Methods
By using good OO habits (see Listing 2), the same object now has private fields instead of public fields, and it is prudent to expose private fields to the outside world by means of get and set public methods called access methods. These access methods now provide a common way to get information from a PHP class, so that when the implementation changes, the need for changing all the code that uses the class is likely to become smaller.
Listing 2. Good habits of using public access methods
Copy Code code as follows:

<?php
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 might do a lot of work, and it might actually be more of a front-end work. However, in general, using good OO habits is very cost-effective in the long run because future changes will be greatly consolidated.
In the code version shown in Listing 3, I have changed the internal implementation to use the associative array of the name part. Ideally, I would like to have error handling and examine the presence of the element more carefully, but the purpose of this example is to show that the code that uses my class does not need to change-the code does not detect that the class has changed. Remember that the reason for using OO habits is to be careful about encapsulating changes so that the code is more scalable and easier to maintain.
Listing 3. Another example of using different internal implementations

Code
Copy Code code as follows:

<?php
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 the ' to the ' person class.
*/
$person = new Person ();
$person->setprefix ("Mr.");
$person->setgivenname ("John");
Echo ($person->getprefix ());
Echo ($person->getgivenname ());
?>



be a good neighbor .
When building a class, it should correctly handle its own errors. If the class does not know how to handle errors, the errors should be encapsulated in a format understood by their callers. Also, avoid returning empty objects or objects with invalid states. Many times, you can do this by simply checking the parameters and throwing a specific exception that explains why the arguments are not valid. As you develop this habit, it can save you a lot of time by helping you-and the people who maintain the code or use the object.
bad habits: not Handling Errors
Consider the example shown in Listing 4, which takes some parameters and returns a person object populated with some values. However, in the Parsepersonname () method, there is no validation of whether the supplied $val variable is empty, is a 0-length string, or whether the string uses an unresolved format. The Parsepersonname () method does not return a person object, but returns null. Administrators or programmers who use this approach may find it troublesome-at least they now need to start setting breakpoints and debugging PHP scripts.

listing 4. Bad habit of not throwing or handling errors

Copy Code code 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, a
$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 of the if condition to ensure that a valid person object is always obtained. However, you get a person without the set attribute, which still does not improve your predicament very well.
Good habit: Each module handles its own mistakes
Do not let the caller speculate in a vacuum, but validate the parameters beforehand. If a variable is not set to produce a valid result, check the variable and throw the 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 an exception and some new conditions in the Parseperson () method that demonstrates some basic validations.

Listing 5. Good habit of throwing mistakes
Copy Code code as follows:

<?php
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 want people to be able to use your class without having to understand how it works. If they use a method that is incorrect or not used in the desired manner, there is no need to guess why it is not working. As a good neighbor, you need to know that people who reuse your classes are not psychic, so you need to solve guessing problems.

Avoid seeing Medusa
When I first learned about OO concepts, I was very skeptical about whether the interface really helped. My colleague gave me an analogy, saying that it was like seeing Medusa's head without using an interface. In Greek mythology, Medusa is a female monster with a long snake hair. Anyone who looks at her will turn into a stone. Percy Hughes, who killed Medusa, was able to confront her by observing her shadow on the shield and avoiding turning into a stone.
The interface is a mirror to the Medusa. When you use a specific implementation, the code must also change as the implementation code changes. The direct use implementation will limit your choice because you have essentially turned the class into a "stone."
bad habits: Do not use interfaces
Listing 6 shows an example of loading a person object from a database. It takes the name of the person and returns the matching people object in the database.

listing 6. Bad habit of not using interfaces
Copy Code code as follows:

<?php
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 ());
?>



Code that loads person from the database will work correctly until the environment changes. For example, loading a person from a database might be appropriate for the first version of the application, but for the second version, you might want to add functionality from the WEB service loader. In fact, the class has become "stone" because it uses the implementation class directly and has very limited changes that can be made now.
Good habits: Using interfaces
Listing 7 shows a code example that does not change after implementing a new method that loads a user. The example shows an interface named Personprovider that declares a single method. If any code uses Personprovider, the Code prohibits using the implementation class directly. Instead, it uses personprovider as if it were an actual object.

Listing 7. Good habits of using interfaces
Copy Code code as follows:

<?php
Interface Personprovider
{
Public Function Getperson ($givenName, $familyName);
}
Class Dbpersonprovider implements Personprovider
{
Public Function Getperson ($givenName, $familyName)
{
/* Pretend 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 an interface, try to avoid directly referencing the implementation class. Instead, using content outside of an object can provide the correct implementation. If your class will load an implementation based on some logic, it still needs to get the definition of all the implementation classes, and that does not achieve any effect.
You can use the Factory mode to create an instance of an implementation class that implements an interface. By convention, the factory method starts with Create and returns an interface. It can get the necessary parameters for your factory to figure out which implementation class should be returned.
In Listing 7, the Createprovider () method simply gets the $type. If the $type is set to database, the factory returns an instance of Dbpersonprovider. Any new implementations that load people from the database do not require any changes in the classes that use the factories and interfaces. Dbpersonprovider will implement the Personprovider interface and have the actual implementation of the Getperson () method.

take advantage of the weakest link
It's a good thing to have modules loosely coupled together; it is one of the properties that allows you to encapsulate changes. The other two habits-"stay cautious" and "avoid seeing Medusa"-can help you build loosely coupled modules. To implement loosely coupled classes, you can develop a habit of reducing class dependencies.
Bad habits: tight coupling
In Listing 8, reducing dependencies does not necessarily reduce the dependency of the client that uses the object. Instead, the example demonstrates how to reduce the dependency on the correct class and minimize the dependency relationship.

Listing 8. The bad habit of tight coupling in address

Copy Code code as follows:

<?php
Require_once "./addressformatters.php";
Class Address
{
Private $addressLine 1;
Private $addressLine 2;
Private $city;
Private $state; Or province ...
Private $postalCode;
Private $country;
Public Function SetAddressLine1 ($line 1)
{
$this->addressline1 = $line 1;
}
/* 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 that calls the format () method on the Address object might look great-this code does this by using the address class, calling format () and completing it. Instead, the address class is less fortunate. It needs to understand the various formatting methods that are used for the correct formatting, which may make the address object less well reused by others, especially if others are not interested in using the formatting method class in the format () method. Although the code using address does not have a lot of dependencies, the address class has a lot of code, and it's probably just a simple data object.
The address class is tightly coupled to the implementation class that knows how to format the Address object.
Good habits: loosely coupled between objects
When building a good OO design, you must consider the concept known as separation of concerns (separation of CONCERNS,SOC). SoC refers to the attempt to isolate the object through the content that is really concerned, thereby reducing the degree of coupling. In the original address class, it must focus on how to format. This may not be a good design. However, the address class should take into account the parts of addressing, and some formatting should focus on how to properly format the addresses.
In Listing 9, the code that formats the address is moved to interfaces, implementation classes, and factories-to develop the habit of "using interfaces." The Addressformatutils class is now responsible for creating the formatting method and formatting the address. Any other object can now use address without fear of requiring the definition of a format method.

Listing 9. A good habit of loosely coupling between objects
Copy Code code as follows:

<?php
Interface Addressformatter
{
Public function Format ($addressLine 1, $addressLine 2, $city, $state,
$postalCode, $country);
}
Class Multilineaddressformatter implements Addressformatter
{
Public function Format ($addressLine 1, $addressLine 2, $city, $state,
$postalCode, $country)
{
Return sprintf ("%s\n%s\n%s,%s%s\n%s",
$addressLine 1, $addressLine 2, $city, $state, $postalCode, $country);
}
}
Class Inlineaddressformatter implements Addressformatter
{
Public function Format ($addressLine 1, $addressLine 2, $city, $state,
$postalCode, $country)
{
Return sprintf (%s%s,%s,%s%s,%s),
$addressLine 1, $addressLine 2, $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");
?>



The downside, of course, is that as long as the pattern is used, it usually means that the number of artifacts (classes, files) increases. However, by reducing maintenance in each class, you can compensate for this shortcoming, and even reduce the amount of artifacts when you get the right reusability.


you are the eraser; I'm the glue.
The highly cohesive OO design is concentrated and organized into related modules. Understanding "concerns" is important for deciding how to closely connect functions and classes.
bad habits: lower cohesion
When design cohesion is low, it cannot organize classes and methods well. The word Spaghetti (spaghetti code) is often used to describe classes and methods that are bundled together and have low cohesion. Listing 10 provides an example of spaghetti code. The relatively generic Utils class will use many different objects and have many dependencies. It does a lot of work, making it difficult to reuse.

listing 10. A bad habit of reducing cohesion

Copy Code code as follows:

<?php
Class Utils
{
public static function Formataddress ($formatType, $address 1,
$address 2, $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 would set values, etc ...
Return to new address ();
}
public static function Parsetelephonenumber ($formatType, $val)
{
Real implementation would set values, etc ...
return new telephonenumber ();
}
}
?>



good habits: using high cohesion
High cohesion refers to grouping classes and methods that are interrelated. If both methods and classes are highly cohesive, you can easily decompose the entire group without affecting the design. A highly cohesive design provides the opportunity to reduce coupling. Listing 11 shows the two methods that are better organized into classes. The Addressutils class will contain methods for dealing with the address class, showing a high degree of cohesion between addressing methods. Similarly, personutils will contain methods that specifically deal with the person object. The two new classes with a highly cohesive method have low coupling because they can be used completely independently.

listing 11. Good habits of high cohesion
Copy Code code as follows:

<?php
Class Addressutils
{
public static function Formataddress ($formatType, $address 1,
$address 2, $city, $state)
{
Return "some address string";
}
public static function Parseaddress ($formatType, $val)
{
Real implementation would set values, etc ...
Return to new address ();
}
}
Class Personutils
{
public static function Formatpersonname ($formatType, $givenName,
$familyName)
{
Return ' some person name ';
}
public static function Parsepersonname ($formatType, $val)
{
Real implementation would set values, etc ...
return new PersonName ();
}
}
?>



Restricting propagation
I often mention to members of my software team (where I am a technical director or architect) that the most common enemy of OO language is copy and paste operations. When used in the absence of an OO design, nothing is as destructive as copying code between classes. Whenever you want to copy code from one class to the next, stop and consider how you can use a class hierarchy to take advantage of similar functionality or the same functionality. In most cases, with a good design, you'll find it completely unnecessary to copy the code.
Bad habits: Do not use class hierarchies
Listing 12 shows a simple example of some of the classes. They start with repeated fields and methods--in the long run, are not conducive to application changes. If there is a flaw in the person class, there is likely to be a flaw in the Employee class because it appears that the implementation is replicated between two classes.

listing 12. Bad habits that don't use hierarchies
Copy Code code as follows:

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



Inheritance is a difficult habit to start with, because it usually takes a lot of time to build an analysis of the correct inheritance model. Conversely, using CTRL + C and CTRL + V keys to build a new implementation takes only a few seconds. But the amount of time saved is usually quickly offset in the maintenance phase, because the application will actually cost a lot of maintenance.
good habits: using inheritance
In Listing 13, the new Employee class expands the person class. It now inherits all the common methods and does not implement them again. In addition, listing 13 shows the use of abstract methods, demonstrating how to put basic functionality into a base class and how to prevent the implementation class from using specific functions.

listing 13. Using the good habits of inheritance
Copy Code code as follows:

<?php
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 ());
}
}
?>



consider usage patterns
Design patterns refer to common interactions between objects and methods, and time proves that they 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 having to repeat the mistakes of others and benefit from proven design.
Bad habit: Consider one object at a time
In fact, there is no appropriate code example to demonstrate how to consider using patterns (although there are plenty of good examples to display schema implementations). However, generally speaking, you know that only one object can be considered at a time when the following conditions are met:
The object model will not be designed in advance.
Start writing a single method implementation without removing most of the models.
Instead of using a design pattern name in a conversation, you would rather talk about implementation.
good habits: adding objects in a pattern at the same time
In general, you are considering using patterns when you do the following:
Build classes and their interactions in advance.
Apply a class based on the pattern.
Use pattern names, such as Factory, Singleton, and facades.
Remove most of the models and start adding implementations.
--------------------------------------------------------------------------------------------------------------- ------------
Concluding remarks
Developing good OO habits in PHP will help you build more stable, maintainable, and easily scalable applications. Remember:
Remain cautious.
Be a good neighbour.
Avoid seeing Medusa.
Take advantage of the weakest link.
You are rubber, I am glue.
Restrict propagation.
Consider usage patterns.
When you develop and apply these habits, you are likely to be surprised to discover the quality of the application leap.

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.