搜尋
首頁後端開發php教程一文讀懂php設計模式之代理模式

一文讀懂php設計模式之代理模式

Jul 31, 2020 pm 04:59 PM
php設計模式代理模式

代理模式屬於結構性設計模式,針對類別與物件組合在一起的經典結構。代理模式也是一種使用較多的設計模式,需要我們重點掌握,他可以在不改變目標物件的情況下,加入一些額外的功能。

定義

代理模式(Proxy)為其他物件提供一種代理以控制對這個物件的存取。使用代理模式建立代理對象,讓代理對象控制目標對象的存取(目標對象可以是遠端的對象、建立開銷大的對像或需要安全控制的對象),並且可以在不改變目標對象的情況下添加一些額外的功能。

問題

目前系統關於使用者登入註冊的業務,有一個Login類別。偽代碼如下:

class UserLogin
{
    // …… 省略属性和部分方法
    
    public function login ($name, $pass)
    {
        // 登录业务
    }
    
    public function reg ($name, $pass)
    {
        // 注册业务
    }
}

現在,我們想在使用者登入和註冊的業務中加入一個功能-限流,讓客戶端呼叫該方法的頻次限制在一秒鐘最多5次。現在,我們來實現該功能,偽代碼如下:

class UserLogin
{
    // …… 省略属性和部分方法
    
    public function login ($name, $pass)
    {
        // 限流
        $limit = new Limit();
        if ($limit->restrict()) {
            // ……
        }
        
        // 登录业务
    }
    
    public function reg ($name, $pass)
    {
        // 限流
        $limit = new Limit();
        if ($limit->restrict()) {
            // ……
        }
        
        // 注册业务
    }
}

大家看看上面的程式碼,它有幾個問題,首先,限流程式碼侵入到業務程式碼中,跟業務程​​式碼高度耦合。其次,限流和業務代碼無關,違反單一職責原則。

實作

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

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

class UserLogin implements IUserLogin
{
    // …… 省略属性和部分方法
    
    public function reg ($uname, $pass)
    {
        // 注册业务
    }
    
    public function login ($uname, $pass)
    {
        // 登录业务
    }
}

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);
    }
}

上面的方法是基於接口而非實現程式設計的設計思想,但如果原始類別並沒有定義接口,或者這個類別並不是我們開發和維護的,那麼要怎麼實現代理模式呢?

對於這種外部類別的擴展,我們一般會採用繼承的方法來實作。

class UserLogin
{
    public function reg ($uname, $pass)
    {
        // 注册业务
    }
    
    public function login ($uname, $pass)
    {
        // 登录业务
    }
}

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);
    }
}

大家看看上面的程式碼,是不是還有什麼問題?你會發現

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

這段相似的程式碼,出現了兩次。現在我們只是為兩個方法增加了限流功能,如果UserLogin類別有10個方法,每個方法我們都想要新增限流的功能,那麼我們就需要重複複製10次該段程式碼。如果,我們想要為10給類別中所有方法都加入限流功能,每個類別中都有10個方法,那麼上面的限流程式碼將會重複100次。

當然,你會說我可以將限流的程式碼封裝到一個函數裡不就解決了上述問題麼?但還有一個問題解決不了,原始類別裡每個方法在代理類別中都要重新實作一遍。就像上面原始類別裡有reg、login方法,代理類別裡也有reg、login方法。

動態代理程式

如何解決上述的問題,我們可以藉助動態代理來解決。想要使用動態代理,就要理解並使用PHP中的反射機制。

php具有完整的反射 API,增加了對類別、介面、函數、方法和擴充進行反向工程的能力。此外,反射 API 提供了方法來取出函數、類別和方法中的文件註解。關於php的反射相關知識,這裡就不詳述了,大家可以自行查閱相關資訊。

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

下面我們來展示如何用反射實作動態代理,偽代碼如下:

class UserLogin
{
    public function reg ($uname, $pass)
    {
        // 注册业务
        echo '注册业务' . PHP_EOL;
    }
    public function login ($uname, $pass)
    {
        // 登录业务
        echo '登录业务' . 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 "这里是限流业务处理" . 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');

應用程式場景

  • 存取控制(保護代理程式)。例如係統有一個訂單的模組,原本該模組也有權限控制,但現在我們希望只針對指定ip的客戶端可以訪問,那麼我們可以使用代理模式。

  • 本機執行遠端服務 (遠端代理)適用於服務物件位於遠端伺服器上的情況。

  • 在業務程式碼中開發一些非功能性的需求,例如:限流、統計、日誌記錄

  • 快取方面的應用,例如新增一個快取代理,當快取存在時,就呼叫快取代理來取得快取的數據,當快取不存在時,就呼叫原始介面。

#

以上是一文讀懂php設計模式之代理模式的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
如何使PHP應用程序更快如何使PHP應用程序更快May 12, 2025 am 12:12 AM

tomakephpapplicationsfaster,關注台詞:1)useopcodeCachingLikeLikeLikeLikeLikePachetoStorePreciledScompiledScriptbyTecode.2)MinimimiedAtabaseSqueriSegrieSqueriSegeriSybysequeryCachingandeffeftExting.3)Leveragephp7 leveragephp7 leveragephp7 leveragephpphp7功能forbettercodeefficy.4)

PHP性能優化清單:立即提高速度PHP性能優化清單:立即提高速度May 12, 2025 am 12:07 AM

到ImprovephPapplicationspeed,關注台詞:1)啟用opcodeCachingwithapCutoredUcescriptexecutiontime.2)實現databasequerycachingingusingpdotominiminimizedatabasehits.3)usehttp/2tomultiplexrequlexrequestsandreduceconnection.4 limitesclection.4.4

PHP依賴注入:提高代碼可檢驗性PHP依賴注入:提高代碼可檢驗性May 12, 2025 am 12:03 AM

依赖注入(DI)通过显式传递依赖关系,显著提升了PHP代码的可测试性。1)DI解耦类与具体实现,使测试和维护更灵活。2)三种类型中,构造函数注入明确表达依赖,保持状态一致。3)使用DI容器管理复杂依赖,提升代码质量和开发效率。

PHP性能優化:數據庫查詢優化PHP性能優化:數據庫查詢優化May 12, 2025 am 12:02 AM

DatabasequeryoptimizationinPHPinvolvesseveralstrategiestoenhanceperformance.1)Selectonlynecessarycolumnstoreducedatatransfer.2)Useindexingtospeedupdataretrieval.3)Implementquerycachingtostoreresultsoffrequentqueries.4)Utilizepreparedstatementsforeffi

簡單指南:帶有PHP腳本的電子郵件發送簡單指南:帶有PHP腳本的電子郵件發送May 12, 2025 am 12:02 AM

phpisusedforsenderemailsduetoitsbuilt-inmail()函數andsupportivelibrariesLikePhpMailerAndSwiftMailer.1)usethemail()functionForbasiceMails,butithasimails.2)butithasimail.2)

PHP性能:識別和修復瓶頸PHP性能:識別和修復瓶頸May 11, 2025 am 12:13 AM

PHP性能瓶颈可以通过以下步骤解决:1)使用Xdebug或Blackfire进行性能分析,找出问题所在;2)优化数据库查询并使用缓存,如APCu;3)使用array_filter等高效函数优化数组操作;4)配置OPcache进行字节码缓存;5)优化前端,如减少HTTP请求和优化图片;6)持续监控和优化性能。通过这些方法,可以显著提升PHP应用的性能。

PHP的依賴注入:快速摘要PHP的依賴注入:快速摘要May 11, 2025 am 12:09 AM

依賴性注射(DI)InphpisadesignPatternthatManages和ReducesClassDeptions,增強量強制性,可驗證性和MATIALWINABIOS.ItallowSpasspassingDepentenciesLikEdenciesLikedAbaseConnectionStoclasseconnectionStoclasseSasasasasareTers,interitationAseTestingEaseTestingEaseTestingEaseTestingEasingAndScalability。

提高PHP性能:緩存策略和技術提高PHP性能:緩存策略和技術May 11, 2025 am 12:08 AM

cachingimprovesphpermenceByStorcyResultSofComputationsorqucrouctationsorquctationsorquickretrieval,reducingServerLoadAndenHancingResponsetimes.feftectivestrategiesinclude:1)opcodecaching,whereStoresCompiledSinmememorytssinmemorytoskipcompliation; 2)datacaching datacachingsingMemccachingmcachingmcachings

See all articles

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

Video Face Swap

Video Face Swap

使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱門文章

熱工具

VSCode Windows 64位元 下載

VSCode Windows 64位元 下載

微軟推出的免費、功能強大的一款IDE編輯器

mPDF

mPDF

mPDF是一個PHP庫,可以從UTF-8編碼的HTML產生PDF檔案。原作者Ian Back編寫mPDF以從他的網站上「即時」輸出PDF文件,並處理不同的語言。與原始腳本如HTML2FPDF相比,它的速度較慢,並且在使用Unicode字體時產生的檔案較大,但支援CSS樣式等,並進行了大量增強。支援幾乎所有語言,包括RTL(阿拉伯語和希伯來語)和CJK(中日韓)。支援嵌套的區塊級元素(如P、DIV),

MantisBT

MantisBT

Mantis是一個易於部署的基於Web的缺陷追蹤工具,用於幫助產品缺陷追蹤。它需要PHP、MySQL和一個Web伺服器。請查看我們的演示和託管服務。

記事本++7.3.1

記事本++7.3.1

好用且免費的程式碼編輯器

SAP NetWeaver Server Adapter for Eclipse

SAP NetWeaver Server Adapter for Eclipse

將Eclipse與SAP NetWeaver應用伺服器整合。