프록시 패턴은 클래스와 객체가 결합된 고전적인 구조를 겨냥한 구조적 디자인 패턴입니다. 프록시 패턴은 우리가 집중해야 할 자주 사용되는 디자인 패턴이기도 합니다. 대상 객체를 변경하지 않고도 몇 가지 추가 기능을 추가할 수 있습니다.
정의
프록시 패턴(Proxy)은 다른 개체에 대한 프록시를 제공하여 이 개체에 대한 액세스를 제어합니다. 프록시 패턴을 사용하여 프록시 객체를 생성하고 프록시 객체가 대상 객체에 대한 액세스를 제어하도록 합니다(대상 객체는 원격 객체, 생성 비용이 많이 드는 객체 또는 보안 제어가 필요한 객체일 수 있음). 대상 객체 기능을 변경하지 않고 몇 가지 추가 항목을 추가합니다.
Question
현재 시스템에는 사용자 로그인 및 등록 업무를 위한 로그인 클래스가 있습니다. 의사 코드는 다음과 같습니다.
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()) { // …… } // 注册业务 } }
위 코드를 보면 몇 가지 문제가 있습니다. 첫째, 현재 제한 코드가 비즈니스 코드와 밀접하게 결합되어 있습니다. 둘째, 전류 제한은 비즈니스 코드와 아무런 관련이 없으며 단일 책임 원칙을 위반합니다.
Implementation
다음으로 위의 코드를 프록시 패턴을 사용하여 다시 작성합니다.
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가 있습니다. 또한 Reflection API는 함수, 클래스 및 메서드에서 문서 주석을 추출하는 메서드를 제공합니다. PHP Reflection과 관련된 지식은 여기서 자세히 다루지 않겠습니다. 관련 정보는 직접 확인하시면 됩니다.
리플렉션을 사용하면 성능이 많이 소모되니 주의하세요. 일반적인 상황에서는 사용하지 마세요.
리플렉션을 사용하여 동적 프록시를 구현하는 방법을 보여드리겠습니다. 의사 코드는 다음과 같습니다.
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 중국어 웹사이트의 기타 관련 기사를 참조하세요!