php oop面向?qū)ο?/h1>
作者:佚名 時間:2012-04-11 分享到:
1.OOP思想與理論
其實(shí)我不知道在這里提OOP思想會不會是一種再炒蛋炒飯的行為,但是還是說說自己的理解。
OOP即Object-Oriented Programming,是面向?qū)ο蟪绦蛟O(shè)計的意思。
OO(面向?qū)ο?的思想更接近于人類對自然界的認(rèn)知——這話講得似乎很高深,其實(shí)說白了就是人們?nèi)绾卫斫夂涂创挛锏。比如汽車是一個類(是抽象的,現(xiàn)實(shí)中不存在汽車這樣的實(shí)物,存在的是汽車的各種實(shí)例),這個類的屬性有:生產(chǎn)日期、輪子大小等;這個類的方法有:加速、減速、鳴喇叭等;而保時捷屬于汽車這個類,是汽車這個類的一個實(shí)例——即對象,因此保時捷也具有這個類的屬性和方法;當(dāng)然,勞斯萊斯也有這些屬性和方法,不過具體的細(xì)節(jié)顯然和保時捷是不一樣的。
(1)類和對象
人類是一個類,而你和我以及在看這篇文章的朋友是人類這個類的實(shí)例——即對象。我們就首先以人類為模型來學(xué)習(xí),來看一段代碼:
<?php
class People {
private $name;
function sayHello() {
echo "hello\n";
}
}
$man = new People ;
$man->sayHello() ;
?>
代碼首先創(chuàng)建一個People類,該類有姓名這個屬性,而且擁有會講hello這樣的方法;接著實(shí)例化一個對象$man,$man也一樣有姓名這個屬性和講hello這個方法;最后調(diào)用sayHello()這個方法,使用的方式是:$man->sayHello()。
到這里,應(yīng)該能很好地理解類和對象了吧^_^。
(2)繼承、重載與多態(tài)
既然是人類的正常思維,那么繼承就更好理解,無非就是基因的遺傳嘛。
我們把中國人、美國人看做是繼承人類的兩個類。再來一段代碼:
<?php
class People {
private $name;
function sayHello() {
echo "hello<br />";
}
}
class Chinese extends People {
function sayHello() {
echo "你好<br />";
}
}
class American extends People {
}
$cnMan = new Chinese;
$usaMan = new American;
$cnMan->sayHello();
$usaMan->sayHello();
?>
運(yùn)行會發(fā)現(xiàn)輸出為:
你好
hello
從這個實(shí)例中我們可以看出子類American繼承了父類People的方法;當(dāng)然,大家都是明白人,也知道子類Chinese也繼承了父類People的屬性和方法,但是中國人嘛,畢竟講得最多的還是中國話;在Chinese類的定義中,重寫父類中的屬性或者方法叫做重載,我們完全可以理解成遺傳變異或者叫做進(jìn)化——恩,進(jìn)化顯得更好點(diǎn)。
而中國人和美國人講"你好"的方式是不一樣的,前者用漢語表現(xiàn)而后者用英語表現(xiàn)出來——這可以比較模糊地理解為多態(tài),說白了,就是由于環(huán)境不同而產(chǎn)生了不同的進(jìn)化方向。
(3)更明確地認(rèn)識重載和多態(tài)
其實(shí)重載是多態(tài)的一種;如何表現(xiàn)多態(tài)呢——當(dāng)然是重寫方法嘛,這就是重載。引用上面的話:重載就是重寫父類中的屬性或者方法。而多態(tài)仍是繼承的范圍,只不過在遺傳過程中表現(xiàn)出不同的進(jìn)化方向。比如你和你弟弟都是遺傳爸媽的,但是兩個人一般是不一樣的吧。因此多態(tài)就是同一個方法或者屬性在不同的子類下表現(xiàn)出不同的形態(tài)。
(4)關(guān)鍵字this、作用域符號::、構(gòu)造函數(shù)__construct()以及析構(gòu)函數(shù)__destruct()
首先來看this。this關(guān)鍵字是指本體,即引用當(dāng)前屬性或方法的對象,比如$this->sayHello()表示引用自身的sayHello()方法,看下代碼:
<?php
class People {
private $nationality = "noNationality";
private $name = "noName";
function sayHello() {
echo "hello<br />";
}
function setName($n) {
$this->name = $n;
}
function setNationality($n) {
$this->nationality = $n;
}
function writeName() {
echo $this->name . "<br />";
}
function writeNationality() {
echo $this->nationality . "<br/>";
}
}
class Chinese extends People {
function sayHello() {
echo "你好<br />";
}
}
class American extends People {
}
$cnMan = new Chinese;
$usaMan = new American;
$cnMan->setName("菜籃子");
$cnMan->setNationality("中國");
$cnMan->sayHello();
$cnMan->writeName();
$cnMan->writeNationality();
?>
輸出為:
你好
菜籃子
中國
接著我們在American類中寫上構(gòu)造函數(shù)和析構(gòu)函數(shù),并用上作用域符號:
<?php
class People {
private $nationality = "noNationality";
private $name = "noName";
function sayHello() {
echo "hello<br />";
}
function setName($n) {
$this->name = $n;
}
function setNationality($n) {
$this->nationality = $n;
}
function writeName() {
echo $this->name . "<br />";
}
function writeNationality() {
echo $this->nationality . "<br/>";
}
}
class Chinese extends People {
function sayHello() {
echo "你好<br />";
}
}
class American extends People {
function sayHelloInChinese() {
echo "I can say hello in Chinese , you see , ";
Chinese::sayHello();
}
function __construct() {
echo "一個American類的實(shí)例被創(chuàng)建了<br />";
}
function __destruct() {
echo "一個American類的實(shí)例被撤銷了<br />";
}
}
$cnMan = new Chinese;
$usaMan = new American;
$cnMan->setName("菜籃子");
$cnMan->setNationality("中國");
$cnMan->sayHello();
$cnMan->writeName();
$cnMan->writeNationality();
$usaMan->sayHelloInChinese();
?>
輸出為:
一個American類的實(shí)例被創(chuàng)建了
你好
菜籃子
中國
I can say hello in Chinese , you see , 你好
一個American類的實(shí)例被撤銷了
因此我們可以了解到__construct()函數(shù)在一個對象實(shí)例化的時候調(diào)用,而__destruct()函數(shù)在一個對象被撤銷時調(diào)用——我們應(yīng)該知道PHP5會自動回收資源,不必像C一樣用上free();而作用域符號::指定調(diào)用哪個類中的屬性或者方法。
(5)訪問控制:public protected private
引用php.net的一段話:“對屬性或方法的訪問控制,是通過在前面添加關(guān)鍵字 public、protected 或 private 來實(shí)現(xiàn)的。由 public 所定義的類成員可以在任何地方被訪問;由 protected 所定義的類成員則可以被其所在類的子類和父類訪問(當(dāng)然,該成員所在的類也可以訪問);而由 private 定義的類成員則只能被其所在類訪問。 ”
而且,方法如果沒有指定關(guān)鍵字,默認(rèn)為public。下見一段代碼了解:
<?php
class People {
private $name;
protected $nationality;
public $sex;
function sayHello() {
echo "hello<br />";
}
}
$man = new People ;
$man->sayHello() ;
$man->sex = "男";
echo $man->sex;
$man->nationality = "中國";
echo $man->nationlity;
?>
輸出為:
hello
男
Fatal error: Cannot access protected property People::$nationality ……
這說明了由 protected 所定義的類成員則可以被其所在類的子類和父類訪問,但是不能被對象訪問。當(dāng)然,private限制得更多了,只能類本身訪問。我們改進(jìn)oop_5.php進(jìn)一步了解訪問控制:
<?php
// @filename oop_5.php
class People {
private $name;
protected $nationality;
public $sex;
function sayHello() {
echo "hello<br />";
}
function setName($n) {
$this->name = $n;
}
function setNationality($n) {
$this->nationality = $n;
}
function writeName() {
echo $this->name . "<br />";
}
function writeNationality() {
echo $this->nationality . "<br />";
}
}
$man = new People ;
$man->sayHello() ;
$man->sex = "男";
echo $man->sex . "<br />";
class Chinese extends People {
protected $nationality = "中國";
function emigrate($n) {
$this->nationality = $n;
}
}
$cnMan = new Chinese;
$cnMan->setName("菜籃子");
$cnMan->setNationality("中國");
$cnMan->sayHello();
$cnMan->writeName();
$cnMan->writeNationality();
$cnMan->emigrate("美國");
$cnMan->writeNationality();
?>
輸出為:
hello
男
hello
菜籃子
中國
美國
如果在最后加上一句:$cnMan->nationality = "菲律賓";則會出現(xiàn)錯誤:Fatal error: Cannot access protected property Chinese::$nationality in……
訪問控制是為了實(shí)現(xiàn)信息的隱藏——即所謂的封裝性。就好比你只要知道踩油門就可以加速而不必知道具體的工作原理,這就是封裝了^_^。
(6)final static clone
final顧名思義就是最后的,指定某個方法不能重載或者某個類不能被繼承。
static是指定靜態(tài)方法或者變量的關(guān)鍵字。靜態(tài)方法可以無需實(shí)例化類就可以使用,即用類名和作用域符號結(jié)合使用,而靜態(tài)變量不止如此,它還在類的作用范圍有“存儲”功能,并且對象不能直接訪問:
<?php
// @filename oop_6.php
class test1 {
static $num1 = 1;
function addNum1() {
echo self::$num1 . "<br />";
self::$num1++;
}
}
class test2 extends test1 {
function writeParentNum() {
echo parent::$num1 . "<br />";
parent::$num1++;
}
}
echo test1::addNum1(); //輸出1
$a = new test1;
$a->addNum1(); //輸出2
$b = new test1;
echo $b->num1 . "<br />"; //不輸出
echo test1::addNum1(); //輸出3
test2::addNum1(); //輸出4
$c = new test2;
$c->writeParentNum(); //輸出5
echo 'the end';
?>
輸出為:
1
2
3
4
5
the end
而第三個是clone,用法為$obj1 = clone $obj2;即復(fù)制$obj2所有屬性和方法到$obj1——純粹只是復(fù)制屬性和方法而已,而二者的內(nèi)存指向是不一樣的,與$obj1 = $obj2是不同的,詳細(xì)了解見下面代碼:
<?php
class SubObject
{
static $instances = 0; //靜態(tài)變量
public $instance;
public function __construct() { //構(gòu)造函數(shù)
$this->instance = ++self::$instances;
}
public function __clone() { //當(dāng)使用clone關(guān)鍵字時,會首先嘗試尋找調(diào)用已定義的__clone()函數(shù)
//而且__clone()函數(shù)不能直接不調(diào)用
$this->instance = ++self::$instances;
}
}
class MyCloneable
{
public $object1;
public $object2;
function __clone()
{
// Force a copy of this->object, otherwise
// it will point to same object.
$this->object1 = clone $this->object1;
}
}
$obj = new MyCloneable();
$obj->object1 = new SubObject(); //這里會調(diào)用構(gòu)造函數(shù),靜態(tài)變量增1,為1
//即$obj->object1的屬性:$instance = 1; $instances = 1;
$obj->object2 = new SubObject(); //這里會調(diào)用構(gòu)造函數(shù),靜態(tài)變量再增1,為2
//即$obj->object2的屬性:$instance = 2; $instances = 2;
$obj2 = clone $obj; //這里會調(diào)用MyCloneable類中__clone()函數(shù)
//執(zhí)行$this->object1 = clone $this->object1;(其中僅有$obj2->object1克隆了$obj->object1;)
//因此,$obj2->object1新開辟了一個內(nèi)存空間,并且克隆了$obj->object1的所有屬性
//可知 $obj2->object1->instance=1;$obj2->object1->instances=2;
//并且由于再度出現(xiàn)clone關(guān)鍵字,所以調(diào)用SubObject類中__clone()函數(shù)
//所以在這里執(zhí)行了$this->instance = ++self::$instances;
//因此得到$obj2->object1->instance=3;
//而另外進(jìn)行的是:$obj2->object2 = $obj->object2
//這樣的環(huán)境我總是喜歡C語言中的解釋:$obj2->object2 與 $obj->object2指向的是同一內(nèi)存地址
//所以$obj2->object2 與 $obj->object2指向的是同一片內(nèi)存空間,即二者的本質(zhì)是一樣的
//因此可知,$obj2->object2->instance=2;因?yàn)椴]有使用clone關(guān)鍵字,所以沒有調(diào)用__clone()函數(shù)
print("Original Object:<br />");
print_r($obj);
echo '<br />';
print("Cloned Object:<br />");
print_r($obj2);
echo '<br />';
?>
輸出如下:
Original Object:
MyCloneable Object
(
[object1] => SubObject Object
(
[instance] => 1
)
[object2] => SubObject Object
(
[instance] => 2
)
)
Cloned Object:
MyCloneable Object
(
[object1] => SubObject Object
(
[instance] => 3
)
[object2] => SubObject Object
(
[instance] => 2
)
)
(7)抽象方法、抽象類以及接口
我根據(jù)php.net里Class Abstraction的內(nèi)容直接翻譯過來并摘要得:
1.抽象類不能被實(shí)例化,只能被繼承再實(shí)例化。
2.只要含有抽象函數(shù)則該類必須為抽象類。
3.集成抽象類后,對于抽象函數(shù)的具體實(shí)現(xiàn)時要注意訪問控制只能更松散而不能更嚴(yán)格,比如原來是protected關(guān)鍵字,實(shí)現(xiàn)時只能是protected或者public,而不能是private。
而關(guān)于接口,我也是直接根據(jù)php.net里的Object Interfaces的內(nèi)容直接翻譯:
1.接口允許用戶指定一些某個類要實(shí)現(xiàn)的函數(shù);
2.接口用interface關(guān)鍵字定義,但是接口內(nèi)的函數(shù)不能有實(shí)際內(nèi)容;
3.接口內(nèi)所有函數(shù)必須指定為public,這是接口的本質(zhì);
4.類用implements關(guān)鍵字實(shí)現(xiàn)接口,如class Template implements iTemplate{}或者class Template implements iTemplate1,iTemplate2{};
5.實(shí)現(xiàn)多個接口的時候,各個接口內(nèi)不能有重名屬性或者含義,否則會有歧義;
6.一個類實(shí)現(xiàn)接口時必須具體化每個接口內(nèi)指定的函數(shù)。
這樣我們會發(fā)現(xiàn)當(dāng)抽象類中所有函數(shù)都沒有具體操作句柄的時候,就幾乎為接口了^_^。
進(jìn)一步學(xué)習(xí)請參考:
http://docs.php.net/manual/zh/language.oop5.abstract.php
http://docs.php.net/manual/zh/language.oop5.interfaces.php
(8)__autoload()
直接參考php.net的經(jīng)典代碼:
<?php
function __autoload($class_name) {
require_once $class_name . '.php';
}
$obj = new MyClass1();
$obj2 = new MyClass2();
?>
^_^自動加載函數(shù)__autoload()的作用是實(shí)例化一個對象時,如果當(dāng)前文件內(nèi)沒有指定類則調(diào)用__autoload()。比如我們經(jīng)常把某個類單獨(dú)存為一個文件*.class.php,那么就可以用到了。
當(dāng)然,OOP不僅僅是這些的,想要深入學(xué)習(xí)還得靠自己。 ^_^
接下來將會分享幾個OOP實(shí)例,敬請期待,謝謝。
實(shí)例篇:
<?php
class dbConnect {
private $dbhost;
private $dbuser;
private $dbpwd;
private $dbname;
public $link;
public $query;
function __construct($h,$u,$p,$n) {
$this->dbhost = $h;
$this->dbuser = $u;
$this->dbpwd = $p;
$this->dbname = $n;
}
function __destruct() {
mysql_close($this->link);
}
public function createCon() {
$this->link = mysql_connect($this->dbhost,$this->dbuser,$this->dbpwd);
mysql_select_db($this->dbname,$this->link);
mysql_query("set names utf8");
}
}