首頁(yè)新聞動(dòng)態(tài)正文

PHP面向?qū)ο螅涸O(shè)計(jì)模式[單例模式]

更新時(shí)間:2018-11-29 來源:黑馬程序員技術(shù)社區(qū) 瀏覽量:

單件模式要解決的問題就是“如何讓這個(gè)類只有一個(gè)實(shí)例”。
我們的web應(yīng)用中,大量使用了數(shù)據(jù)庫(kù)連接,如果反復(fù)建立與數(shù)據(jù)庫(kù)的連接必然消耗更多的系統(tǒng)資源。
我們?nèi)绾谓鉀Q這個(gè)問題,建立唯一的數(shù)據(jù)庫(kù)連接是必要的方式。
我們又如何知道與這個(gè)數(shù)據(jù)庫(kù)的連接是否已經(jīng)建立? 還是需要現(xiàn)在建立?
單件模式可以解決這個(gè)問題。
先假設(shè)我們需要一個(gè)類完成在內(nèi)存中只有一份的功能,我們?cè)撊绾巫瞿兀?br/>我們一步一步的使用前面學(xué)過的知識(shí)來寫一個(gè)單件的例子。

前面學(xué)過,每次用 new 類名 的方式,就可以創(chuàng)建一個(gè)對(duì)象。我們必須禁止外部程序用 new 類名的方式來創(chuàng)建多個(gè)實(shí)例。
解決辦法是:我們將構(gòu)造函數(shù)設(shè)置成 private ,讓構(gòu)造函數(shù)只能在內(nèi)部被調(diào)用,而外部不能調(diào)用。這樣,這個(gè)類就不能被外部用 new 的方式建立多個(gè)實(shí)例了。

以下是不能被外部用new實(shí)例化的類。

<?
class A{
    private function __construct(){}
}

$a = new A();
?>

程序運(yùn)行結(jié)果為:
Fatal error: Call to private A::__construct() from invalid context in E:\PHPProjects\test.php on line 6
我們已經(jīng)禁止外部用new實(shí)例化這個(gè)類,我們改如何讓用戶訪問這個(gè)類呢?前門堵了,我們需要給用戶留個(gè)后門。
解決辦法是:static 修飾的方法,可以不經(jīng)實(shí)例化一個(gè)類就可以直接訪問這個(gè)方法。

<?
class A{
    private function __construct(){}

    static function getClassA(){
        return "這里是后門,可以通過這里進(jìn)入類內(nèi)部..";
    }
}

echo A::getClassA();
?>

程序運(yùn)行結(jié)果為:

這里是后門,可以通過這里進(jìn)入類內(nèi)部..

雖然我們已經(jīng)進(jìn)入類內(nèi)部,但我們要的是這個(gè)類的唯一實(shí)例?先不管別的,我們先需要一個(gè)實(shí)例。通過這個(gè)static的方法返回這個(gè)實(shí)例,如何做呢?
解決辦法是:private的構(gòu)造函數(shù),不能被外部實(shí)例化。但是我們已經(jīng)成功潛入類的內(nèi)部了(間諜?007?),我們?cè)趦?nèi)部當(dāng)然可以調(diào)用private的方法創(chuàng)建對(duì)象。我們這樣做看看。
下面的例子我們確實(shí)返回了A類的實(shí)例,但注意兩次執(zhí)行返回的不是同一個(gè)實(shí)例。

//不能用new實(shí)例化的類.<br>
//static的方法留給外部訪問.<br>
//在方法內(nèi)部返回實(shí)例.<br><br>

<?
class A{
    private function __construct(){}
    static function getClassA(){
        $a = new A();
        return $a;
    }
}

// 看到這里確實(shí)返回的是 A 的實(shí)例.但不是同一個(gè)對(duì)象.
$a1 = A::getClassA();
$a2 = A::getClassA();
echo "\$a1 的類是 ".get_class($a1)." , \$a2 是 ".get_class($a1);
if($a1 === $a2){
   echo "<br> \$a1 \$a2 指向同一對(duì)象.";
}else{
   echo "<br> \$a1 \$a2 不是一個(gè)對(duì)象.";
}
?>

程序運(yùn)行結(jié)果為:

//不能用new實(shí)例化的類.
//static的方法留給外部訪問.
//在方法內(nèi)部返回實(shí)例.

$a1 的類是 A , $a2 是 A
$a1 $a2 不是一個(gè)對(duì)象.
我們已經(jīng)通過static方法返回了A的實(shí)例。但還有問題。我們?nèi)绾伪WC我們多次操作獲得的是同一個(gè)實(shí)例的呢?
解決辦法:static的屬性在內(nèi)部也只有一個(gè)。static 屬性能有效的被靜態(tài)方法調(diào)用。將這個(gè)屬性也設(shè)置成private,以防止外部調(diào)用。先將這個(gè)屬性設(shè)置成 null。每次返回對(duì)象前,先判斷這個(gè)屬性是否為 null 。如果為 null 就創(chuàng)建這個(gè)類的新實(shí)例,并賦值給這個(gè) static 屬性。如果不為空,就返回這個(gè)指向?qū)嵗?static 屬性。

//不能用new實(shí)例化的類.<br>
//static的方法留給外部訪問.<br>
//在方法內(nèi)部返回實(shí)例.<br>
//定義靜態(tài)屬性保證這個(gè)實(shí)例能被靜態(tài)方法調(diào)用.<br>
//增加判斷部分.<br><br>

<?
class A{
    private static $a = null;
    private function __construct(){}
    static function getClassA(){
        if( null == self::$a){
            self::$a = new A();
        }
        return self::$a;
    }
}
// 看到這里確實(shí)返回的是 A 的實(shí)例.但不是同一個(gè)對(duì)象.
$a1 = A::getClassA();
$a2 = A::getClassA();
echo "\$a1 的類是 ".get_class($a1)." , \$a2 是 ".get_class($a1);
if($a1 === $a2){
   echo "<br> \$a1 \$a2 指向同一對(duì)象.";
}else{
   echo "<br> \$a1 \$a2 不是一個(gè)對(duì)象.";
}
?>

程序運(yùn)行結(jié)果為:

//不能用new實(shí)例化的類.
//static的方法留給外部訪問.
//在方法內(nèi)部返回實(shí)例.
//定義靜態(tài)屬性保證這個(gè)實(shí)例能被靜態(tài)方法調(diào)用.
//增加判斷部分.

$a1 的類是 A , $a2 是 A
$a1 $a2 指向同一對(duì)象.
到此,我們寫了一個(gè)最簡(jiǎn)單的 單件模式 。
現(xiàn)在,你可以嘗試寫一個(gè)應(yīng)用單件設(shè)計(jì)模式的數(shù)據(jù)庫(kù)連接類。
要記住單件模式的使用效果和書寫方式。

作者:黑馬程序員PHP+H5全棧培訓(xùn)學(xué)院

首發(fā): http://java.itheima.com







分享到:
在線咨詢 我要報(bào)名
和我們?cè)诰€交談!