If you are not already planning to create applications using OO principles, these 7 habits will help you start transitioning between procedural and OO programming using PHP's object-oriented (OO) language features.
In the early days of PHP programming, PHP code was inherently limited to being procedural. Procedural 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. Doing this is a bit difficult and makes the code harder to read because it is a mixed paradigm (procedural language with pseudo-OO design). Using OO constructs in PHP code—such as the ability to define and use classes, the ability to construct relationships between classes using inheritance, and the ability to define interfaces—makes it easier to build code that adheres to good OO practices.
Although purely procedural designs without excessive modularity work well, the advantages of OO design show up in maintenance. Since most of a typical application's life cycle is spent on maintenance, code maintenance is an important part of the application life cycle. And code maintenance can easily be forgotten during development. If there is competition for application development and deployment, then long-term maintainability may take a back seat.
Modularity — one of the key features of good OO design — can help with such maintenance. Modularity will help encapsulate changes so that the application can be expanded and modified more easily over time.
In summary, while there are more than 7 habits for building OO software, following these 7 habits will keep your code compliant with basic OO design standards. They will give you a stronger foundation on which to build more OO habits and build software that can be easily maintained and extended. These habits target several key features of modularity. For more information about the advantages of language-independent OO design, see Resources.
7 good PHP OO habits include:
Stay humble.
Be a good neighbor.
Avoid seeing Medusa.
Exploit the weakest link.
You are the rubber; I am the glue.
Limit the spread.
Consider using patterns.
Stay humble
Staying modest means avoiding exposing yourself in class implementation and function implementation. Hiding your information is a basic habit. If you can't get into the habit of hiding implementation details, it will be difficult to get into any other habit. Information hiding is also called encapsulation.
Exposing public fields directly is a bad practice for many reasons, the most important of which is that it leaves you with less choice in implementing changes. Changes are isolated using OO concepts, and encapsulation plays an integral role in ensuring that changes are not viral in nature. Viral changes are changes that start out small—such as changing an array holding three elements to an array containing only two elements. Suddenly, you find that you need to change more and more code to accommodate what should be a trivial change.
A simple way to start hiding information is to keep fields private and expose them with public access methods, like windows in your home. Rather than leaving the entire wall open to the outside, just open a window or two (I’ll cover more about access methods in “Good Practice: Using Public Access Methods”).
In addition to allowing your implementation to hide behind changes, using public access methods instead of directly exposing fields will allow you to build on the base implementation by overriding the access method's implementation to perform something slightly different than the parent method Behavior. It also allows you to build an abstract implementation such that the actual implementation is delegated to a class that overrides the base implementation.
Bad Habit: Exposing Public Fields
In the bad code example in Listing 1, the fields of the Person object are exposed directly as public fields rather than using access methods. Although this behavior is tempting, especially for lightweight data objects, it will impose limitations on you.
Listing 1. Bad habit of exposing public fields
Copy code 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 an object changes in any way, all code that uses that object needs to change as well. For example, if a person's given name, last name, and other first names were encapsulated into a PersonName object, all code would need to be modified to accommodate the changes.
Good Habit: Use Public Access Methods
By using good OO habits (see Listing 2), the same object now has private fields instead of public fields, and the get and set public methods are discreetly called access methods Expose private fields to the outside world. These access methods now provide a public way to obtain information from a PHP class, so that when the implementation changes, there is likely to be less need to change all code that uses the class.
Listing 2. Good habit of using public access methods
Copy the code 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 seem like a lot of work, and may actually be more of a front-end job. However, generally, using good OO habits pays off in the long run because future changes will be greatly reinforced.
In the version of the code shown in Listing 3, I have changed the internal implementation to use an associative array of name parts. Ideally, I'd like to have error handling and check more carefully for element presence, but the point of this example is to show how much the code using my class doesn't need to change - the code is unaware that the class has changed. The reason to remember to adopt OO habits is to carefully encapsulate changes so that the code will be more scalable and easier to maintain.
Listing 3. Another example using different internal implementations
Copy the 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 building a class, it should handle its own errors correctly. If the class does not know how to handle errors, it should encapsulate them in a format that its caller understands. Additionally, avoid returning empty objects or objects with invalid state. Many times this can be achieved simply by validating the parameters and throwing a specific exception explaining why the supplied parameter is invalid. When you get into this habit, it can save you — and the people who maintain the code or use the objects — a lot of time.
Bad Habit: Not Handling Errors
Consider the example shown in Listing 4, which will accept some parameters and return a Person object filled with some values. However, in the parsePersonName() method, there is no verification that the provided $val variable is empty, a zero-length string, or that the string is in an unparsable format. The parsePersonName() method does not return a Person object, but returns null. Administrators or programmers using this approach may find it troublesome - at least they need to start setting breakpoints and debugging PHP scripts now.
Listing 4. Bad habit of not throwing or handling errors
Copy code 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;
}
}
Listing 4 The parsePersonName() method can be modified to initialize the Person object outside the if condition, ensuring that a valid Person object is always obtained. However, what you get is a Person without a set property, which still doesn't improve your dilemma very much.
Good practice: each module handles its own errors
Don’t let the caller guess, but pre-validate the parameters. If an unset variable cannot produce a valid result, check the variable and throw an 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 as well as some new conditions in the parsePerson() method that demonstrates some basic validation.
Listing 5. Good habits for throwing errors
Copy the code 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 hope that people can use your class without having to understand How it works. If they're using something incorrectly or not in the way expected, there's no need to guess why it's not working. As a good neighbor, you need to know that people reusing your classes don't have special features, so you need to solve the problem of guessing.
Avoid seeing Medusa
When I first learned about OO concepts, I was very skeptical that interfaces would really help. My colleague gave me an analogy, saying that not using interfaces is like seeing the head of Medusa. In Greek mythology, Medusa is a snake-haired monster. Anyone who looks at her will turn to stone. Perseus, who killed Medusa, was able to fight her by avoiding turning to stone by observing her shadow on his shield.
The interface is the mirror to deal with Medusa. When you use a specific concrete implementation, the code must also change as the implementation code changes. Using the implementation directly will limit your options because you have essentially turned the class into "stone".
Bad Habit: Not Using Interfaces
Listing 6 shows an example of loading a Person object from the database. It will get the person's name and return the matching Person object from the database.
Listing 6. Bad habits of not using interfaces
Copy code 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, from The code that loads Person into the database can run normally. For example, loading a Person from a database might work for the first version of the application, but for a second version, you might want to add the ability to load Persons from a Web service. In fact, the class has turned to "stone" because it is using the implementation class directly and can now make very limited changes.
Good practice: Use interfaces
Listing 7 shows a code example that has not been changed after implementing the new method of loading users. The example shows an interface named PersonProvider that declares a single method. If any code uses PersonProvider, the code is prohibited from using the implementation class directly. Instead, it uses the PersonProvider as if it were an actual object.
List 7. Good habits for using interfaces
Copy the code 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());
?>
在使用接口时,尝试避免直接引用实现类。相反,使用对象外部的内容可以提供正确的实现。如果您的类将装入基于某些逻辑的实现,它仍然需要获取所有实现类的定义,并且那样做也无法取得任何效果。
您可以使用 Factory 模式来创建实现接口的实现类的实例。根据约定,factory 方法将以 create 为开头并返回接口。它可以为您的 factory 获取必要的参数以计算出应当返回哪个实现类。
在清单 7 中,createProvider() 方法只是获取 $type。如果 $type 被设为 database,工厂将返回 DBPersonProvider 的实例。从数据库中装入人员的任何新实现都不要求在使用工厂和接口的类中进行任何更改。DBPersonProvider 将实现 PersonProvider 接口并且拥有 getPerson() 方法的实际实现。
利用最弱的链接
将模块松散耦合 在一起是件好事情;它是允许您封装更改的属性之一。另外两个习惯 — “保持谨慎” 和 “避免看到美杜莎” — 可帮助您构建松散耦合的模块。要实现松散耦合的类,可通过养成降低类依赖关系的习惯实现。
坏习惯:紧密耦合
在清单 8 中,降低依赖关系并不是必须降低使用对象的客户机的依赖关系。相反,该示例将演示如何降低与正确类的依赖关系并最小化这种依赖关系。
清单 8. Address 中紧密耦合的坏习惯
复制代码 代码如下:
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");
?>
Code that calls the format() method on an Address object might look great — what this code does is take the Address class, call format() and be done with it. In contrast, the Address class is not so lucky. It requires knowledge of the various formatting methods for correct formatting, which may make the Address object less reusable by others, especially if others have no interest in using the formatting method class in the format() method . While the code that uses Address doesn't have many dependencies, the Address class does have a lot of code, and it might just be a simple data object.
The Address class is tightly coupled to an implementation class that knows how to format Address objects.
Good Habit: Loosely Couple Between Objects
When building a good OO design, you must consider a concept called Separation of Concerns (SoC). SoC refers to the attempt to reduce coupling by separating objects by what they really care about. In the original Address class, it had to be concerned with how to format it. This may not be good design. However, the Address class should consider the parts of the Address, and some formatting method should focus on formatting the address correctly.
In Listing 9, the code for formatting addresses has been moved into interfaces, implementation classes, and factories—to get in the habit of “using interfaces.” Now, the AddressFormatUtils class is responsible for creating the formatting methods and formatting the Address. Any other object can now use Address without having to worry about asking for the definition of the formatting method.
Listing 9. Good practice for loose coupling between objects
Copy code The code is as follows:
php
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("%sn%sn%s, %s %sn%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, %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 you use patterns, usually This means that the number of artifacts (classes, files) will increase. However, this shortcoming can be compensated for by reducing maintenance in each class, and even reducing the amount of artifacts when getting the reusability right.
You are the rubber; I am the glue
Highly cohesive OO designs are focused and organized into relevant modules. Understanding "concerns" is important in deciding how closely functions and classes should be related.
Bad Habit: Reduced Cohesion
When a design has low cohesion, it does not organize classes and methods well. The term 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. A relatively general Utils class will use many different objects and have many dependencies. It performs a lot of operations, making it difficult to reuse.
Listing 10. Bad habits that reduce cohesion
Copy code 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 would set values, etc...
return new Address();
}
public static function parseTelephoneNumber($formatType, $val)
{
// real implementation would set values, etc...
return new TelephoneNumber();
}
}
?>
Good practice: Take advantage of high cohesion
High cohesion means grouping related classes and methods together. If both methods and classes are highly cohesive, the entire group can be easily broken down without affecting the design. Designs with high cohesion will provide opportunities to reduce coupling. Listing 11 shows two methods that are better organized into classes. The AddressUtils class will contain methods for working with the Address class, showing a high degree of cohesion between address-related methods. Likewise, PersonUtils will contain methods specifically for working with Person objects. These two new classes with highly cohesive methods are very low coupling because they can be used completely independently.
List 11. Good habits for high cohesion
Copy the code 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 would 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 would set values, etc...
return new PersonName();
}
}
?>
Limit spread
I often mention to members of my software teams (where I work as a technical lead or architect) that the biggest enemy of OO languages is copy and paste operations . When used without prior OO design, nothing is as disruptive as copying code between classes. Whenever you want to copy code from one class to the next, stop and consider how you can use class hierarchy to take advantage of similar or identical functionality. In most cases, with good design, you'll find that duplicating code is completely unnecessary.
Bad Habit: Not Using Class Hierarchies
Listing 12 shows a simple example of partial classes. They start with duplicate fields and methods - not conducive to application changes in the long run. If there is a flaw in the Person class, there is most likely a flaw in the Employee class, since it appears as if the implementation is copied between the two classes.
Listing 12. Bad habit of not using hierarchy
Copy code The code is as follows:
class Person
{
private $givenName;
private $familyName;
}
class Employee
{
private $givenName;
private $familyName;
}
?>
Inheritance is a difficult habit to get into because the analysis to build the correct inheritance model usually takes a lot of time. In turn, building a new implementation using Ctrl+C and Ctrl+V only takes a few seconds. But this time savings is often quickly offset during the maintenance phase, as the application will actually cost a lot to maintain.
Good practice: Leverage inheritance
In Listing 13, the new Employee class extends the Person class. It will now inherit all common methods and not reimplement them. Additionally, Listing 13 shows the use of abstract methods, demonstrating how to put basic functionality into a base class and how to prevent implementation classes from using specific functions.
Listing 13. Good habits of using inheritance
Copy the code 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());
}
}
?>
考虑使用模式
设计模式指对象和方法的常见交互,并且时间证明它可以解决某些问题。当您考虑使用设计模式时,您就需要了解类之间如何进行交互。它是构建类及其交互操作的简单方法,无需重蹈他人的覆辙,并从经过证明的设计中获益。
坏习惯:一次考虑一个对象
实际上没有适当的代码示例可以演示如何考虑使用模式(尽管有丰富的优秀示例可以显示模式实现)。但是,一般而言,您知道在满足以下条件时一次只能考虑一个对象:
不会提前设计对象模型。
开始编写单一方法的实现,而无需去掉大部分模型。
在交谈中不使用设计模式名而宁愿谈论实现。
好习惯:同时添加模式中形成的对象
一般而言,当您在执行以下操作时就是在考虑使用模式:
提前构建类及其交互操作。
根据模式套用类。
使用模式名,如 Factory、Singleton 和 Facade。
去掉大部分模型,然后开始添加实现。
结束语
在 PHP 中养成良好的 OO 习惯将帮助您构建更稳定、更易于维护和更易于扩展的应用程序。记住:
保持谨慎。
做个好邻居。
避免看到美杜莎。
利用最弱的链接。
您是橡皮,我是胶水。
限制传播。
考虑使用模式。
当您养成并应用这些习惯后,您很可能会惊讶地发现应用程序在质量上的飞跃。
http://www.bkjia.com/PHPjc/321168.htmlwww.bkjia.comtruehttp://www.bkjia.com/PHPjc/321168.htmlTechArticle如果您尚未打算用 OO 原则创建应用程序,则使用 PHP 的面向对象(OO)的语言特性,这 7 个习惯将帮助您开始在过程编程与 OO 编程之间进行...