溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

關(guān)于php中代理模式的案例分析

發(fā)布時間:2020-08-01 14:50:37 來源:億速云 閱讀:165 作者:清晨 欄目:編程語言

這篇文章主要介紹關(guān)于php中代理模式的案例分析,文中介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們一定要看完!

代理模式屬于結(jié)構(gòu)性設(shè)計模式,針對類與對象組合在一起的經(jīng)典結(jié)構(gòu)。代理模式也是一種使用較多的設(shè)計模式,需要我們重點掌握,他可以在不改變目標(biāo)對象的情況下,添加一些額外的功能。

定義

代理模式(Proxy)為其他對象提供一種代理以控制對這個對象的訪問。使用代理模式創(chuàng)建代理對象,讓代理對象控制目標(biāo)對象的訪問(目標(biāo)對象可以是遠(yuǎn)程的對象、創(chuàng)建開銷大的對象或需要安全控制的對象),并且可以在不改變目標(biāo)對象的情況下添加一些額外的功能。

問題

目前系統(tǒng)關(guān)于用戶登錄注冊的業(yè)務(wù),有一個Login類。偽代碼如下:

class UserLogin
{
    // …… 省略屬性和部分方法
    
    public function login ($name, $pass)
    {
        // 登錄業(yè)務(wù)
    }
    
    public function reg ($name, $pass)
    {
        // 注冊業(yè)務(wù)
    }
}

現(xiàn)在,我們想在用戶登錄和注冊的業(yè)務(wù)中添加一個功能——限流,讓客戶端調(diào)用該方法的頻次限制在一秒鐘最多5次?,F(xiàn)在,我們來實現(xiàn)該功能,偽代碼如下:

class UserLogin
{
    // …… 省略屬性和部分方法
    
    public function login ($name, $pass)
    {
        // 限流
        $limit = new Limit();
        if ($limit->restrict()) {
            // ……
        }
        
        // 登錄業(yè)務(wù)
    }
    
    public function reg ($name, $pass)
    {
        // 限流
        $limit = new Limit();
        if ($limit->restrict()) {
            // ……
        }
        
        // 注冊業(yè)務(wù)
    }
}

大家看看上面的代碼,它有幾個問題,首先,限流代碼侵入到業(yè)務(wù)代碼中,跟業(yè)務(wù)代碼高度耦合。其次,限流和業(yè)務(wù)代碼無關(guān),違背單一職責(zé)原則。

實現(xiàn)

接下來,我們用代理模式重寫上面的代碼,重寫后的代碼如下所示:

interface IUserLogin
{
    function login ();
    function register ();
}

class UserLogin implements IUserLogin
{
    // …… 省略屬性和部分方法
    
    public function reg ($uname, $pass)
    {
        // 注冊業(yè)務(wù)
    }
    
    public function login ($uname, $pass)
    {
        // 登錄業(yè)務(wù)
    }
}

class UserLoginProxy implements IUserLogin
{
    private $limit = null;
    private $login = null;
    
    public function __construct(Limit $limit, Login $login)
    {
        $this->limit = $limit;
        $this->login = $login;
    }
    
    public function login($uname, $pass)
    {
        if ($this->limit->restrict()) {
            // ...
        }
        return $this->login->login($uname, $pass);
    }
    
    public function register($uname, $pass)
    {
        if ($this->limit->restrict()) {
            // ...
        }
        return $this->login->register($uname, $pass);
    }
}

上面的方法是基于接口而非實現(xiàn)編程的設(shè)計思想,但如果原始類并沒有定義接口,或者這個類并不是我們開發(fā)和維護(hù)的,那么要怎么實現(xiàn)代理模式呢?

對于這種外部類的擴(kuò)展,我們一般采用繼承的方法來實現(xiàn)。

class UserLogin
{
    public function reg ($uname, $pass)
    {
        // 注冊業(yè)務(wù)
    }
    
    public function login ($uname, $pass)
    {
        // 登錄業(yè)務(wù)
    }
}

class UserLoginProxy extends Login
{
    private $limit = null;
    
    public function __construct(Limit $limit, Login $login)
    {
        $this->limit = $limit;
        $this->login = $login;
    }
    
    public function login($uname, $pass)
    {
        if ($this->limit->restrict()) {
            // ...
        }
        return parent::login($uname, $pass);
    }
    
    public function reg($uname, $pass)
    {
        if ($this->limit->restrict()) {
            // ...
        }
        return parent::register($uname, $pass);
    }
}

大家看看上面的代碼,是不是還有什么問題?你會發(fā)現(xiàn)

if ($this->limit->restrict()) {
    // ...
}

這段相似的代碼,出現(xiàn)了兩次。現(xiàn)在我們只是給兩個方法添加了限流功能,如果UserLogin類有10個方法,每個方法我們都想要添加限流的功能,那么我們就需要重復(fù)復(fù)制10次該段代碼。如果,我們想要給10給類中所有方法都添加限流功能,每個類中都有10個方法,那么上面的限流代碼將會重復(fù)100次。

當(dāng)然,你會說我可以將限流的代碼封裝到一個函數(shù)里不就解決了上述問題么?但還有一個問題解決不了,原始類里每個方法在代理類中都要重新實現(xiàn)一遍。就像上面原始類里有reg、login方法,代理類里也有reg、login方法。

動態(tài)代理

如何解決上述的問題,我們可以借助動態(tài)代理來解決。想要使用動態(tài)代理,就要理解并使用PHP中的反射機(jī)制。

php具有完整的反射 API,添加了對類、接口、函數(shù)、方法和擴(kuò)展進(jìn)行反向工程的能力。 此外,反射 API 提供了方法來取出函數(shù)、類和方法中的文檔注釋。關(guān)于php的反射相關(guān)知識,這里就不詳述了,大家可以自行查閱相關(guān)信息。

注意,使用反射對性能消耗很大,一般情況下請不要使用。

下面我們來展示如何用反射實現(xiàn)動態(tài)代理,偽代碼如下:

class UserLogin
{
    public function reg ($uname, $pass)
    {
        // 注冊業(yè)務(wù)
        echo '注冊業(yè)務(wù)' . PHP_EOL;
    }
    public function login ($uname, $pass)
    {
        // 登錄業(yè)務(wù)
        echo '登錄業(yè)務(wù)' . PHP_EOL;
    }
}
class LimitProxy
{
    // 用來保存多個實例對象
    private $target = [];
    public function __construct(Object $obj)
    {
        $this->target[] = $obj;
    }
    public function __call($name, $arguments)
    {
        foreach ($this->target as $obj) {
            $ref = new \ReflectionClass($obj);
            if ($method = $ref->getMethod($name)) {
                if ($method->isPublic() && !$method->isAbstract()) {
                    // 限流
                    echo "這里是限流業(yè)務(wù)處理" . PHP_EOL;
                    $result = $method->isStatic() ? $method->invoke(null, $obj, ...$arguments) : $method->invoke($obj, ...$arguments);
                    return $result;
                }
            }
        }
    }
}

測試代碼如下:

$login = new Login();
$loginProxy = new LimitProxy($login);
$loginProxy->reg('gwx', '111111');
$loginProxy->login('james', '111111111');

應(yīng)用場景

  • 訪問控制 (保護(hù)代理)。 比如系統(tǒng)有一個訂單的模塊,原本該模塊也有權(quán)限控制,但現(xiàn)在我們希望只針對指定ip的客戶端可以訪問,那么我們可以使用代理模式。

  • 本地執(zhí)行遠(yuǎn)程服務(wù) (遠(yuǎn)程代理)適用于服務(wù)對象位于遠(yuǎn)程服務(wù)器上的情形。

  • 在業(yè)務(wù)代碼中開發(fā)一些非功能性的需求,比如:限流、統(tǒng)計、日志記錄

  • 緩存方面的應(yīng)用,比如添加一個緩存代理,當(dāng)緩存存在時,就調(diào)用緩存代理獲取緩存的數(shù)據(jù),當(dāng)緩存不存在時,就調(diào)用原始接口。

以上是關(guān)于php中代理模式的案例分析的所有內(nèi)容,感謝各位的閱讀!希望分享的內(nèi)容對大家有幫助,更多相關(guān)知識,歡迎關(guān)注億速云行業(yè)資訊頻道!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI