Tôi có một triển khai khác cho điều đó trong PHP> = 5.3.0 và giống như một Decorator mà Northborn Design đã giải thích.
Tất cả những gì chúng tôi cần là API để tạo tiện ích và trình trang trí để áp dụng các tiện ích. Chúng ta phải nhớ rằng trên phương pháp mở rộng C# chúng không phá vỡ sự đóng gói của đối tượng được mở rộng, và chúng không sửa đổi đối tượng (không có điểm nào cho điều đó, thay vào đó thực hiện phương thức sẽ hiệu quả hơn). Và các phương pháp mở rộng là tinh khiết tĩnh và họ nhận được trường hợp của các đối tượng, như trong ví dụ dưới đây (C#, from MSDN):
public static int WordCount(this String str)
{
return str.Split(new char[] { ' ', '.', '?' },
StringSplitOptions.RemoveEmptyEntries).Length;
}
Dĩ nhiên chúng ta không có các đối tượng String trong PHP, nhưng đối với tất cả các đối tượng khác chúng ta có thể tạo ra các trang trí chung cho loại thuật sĩ đó.
Cho phép xem thi tôi cho rằng:
API:
<?php
namespace Pattern\Extension;
/**
* API for extension methods in PHP (like C#).
*/
class Extension
{
/**
* Apply extension to an instance.
*
* @param object $instance
* @return \Pattern\Extension\ExtensionWrapper
*/
public function __invoke($instance)
{
return Extension::apply($instance);
}
/**
* Apply extension to an instance.
*
* @param object $instance
* @return \Pattern\Extension\ExtensionWrapper
*/
public static function apply($instance)
{
return new ExtensionWrapper($instance, \get_called_class());
}
/**
* @param mixed $instance
* @return boolean
*/
public static function isExtensible($instance)
{
return ($instance instanceof Extensible);
}
}
?>
Các Decorator:
<?php
namespace Pattern\Extension;
/**
* Demarcate decorators that resolve the extension.
*/
interface Extensible
{
/**
* Verify the instance of the holded object.
*
* @param string $className
* @return bool true if the instance is of the type $className, false otherwise.
*/
public function holdsInstanceOf($className);
/**
* Returns the wrapped object.
* If the wrapped object is a Extensible the returns the unwrap of it and so on.
*
* @return mixed
*/
public function unwrap();
/**
* Magic method for the extension methods.
*
* @param string $name
* @param array $args
* @return mixed
*/
public function __call($name, array $args);
}
?>
Và việc thực hiện chung:
<?php
namespace Pattern\Extension;
/**
* Generic version for the Extensible Interface.
*/
final class ExtensionWrapper implements Extensible
{
/**
* @var mixed
*/
private $that;
/**
* @var Extension
*/
private $extension;
/**
* @param object $instance
* @param string | Extension $extensionClass
* @throws \InvalidArgumentException
*/
public function __construct($instance, $extensionClass)
{
if (!\is_object($instance)) {
throw new \InvalidArgumentException('ExtensionWrapper works only with objects.');
}
$this->that = $instance;
$this->extension = $extensionClass;
}
/**
* {@inheritDoc}
* @see \Pattern\Extension\Extensible::__call()
*/
public function __call($name, array $args)
{
$call = null;
if (\method_exists($this->extension, '_'.$name)) {
// this is for abstract default interface implementation
\array_unshift($args, $this->unwrap());
$call = array($this->extension, '_'.$name);
} elseif (\method_exists($this->extension, $name)) {
// this is for real implementations
\array_unshift($args, $this->unwrap());
$call = array($this->extension, $name);
} else {
// this is for real call on object
$call = array($this->that, $name);
}
return \call_user_func_array($call, $args);
}
/**
* {@inheritDoc}
* @see \Pattern\Extension\Extensible::unwrap()
*/
public function unwrap()
{
return (Extension::isExtensible($this->that) ? $this->that->unwrap() : $this->that);
}
/**
* {@inheritDoc}
* @see \Pattern\Extension\Extensible::holdsInstanceof()
*/
public function holdsInstanceOf($className)
{
return \is_a($this->unwrap(), $className);
}
}
?>
Sử dụng:
Giả sử một bữa tiệc hạng Ba tồn tại:
class ThirdPartyHello
{
public function sayHello()
{
return "Hello";
}
}
Tạo mở rộng của bạn:
use Pattern\Extension\Extension;
class HelloWorldExtension extends Extension
{
public static function sayHelloWorld(ThirdPartyHello $that)
{
return $that->sayHello().' World!';
}
}
Plus: Đối với giao diện Lovers, tạo ra một Extension Tóm tắt:
<?php
interface HelloInterfaceExtension
{
public function sayHelloFromInterface();
}
?>
<?php
use Pattern\Extension\Extension;
abstract class AbstractHelloExtension extends Extension implements HelloInterfaceExtension
{
public static function _sayHelloFromInterface(ThirdPartyOrLegacyClass $that)
{
return $that->sayHello(). ' from Hello Interface';
}
}
?>
Sau đó sử dụng nó :
////////////////////////////
// You can hide this snippet in a Dependency Injection method
$thatClass = new ThirdPartyHello();
/** @var ThirdPartyHello|HelloWorldExtension $extension */
$extension = HelloWorldExtension::apply($thatClass);
//////////////////////////////////////////
$extension->sayHello(); // returns 'Hello'
$extension->sayHelloWorld(); // returns 'Hello World!'
//////////////////////////////////////////
// Abstract extension
$thatClass = new ThirdPartyHello();
/** @var ThirdPartyHello|HelloInterfaceExtension $extension */
$extension = AbstractHelloExtension::apply($instance);
$extension->sayHello(); // returns 'Hello'
$extension->sayHelloFromInterface(); // returns 'Hello from Hello Interface'
Ưu điểm: cách
- Rất tương tự của phương pháp # mở rộng C trong PHP;
- Không thể kiểm tra trực tiếp một cá thể mở rộng như một thể hiện của đối tượng mở rộng, nhưng điều này là tốt đẹp vì nó an toàn hơn vì chúng ta có thể có những nơi mà thể hiện của bế tắc đó không được mở rộng;
- Vì mục đích của một khuôn khổ để cải thiện sự nhanh nhẹn của nhóm bạn phải viết ít hơn;
- Việc sử dụng mở rộng có vẻ là một phần của đối tượng nhưng nó chỉ được trang trí (có thể điều này rất thú vị đối với các nhóm phát triển nhanh nhưng xem xét trong tương lai việc thực hiện đối tượng mở rộng đó nếu có liên quan);
- Bạn có thể sử dụng phương pháp tĩnh của tiện ích mở rộng trực tiếp để cải thiện hiệu suất nhưng làm như vậy bạn mất khả năng giả lập các phần mã của bạn (DI được chỉ định cao).
Nhược điểm:
- Việc gia hạn phải được khai báo cho đối tượng, nó là không chỉ là một khẩu như trong C#, bạn phải "trang trí" thể hiện mong muốn để cung cấp cho phần mở rộng nó.
- Không thể kiểm tra trực tiếp một cá thể tiện ích mở rộng làm phiên bản của đối tượng mở rộng, nhiều mã hơn để kiểm tra bằng cách sử dụng API;
- Hạn chế hiệu suất do sử dụng các phương pháp ma thuật (nhưng khi thực hiện là cần thiết, chúng tôi thay đổi ngôn ngữ, tái tạo lõi, sử dụng khung công tác tối thiểu, lắp ráp nếu cần);
Dưới đây là một Gist cho Api rằng: https://gist.github.com/tennaito/9ab4331a4b837f836ccdee78ba58dff8
Strings không đối tượng trong PHP. –
Tôi xin lỗi, "phương pháp khuyến nông" là gì? nó khác với các phương pháp khác như thế nào? – Artefacto
@Artefacto: "hello" .MyExtention ("another"); 'không giải thích được? – caesay