RSS
  • 首页
  • 关于

[译]PHP面向对象入门

2010年7月14日 by admin | 0 Comment »
原创译文,转载请注明出处。原作者为Jason Lengstorf,原文请 单击这里 查看。
对很多PHP程序员来说,面向对象编程是一个令人畏惧的概念,充满着复杂的语法以及其它障碍。正如我在我的《Pro PHP and jQuery》这本书中所详细描述的那样,您将学会面向对象编程(OOP),这是一种将相关动作组织在一起从而创造出更紧凑有效的代码的编程风格。

理解面向对象编程

面向对象编程是一种允许开发者们将相似的多个任务组织到类里面的编程风格。这对于保持代码遵循“不要自我重复”(DRY,Don’t Repeat Yourself)这一准则以及保持易维护性是非常有帮助的。

DRY编程的主要好处是,如果一部分信息发生了变化,通常只需要更新一部分代码。开发者们最大的噩梦之一是维护那些数据被反复地声明的代码,这就意味着对程序的任何修改都将变成类似于”Where’s Waldo?”那样的令人无限沮丧的解谜游戏,因为数据和功能都是重复的。

OOP对于很多程序员来说是有点令人畏惧的,因为它引入了新的语法,而且乍一看去,比简单的面向过程或者内联的代码更为复杂。然而,仔细审视一番后你会发现,OOP是一种非常简洁并且极为简单的编程方法。

理解对象和类

在深入了解OOP的细节之前,必须对类和对象之前的区别有一个大概的理解。本章节将讲解的是构建类的基础、它们的不同能力以及它们的一些用途。

你马上就会觉得OOP有点让你犯糊涂:经验丰富的开发者们在谈论对象和类,听起来似乎这二者是可以互换的。然而事实并非如此。二者的巨大区别可能会让你伤透脑筋。

例如,类,就像是房屋的设计图。它在纸上定义好了房屋的形状,各个部分之间的关系构造也都明确地设计好了,尽管这房屋事实上并不存在。

对象,就像是一栋真正的房屋,是根据设计图建造出来的。对象中存储的数据就像是建造房屋的木材、电线以及混凝土。

如果没有依照设计图组织起来,它们就只是一堆东西而已。但是当它们被组织到一起的时候,就成了一栋结构化的有用的房屋。

类,产生数据结构和相关操作,并使用这些信息来构建对象。同一时间内,同一个类可以被用来构建多个独立的对象。继续用我们刚才的那种建筑比喻,从一份设计图可以建造不同的房屋:它们外表看起来一样,但住在里面的人不同,里面的装修和家具也不同。

类结构

创建类的语法是很简洁的:使用class关键字来声明一个类,然后给出类的名称,以及一对花括号({}):

<?php
class MyClass
{
    // Class properties and methods go here
}
?>

创建类以后,使用new关键字,这个新的类就可以被实例化并存储到变量之中:

$obj = new MyClass;

要显示这个类的内容,使用var_dump()函数:

var_dump($obj);

您可以试着将以上代码写到一个新的文件,命名为test.php,保存到您的本地测试文件夹中:

<?php
 
class MyClass
{
	// Class properties and methods go here
}
 
$obj = new MyClass;
 
var_dump($obj);
 
?>

在您的浏览器中打开这个页面,地址是http://localhost/test.php,将会显示以下内容:

object(MyClass)#1 (0) { }

以这种最简单形式,您完成了您的第一个面向对象脚本。

定义类的属性

为了给类添加数据,通常要使用属性或者类特定变量。它们就像常规的变量一样工作,区别就在于,它们被绑定到对象,因此就只能通过对象来访问。
为了给MyClass添加属性,请添加以下代码:

<?php
 
class MyClass
{
    public $prop1 = "I'm a class property!";
}
 
$obj = new MyClass;
 
var_dump($obj);
 
?>

这里的public关键字决定了这项属性的可见性,这在接下来的段落中会讲到。然后,使用标准的变量语法来命名这项属性,并且给变量赋值(尽管类的属性是不需要初始值的)。
要读取这项属性并输出到浏览器,您要引用相应的对象才能读取这一属性的值:

echo $obj->prop1;

因为同一个类可能会生成多个实例,如果不引用单独的实例,程序将无法决定要读取哪一个对象的属性。箭头(->)是一种OOP结构,作用是访问给出的对象中所包含的属性和方法。
参照下列代码来修改 test.php 即可读取出这项属性值,而不需要将整个类都读取出来:

<?php
 
class MyClass
{
    public $prop1 = "I'm a class property!";
}
 
$obj = new MyClass;
 
echo $obj->prop1; // Output the property
 
?>

刷新您的浏览器,将显示以下内容:

I'm a class property!

定义类的方法

方法是指类特定的函数。类所能实现的单个操作是通过方法的形式在类中定义的。
例如,要创建能够设置和修改类属性$prop1的方法,可以添加如下代码:

<?php
 
class MyClass
{
    public $prop1 = "I'm a class property!";
 
    public function setProperty($newval)
    {
        $this->prop1 = $newval;
    }
 
    public function getProperty()
    {
        return $this->prop1 . "<br />";
    }
}
 
$obj = new MyClass;
 
echo $obj->prop1;
 
?>

注意——OOP允许对象通过$this来引用它们自己。在方法内部也同样是使用$this,但是在类的外部,您必须使用对象的名称。
就像普通函数一样去调用这些方法,但是首先必须先引用它们所属的对象。通过以下代码您就可以实现读取MyClass的属性,修改其属性值,再读取出来:

<?php
 
class MyClass
{
    public $prop1 = "I'm a class property!";
 
    public function setProperty($newval)
    {
        $this->prop1 = $newval;
    }
 
    public function getProperty()
    {
        return $this->prop1 . "<br />";
    }
}
 
$obj = new MyClass;
 
echo $obj->getProperty(); // Get the property value
 
$obj->setProperty("I'm a new property value!"); // Set a new one
 
echo $obj->getProperty(); // Read it out again to show the change
 
?>

刷新您的浏览器,您将看到以下输出结果:

I'm a class property!
I'm a new property value!

“当您使用一个类的多个实例时,OOP的好处就变得显而易见。”

<?php
 
class MyClass
{
    public $prop1 = "I'm a class property!";
 
    public function setProperty($newval)
    {
        $this->prop1 = $newval;
    }
 
    public function getProperty()
    {
        return $this->prop1 . "<br />";
    }
}
 
// Create two objects
$obj = new MyClass;
$obj2 = new MyClass;
 
// Get the value of $prop1 from both objects
echo $obj->getProperty();
echo $obj2->getProperty();
 
// Set new values for both objects
$obj->setProperty("I'm a new property value!");
$obj2->setProperty("I belong to the second instance!");
 
// Output both objects' $prop1 value
echo $obj->getProperty();
echo $obj2->getProperty();
 
?>

你刷新浏览器后应该就可以看到以下输出结果:

I'm a class property!
I'm a class property!
I'm a new property value!
I belong to the second instance!

正如您所看到的那样,OOP使对象成为单独的实体,这使得分离不同的代码段更加容易。

OOP中的魔术方法

为了更方便地利用对象,PHP也提供了一系列的魔术方法,或者是一些在对象执行特定操作时所调用的特殊方法。这使得开发者们更容易地实现许多实用的任务。

使用构造函数和析构函数

当一个对象被实例化时,通常需要立刻设置好一些东西。为了解决这一问题,PHP提供了魔术方法 __construct(),这个方法在创建新对象的时候会被自动调用。
为了说明构造函数这一概念,我们给MyClass添加一个构造函数,并在创建新实例的时候输出一条消息:

<?php
 
class MyClass
{
    public $prop1 = "I'm a class property!";
 
    public function __construct()
    {
        echo 'The class "', __CLASS__, '" was initiated!<br />';
    }
 
    public function setProperty($newval)
    {
        $this->prop1 = $newval;
    }
 
    public function getProperty()
    {
        return $this->prop1 . "<br />";
    }
}
 
// Create a new object
$obj = new MyClass;
 
// Get the value of $prop1
echo $obj->getProperty();
 
// Output a message at the end of the file
echo "End of file.<br />";
 
?>

注意:__CLASS__ 返回的是所调用的类的名称。这就是人们所熟知的“魔术常量”。有很多魔术常量可供使用,您可以在PHP的手册中找到它们的资料。
刷新浏览器您将看到以下输出结果:

The class "MyClass" was initiated!
I'm a class property!
End of file.

要在对象被销毁的时候调用一个函数,您可以使用 __destruct() 这个魔术方法。这对于类的清空(例如关闭数据库连接)是非常有用的。

在MyClass中定义魔术方法 __destruct() ,当对象被销毁时输出一条消息:

<?php
 
class MyClass
{
    public $prop1 = "I'm a class property!";
 
    public function __construct()
    {
        echo 'The class "', __CLASS__, '" was initiated!<br />';
    }
 
    public function __destruct()
    {
        echo 'The class "', __CLASS__, '" was destroyed.<br />';
    }
 
    public function setProperty($newval)
    {
        $this->prop1 = $newval;
    }
 
    public function getProperty()
    {
        return $this->prop1 . "<br />";
    }
}
 
// Create a new object
$obj = new MyClass;
 
// Get the value of $prop1
echo $obj->getProperty();
 
// Output a message at the end of the file
echo "End of file.<br />";
 
?>

定义了析构函数之后,程序将输出以下内容:

The class "MyClass" was initiated!
I'm a class property!
End of file.
The class "MyClass" was destroyed.

“当运行到文件末尾处的时候,PHP会自动释放所有资源。”

要显式地触发析构函数,您可以使用 unset() 函数来销毁对象:

<?php
 
class MyClass
{
    public $prop1 = "I'm a class property!";
 
    public function __construct()
    {
        echo 'The class "', __CLASS__, '" was initiated!<br />';
    }
 
    public function __destruct()
    {
        echo 'The class "', __CLASS__, '" was destroyed.<br />';
    }
 
    public function setProperty($newval)
    {
        $this->prop1 = $newval;
    }
 
    public function getProperty()
    {
        return $this->prop1 . "<br />";
    }
}
 
// Create a new object
$obj = new MyClass;
 
// Get the value of $prop1
echo $obj->getProperty();
 
// Destroy the object
unset($obj);
 
// Output a message at the end of the file
echo "End of file.<br />";
 
?>

现在程序的输出结果应该是这样的:

The class "MyClass" was initiated!
I'm a class property!
The class "MyClass" was destroyed.
End of file.

转换为字符串

为了避免程序将MyClass当作字符串来输出而产生错误,另一个魔术方法 __toString() 就派上用场了。
如果没有 __toString(),尝试将对象当作字符串来输出的行为将会导致出现致命错误。以下代码就是尝试用echo来输出对象,而不在适当的地方使用魔术方法:

<?php
 
class MyClass
{
    public $prop1 = "I'm a class property!";
 
    public function __construct()
    {
        echo 'The class "', __CLASS__, '" was initiated!<br />';
    }
 
    public function __destruct()
    {
        echo 'The class "', __CLASS__, '" was destroyed.<br />';
    }
 
    public function setProperty($newval)
    {
        $this->prop1 = $newval;
    }
 
    public function getProperty()
    {
        return $this->prop1 . "<br />";
    }
}
 
// Create a new object
$obj = new MyClass;
 
// Output the object as a string
echo $obj;
 
// Destroy the object
unset($obj);
 
// Output a message at the end of the file
echo "End of file.<br />";
 
?>

这会产生以下输出:

The class "MyClass" was initiated!
 
 
Catchable fatal error: Object of class MyClass could not be converted to string in  
/Applications/XAMPP/xamppfiles/htdocs/testing/test.php on line 40

为了避免此项错误,添加一个 __toString() 方法:

<?php
 
class MyClass
{
    public $prop1 = "I'm a class property!";
 
    public function __construct()
    {
        echo 'The class "', __CLASS__, '" was initiated!<br />';
    }
 
    public function __destruct()
    {
        echo 'The class "', __CLASS__, '" was destroyed.<br />';
    }
 
    public function __toString()
    {
        echo "Using the toString method: ";
        return $this->getProperty();
    }
 
    public function setProperty($newval)
    {
        $this->prop1 = $newval;
    }
 
    public function getProperty()
    {
        return $this->prop1 . "<br />";
    }
}
 
// Create a new object
$obj = new MyClass;
 
// Output the object as a string
echo $obj;
 
// Destroy the object
unset($obj);
 
// Output a message at the end of the file
echo "End of file.<br />";
 
?>

这样的话,尝试将对象转为字符串的时候将会调用 getProperty() 方法。刷新您的浏览器您将看到以下内容:

The class "MyClass" was initiated!
Using the toString method: I'm a class property!
The class "MyClass" was destroyed.
End of file.

提示:除了本段所用到的几个魔术方法,还有更多的魔术方法可供使用,您可以在PHP手册中找到更多资料。

使用类继承

通过extends 关键字,类可以继承其它类的方法和属性。例如,创建另一个类来继承MyClass并增加一个方法,您可以这么做:

<?php
 
class MyClass
{
    public $prop1 = "I'm a class property!";
 
    public function __construct()
    {
        echo 'The class "', __CLASS__, '" was initiated!<br />';
    }
 
    public function __destruct()
    {
        echo 'The class "', __CLASS__, '" was destroyed.<br />';
    }
 
    public function __toString()
    {
        echo "Using the toString method: ";
        return $this->getProperty();
    }
 
    public function setProperty($newval)
    {
        $this->prop1 = $newval;
    }
 
    public function getProperty()
    {
        return $this->prop1 . "<br />";
    }
}
 
class MyOtherClass extends MyClass
{
    public function newMethod()
    {
        echo "From a new method in " . __CLASS__ . ".<br />";
    }
}
 
// Create a new object
$newobj = new MyOtherClass;
 
// Output the object as a string
echo $newobj->newMethod();
 
// Use a method from the parent class
echo $newobj->getProperty();
 
?>

程序将输出以下内容:

The class "MyClass" was initiated!
From a new method in MyOtherClass.
I'm a class property!
The class "MyClass" was destroyed.

重载继承的属性和方法

为了在新的类中修改现有的属性或者方法的行为,您只需在新的类中重新定义它们,即可实现重载:

<?php
 
class MyClass
{
    public $prop1 = "I'm a class property!";
 
    public function __construct()
    {
        echo 'The class "', __CLASS__, '" was initiated!<br />';
    }
 
    public function __destruct()
    {
        echo 'The class "', __CLASS__, '" was destroyed.<br />';
    }
 
    public function __toString()
    {
        echo "Using the toString method: ";
        return $this->getProperty();
    }
 
    public function setProperty($newval)
    {
        $this->prop1 = $newval;
    }
 
    public function getProperty()
    {
        return $this->prop1 . "<br />";
    }
}
 
class MyOtherClass extends MyClass
{
    public function __construct()
    {
        echo "A new constructor in " . __CLASS__ . ".<br />";
    }
 
    public function newMethod()
    {
        echo "From a new method in " . __CLASS__ . ".<br />";
    }
}
 
// Create a new object
$newobj = new MyOtherClass;
 
// Output the object as a string
echo $newobj->newMethod();
 
// Use a method from the parent class
echo $newobj->getProperty();
 
?>

这样,程序将会输出:

A new constructor in MyOtherClass.
From a new method in MyOtherClass.
I'm a class property!
The class "MyClass" was destroyed.

重载时保留原有方法的功能

为了在继承的方法中添加功能的同时完整地保留住原始方法的功能,请使用 parent 关键字以及范围解析操作符(::):

<?php
 
class MyClass
{
    public $prop1 = "I'm a class property!";
 
    public function __construct()
    {
        echo 'The class "', __CLASS__, '" was initiated!<br />';
    }
 
    public function __destruct()
    {
        echo 'The class "', __CLASS__, '" was destroyed.<br />';
    }
 
    public function __toString()
    {
        echo "Using the toString method: ";
        return $this->getProperty();
    }
 
    public function setProperty($newval)
    {
        $this->prop1 = $newval;
    }
 
    public function getProperty()
    {
        return $this->prop1 . "<br />";
    }
}
 
class MyOtherClass extends MyClass
{
    public function __construct()
    {
        parent::__construct(); // Call the parent class's constructor
        echo "A new constructor in " . __CLASS__ . ".<br />";
    }
 
    public function newMethod()
    {
        echo "From a new method in " . __CLASS__ . ".<br />";
    }
}
 
// Create a new object
$newobj = new MyOtherClass;
 
// Output the object as a string
echo $newobj->newMethod();
 
// Use a method from the parent class
echo $newobj->getProperty();
 
?>

这将会输出原始构造函数的内容以及新构造函数的内容:

The class "MyClass" was initiated!
A new constructor in MyOtherClass.
From a new method in MyOtherClass.
I'm a class property!
The class "MyClass" was destroyed.

设置类的属性和方法的可见性

为了更好地控制对象,方法和属性都被设置了可见性。这样控制了属性和方法能从什么地方被访问。有以下三个可见性关键字:public, protected以及private。此外,一个方法或者属性能够被声明为 static (静态的),这样,即便是类没有被实例化,它们也能被访问。

注意:可见性是PHP5的新特性。要了解关于PHP4的OOP兼容性的相关信息,请阅读PHP手册。

受限的(protected)属性和方法

当一个属性或者方法被声明为protected,它就只能在该类自身或者其派生的子类中被访问。
下面的代码将会把getProperty()方法声明为protected,并尝试从类的外部访问它:

<?php
 
class MyClass
{
    public $prop1 = "I'm a class property!";
 
    public function __construct()
    {
        echo 'The class "', __CLASS__, '" was initiated!<br />';
    }
 
    public function __destruct()
    {
        echo 'The class "', __CLASS__, '" was destroyed.<br />';
    }
 
    public function __toString()
    {
        echo "Using the toString method: ";
        return $this->getProperty();
    }
 
    public function setProperty($newval)
    {
        $this->prop1 = $newval;
    }
 
    protected function getProperty()
    {
        return $this->prop1 . "<br />";
    }
}
 
class MyOtherClass extends MyClass
{
    public function __construct()
    {
        parent::__construct();
		echo "A new constructor in " . __CLASS__ . ".<br />";
    }
 
    public function newMethod()
    {
        echo "From a new method in " . __CLASS__ . ".<br />";
    }
}
 
// Create a new object
$newobj = new MyOtherClass;
 
// Attempt to call a protected method
echo $newobj->getProperty();
 
?>

运行这段程序的时候会输出以下内容:

The class "MyClass" was initiated!
A new constructor in MyOtherClass.
 
Fatal error: Call to protected method MyClass::getProperty() from context '' in  
/Applications/XAMPP/xamppfiles/htdocs/testing/test.php on line 55

现在,在MyOtherClass中定义一个新方法来调用 getProperty():

<?php
 
class MyClass
{
    public $prop1 = "I'm a class property!";
 
    public function __construct()
    {
        echo 'The class "', __CLASS__, '" was initiated!<br />';
    }
 
    public function __destruct()
    {
        echo 'The class "', __CLASS__, '" was destroyed.<br />';
    }
 
    public function __toString()
    {
        echo "Using the toString method: ";
        return $this->getProperty();
    }
 
    public function setProperty($newval)
    {
        $this->prop1 = $newval;
    }
 
    protected function getProperty()
    {
        return $this->prop1 . "<br />";
    }
}
 
class MyOtherClass extends MyClass
{
    public function __construct()
    {
        parent::__construct();
		echo "A new constructor in " . __CLASS__ . ".<br />";
    }
 
    public function newMethod()
    {
        echo "From a new method in " . __CLASS__ . ".<br />";
    }
 
    public function callProtected()
    {
        return $this->getProperty();
    }
}
 
// Create a new object
$newobj = new MyOtherClass;
 
// Call the protected method from within a public method
echo $newobj->callProtected();
 
?>

程序将输出您想要的结果:

The class "MyClass" was initiated!
A new constructor in MyOtherClass.
I'm a class property!
The class "MyClass" was destroyed.

私有的(private)属性和方法

如果一个方法或者属性被声明为private,那就只有定义它的那个类才能访问它。这就意味着,即便是另一个类继承了此类,那个类也不能够访问此类中的私有属性和方法。
为了演示这一特性,以下代码将在MyClass中把getProperty()声明为private,然后尝试从MyOtherClass中调用 callProtected():

<?php
 
class MyClass
{
    public $prop1 = "I'm a class property!";
 
    public function __construct()
    {
        echo 'The class "', __CLASS__, '" was initiated!<br />';
    }
 
    public function __destruct()
    {
        echo 'The class "', __CLASS__, '" was destroyed.<br />';
    }
 
    public function __toString()
    {
        echo "Using the toString method: ";
        return $this->getProperty();
    }
 
    public function setProperty($newval)
    {
        $this->prop1 = $newval;
    }
 
    private function getProperty()
    {
        return $this->prop1 . "<br />";
    }
}
 
class MyOtherClass extends MyClass
{
    public function __construct()
    {
        parent::__construct();
        echo "A new constructor in " . __CLASS__ . ".<br />";
    }
 
    public function newMethod()
    {
        echo "From a new method in " . __CLASS__ . ".<br />";
    }
 
    public function callProtected()
    {
        return $this->getProperty();
    }
}
 
// Create a new object
$newobj = new MyOtherClass;
 
// Use a method from the parent class
echo $newobj->callProtected();
 
?>

运行这段程序,则会显示报错信息:

The class "MyClass" was initiated!
A new constructor in MyOtherClass.
 
Fatal error: Call to private method MyClass::getProperty() from context 'MyOtherClass' in  
/Applications/XAMPP/xamppfiles/htdocs/testing/test.php on line 49

静态的(static)属性和方法

如果一个属性或者方法被声明为static,那么不需要实例化这个类,它就能够被访问;你只需通过类的名称以及范围解析操作符以及属性或方法名就能访问它了。
“使用静态属性的好处之一是,它们可以在整个脚本运行期间保存一些值不受影响。”
为了演示这一属性,在MyClass中添加一个名为$count的静态属性以及一个名为plusOne()的静态方法。然后设置一个do…while循环,作用是在$count值小于10的时候输出递增的$count值。

<?php
 
class MyClass
{
    public $prop1 = "I'm a class property!";
 
    public static $count = 0;
 
    public function __construct()
    {
        echo 'The class "', __CLASS__, '" was initiated!<br />';
    }
 
    public function __destruct()
    {
        echo 'The class "', __CLASS__, '" was destroyed.<br />';
    }
 
    public function __toString()
    {
        echo "Using the toString method: ";
        return $this->getProperty();
    }
 
    public function setProperty($newval)
    {
        $this->prop1 = $newval;
    }
 
    private function getProperty()
    {
        return $this->prop1 . "<br />";
    }
 
    public static function plusOne()
    {
        return "The count is " . ++self::$count . ".<br />";
    }
}
 
class MyOtherClass extends MyClass
{
    public function __construct()
    {
        parent::__construct();
        echo "A new constructor in " . __CLASS__ . ".<br />";
    }
 
    public function newMethod()
    {
        echo "From a new method in " . __CLASS__ . ".<br />";
    }
 
    public function callProtected()
    {
        return $this->getProperty();
    }
}
 
do
{
    // Call plusOne without instantiating MyClass
    echo MyClass::plusOne();
} while ( MyClass::$count < 10 );
 
?>

注意:访问静态属性的时候,美元符号($)必须在范围解析操作符(::)的后面。

当你运行这段程序的时候,输出的内容是这样的:

The count is 1.
The count is 2.
The count is 3.
The count is 4.
The count is 5.
The count is 6.
The count is 7.
The count is 8.
The count is 9.
The count is 10.

使用文档块(DocBlocks)来注释

尽管不是官方所设置的OOP的一部分,文档块注释风格是一种被广泛认可的类文档编写方式。它不仅为开发者提供了一个编写代码的标准,还被很多流行的软件开发包(SDK)所采纳,例如Eclipse和NetBeans,而且还被用来生成代码提示。
文档块的使用方式就是增加了星号的块注释:

/**
 * This is a very basic DocBlock
 */

文档块真正的好处在于使用tags(标签),标签使用@符号作为开始,紧跟着的是标签的名字以及标签的值。文档块的标签允许开发者们定义文件的作者、类的授权许可、属性和方法的信息以及其它有用的信息。
常用的标签如下:
@author: 使用此标签来注明当前元素(可以是类、文件、方法或者一段代码)的作者。如果有多个作者,同一个文档块中可以使用多个author标签。作者名字的格式是 John Doe .
@copyright: 此标签注明了当前元素版权的年份以及所有者。格式是 2010 Copyright Holder.
@license: 此标签链接到当前元素的授权许可。格式是 http://www.example.com/path/to/license.txt 授权名称.
@var: 此标签指定了变量或者类属性的类型和描述。格式是 type element description.
@param: 此标签注明了函数或者方法的参数的类型和描述。格式是 type $element_name element description.
@return: 此标签注明了函数或者方法的返回值的类型和描述。格式是 type return element description.

下面是一个使用了文档块注释的类的范例:

<?php
 
/**
 * A simple class
 *
 * This is the long description for this class,
 * which can span as many lines as needed. It is
 * not required, whereas the short description is
 * necessary.
 *
 * It can also span multiple paragraphs if the
 * description merits that much verbiage.
 *
 * @author Jason Lengstorf <jason.lengstorf@ennuidesign.com>
 * @copyright 2010 Ennui Design
 * @license http://www.php.net/license/3_01.txt PHP License 3.01
 */
class SimpleClass
{
    /**
     * A public variable
     *
     * @var string stores data for the class
     */
    public $foo;
 
    /**
     * Sets $foo to a new value upon class instantiation
     *
     * @param string $val a value required for the class
     * @return void
     */
    public function __construct($val)
    {
        $this->foo = $val;
    }
 
    /**
     * Multiplies two integers
     *
     * Accepts a pair of integers and returns the
     * product of the two.
     *
     * @param int $bat a number to be multiplied
     * @param int $baz a number to be multiplied
     * @return int the product of the two parameters
     */
    public function bar($bat, $baz)
    {
        return $bat * $baz;
    }
}
 
?>

一旦你仔细审视前面的这个类,你就会发现文档块的好处是显而易见的:一切都被清楚地定义,因此下一个开发者可以很快接手,而不需要费脑筋去思考太多。

比较面向对象和面向过程的代码

事实上并没有一种真正正确或者错误的编程方法。
话虽如此,本段落对软件开发(尤其是大型应用程序开发)中采用面向对象编程方法进行了有力的论证。

理由1:易于实现

尽管起初有点令人畏惧,但事实上OOP提供了一种更容易处理数据的方法。因为对象可以在其内部存储数据,变量就不需要反复地从函数传递到函数了。
而且,同一个类的多个实例可以同时存在,处理大量数据变得轻而易举。例如,想像一下,有两个人的信息需要在一个文件中处理。他们有名字、职业以及年龄。
面向过程的方法
下面是面向过程的处理方法:

<?php
 
function changeJob($person, $newjob)
{
    $person['job'] = $newjob; // Change the person's job
    return $person;
}
 
function happyBirthday($person)
{
    ++$person['age']; // Add 1 to the person's age
    return $person;
}
 
$person1 = array(
    'name' => 'Tom',
    'job' => 'Button-Pusher',
    'age' => 34
);
 
$person2 = array(
    'name' => 'John',
    'job' => 'Lever-Puller',
    'age' => 41
);
 
// Output the starting values for the people
echo "<pre>Person 1: ", print_r($person1, TRUE), "<\/pre>";
echo "<pre>Person 2: ", print_r($person2, TRUE), "<\/pre>";
 
// Tom got a promotion and had a birthday
$person1 = changeJob($person1, 'Box-Mover');
$person1 = happyBirthday($person1);
 
// John just had a birthday
$person2 = happyBirthday($person2);
 
// Output the new values for the people
echo "<pre>Person 1: ", print_r($person1, TRUE), "<\/pre>";
echo "<pre>Person 2: ", print_r($person2, TRUE), "<\/pre>";
 
?>

执行后,程序输出下列内容:

Person 1: Array   
(   
    [name] => Tom   
    [job] => Button-Pusher   
    [age] => 34   
)   
Person 2: Array   
(   
    [name] => John   
    [job] => Lever-Puller   
    [age] => 41   
)   
Person 1: Array   
(   
    [name] => Tom   
    [job] => Box-Mover   
    [age] => 35   
)   
Person 2: Array   
(   
    [name] => John   
    [job] => Lever-Puller   
    [age] => 42   
)

尽管这段代码不是很糟糕,但是编码的时候还是要注意很多地方。人物属性数组必须从各个函数之间传递和返回,这就可能导致错误的发生。
为了解决这个范例的问题,留给开发者的东西应当尽可能的少。只有当前操作的那些绝对必要的信息才传递给函数。

下面就轮到OOP来为您解决问题了。

面向对象的方法
下面是针对此范例的面向对象方法:

<?php
 
class Person
{
    private $_name;
    private $_job;
    private $_age;
 
    public function __construct($name, $job, $age)
    {
        $this->_name = $name;
        $this->_job = $job;
        $this->_age = $age;
    }
 
    public function changeJob($newjob)
    {
        $this->_job = $newjob;
    }
 
    public function happyBirthday()
    {
        ++$this->_age;
    }
}
 
// Create two new people
$person1 = new Person("Tom", "Button-Pusher", 34);
$person2 = new Person("John", "Lever Puller", 41);
 
// Output their starting point
echo "<pre>Person 1: ", print_r($person1, TRUE), "<\/pre>";
echo "<pre>Person 2: ", print_r($person2, TRUE), "<\/pre>";
 
// Give Tom a promotion and a birthday
$person1->changeJob("Box-Mover");
$person1->happyBirthday();
 
// John just gets a year older
$person2->happyBirthday();
 
// Output the ending values
echo "<pre>Person 1: ", print_r($person1, TRUE), "<\/pre>";
echo "<pre>Person 2: ", print_r($person2, TRUE), "<\/pre>";
 
?>

这段程序会输出以下内容:

Person 1: Person Object
(
    [_name:private] => Tom
    [_job:private] => Button-Pusher
    [_age:private] => 34
)
 
Person 2: Person Object
(
    [_name:private] => John
    [_job:private] => Lever Puller
    [_age:private] => 41
)
 
Person 1: Person Object
(
    [_name:private] => Tom
    [_job:private] => Box-Mover
    [_age:private] => 35
)
 
Person 2: Person Object
(
    [_name:private] => John
    [_job:private] => Lever Puller
    [_age:private] => 42
)

要使这一方法面向对象,需要更多的步骤,但是一旦类被定义,添加和修改人物就变得轻而易举;人物的信息不需要从方法之间传递,只有那些绝对必不可少的信息才从方法之间传递。
如果规模比较小,这种差别可能不明显,但是如果已经合理地实施了的话,一旦您的应用程序的大小增加,OOP将会显著地减少您的工作量。
提示——并非所有东西都需要面向对象化。一个快速处理应用程序内部的少量数据的函数,并不需要写进类里面。根据您的需要来决定使用面向对象还是面向过程。

理由2:更好的组织

OOP的另一个好处是,代码更容易被打包和分类。每个类通常都被写在单独的文件里面,而如果使用了统一的命名规范,访问这些类就是很简单的事情。
假设您有个应用程序中包含了150个类,这些类通过根目录下的一个控制器文件来动态调用。所有150个类都遵循 class.classname.inc.php 这样的命名规范并保存在 inc 目录下。
这个控制器能够实现PHP的 __autoload() 函数,作用是动态地调用某一时刻所需调用的类,而不是在这个控制器文件中把这150个文件都包含进来备用(或者是使用您自己实现的某种智能的方法来包含文件):

<?php
    function __autoload($class_name)
    {
        include_once 'inc/class.' . $class_name . '.inc.php';
    }
?>

将每个类写到单独的文件中,这同样改善了代码的可移植性和可重用性。

理由3:更容易维护

归功于面向对象的紧凑性,相对于冗长的面向过程的面条代码(spaghetti code),对面向对象的代码的修改将会更容易实现。
如果某个特定的信息数组获得了一个新的属性,面向过程的代码(最坏的情况下)可能需要在每个调用了此数组的函数中加上这一属性。
而面向对象的代码很容易添加新的属性,只要修改那个处理这一属性的方法就可以了。

本章节所说到的很多好处都是OOP与DRY编程实践相结合的产物。创建易于维护的面向过程的代码是绝对有可能的,同样也可能写出很糟糕的面向对象的代码。[Pro PHP and jQuery]这本书将尝试展示一种新的编程方法,目的是将面向对象与良好的编码习惯相结合,编写出更干净的、容易阅读和维护的代码。


PHP, 译文 | Tags: OOP, PHP, 面向对象



发表评论





IT狂人的博客

  • 声明

    本博文章及相关作品(包括但不限于文字、图片),除特别说明为转载外,均属本人原创,依据《国家知识产权法》、《著作权法》和《信息网络传播权保护条例》,原创知识产权、版权均为本人所有,本人享有著作权,并受法律保护。

    文章欢迎转载,但请事先与本人联系:email
    未经本人许可,任何人不得转载或使用整体或任何部分的内容。未尽事宜,依据相关法律法规处理。

  • 分类目录

    • ASP .Net (1)
    • CodeIgniter (8)
    • PHP (10)
    • Web (7)
    • 未分类 (1)
    • 杂谈 (4)
    • 译文 (7)
    • 音乐&电影 (1)
  • 最近文章

    • Discuz上传图片时出现“无效的图片文件”提示的解决方案
    • discuz定时发帖的另类实现
    • 使用PHP的Glob()函数来遍历文件夹
    • [译]PHP面向对象入门
    • 原来如此
    • 使用htaccess来增强WordPress安全性
    • 归来
    • 因4740G而想起的事
    • 选择Web开发框架时需要考虑的15个重要因素
    • 为NetBeans添加Git支持
  • 文章归档

    • 2010 年 八月 (2)
    • 2010 年 七月 (5)
    • 2010 年 五月 (1)
    • 2010 年 四月 (1)
    • 2010 年 三月 (2)
    • 2010 年 二月 (1)
    • 2010 年 一月 (2)
    • 2009 年 十二月 (1)
    • 2009 年 十月 (2)
    • 2009 年 九月 (3)
    • 2009 年 八月 (10)
  • 标签

    API AVC CakePHP CHM CodeIgniter CURD discuz Django DroidSansFallback eaccelerator footer Framework Fran Healy Git helper IT Kohana MVC MVP MYSQL NetBeans Oasis ORM pChart pdf PHP phpMyAdmin Ruby tcpdf techified time Travis Twitter Web windows Wordpress XML Zend 传记 外链 大写 孔乙己 框架 盗链 过滤
  •  

    2010 七月
    一 二 三 四 五 六 日
    « 五   八 »
     1234
    567891011
    12131415161718
    19202122232425
    262728293031  
  • Playlist


  • 近期评论

    • admin 在 [译文]在NetBeans IDE中增加CodeIgniter代码提示 上的评论
    • admin 在 使用TCPDF输出完美的中文PDF文档 上的评论
    • admin 在 使用TCPDF输出完美的中文PDF文档 上的评论
    • Macca 在 使用TCPDF输出完美的中文PDF文档 上的评论
    • Macca 在 使用TCPDF输出完美的中文PDF文档 上的评论
  • 链接

    • cnBeta.COM
    • CodeIgniter 中国
    • Gray && Simple
    • jQuery中文社区
    • Lily Allen
    • Mtime时光网
    • W3School
    • 小众软件
    • 破烂熊乐园
    • 韩寒
Copyright © 2010 IT狂人的博客 All Rights Reserved. XHTML CSS THEME by I SOFTWARE REVIEWS