This article is the 20th day of "WACUL Advent Calendar 2016". Recently, I personally read back metaprogramming ruby and used it partly at work ~ ~ There is no fresh material to talk about ~ ~, so this time I will write about dynamic proxy.
Actually, I'm not sure how common this word is ... Personally, "a programming pattern that receives the name and parameters of an undefined method and delegates the desired processing to another object" I recognize that.
This is useful when you want to write a wrapper for an external system.
For example, suppose you want to write a client library that uses an external REST api. For the sake of simplicity, let's limit ourselves to GET.
GET /api/hoge
To execute, for example, using requests in python
import requests
class Client:
def hoge(self):
return requests.get('/api/hoge')
client = Client()
client.hoge()
And so on. Nothing is difficult. However, in this case, if fuga, piyo, etc. increase
class Client:
def hoge(self):
return requests.get('/api/hoge')
def fuga(self):
return requests.get('/api/fuga')
def piyo(self):
return requests.get('/api/piyo')
It is difficult because the number of copies is increasing.
Therefore, we use "a special method that is called when a method or property cannot be found". In python, methods and properties are equal and object attributes, so you can use ``` getattr` ``.
class Client:
def __getattr__(self, name):
def _func(*args, **kwargs):
return requests.get('/api/' + name)
return _func
Now you can support client.hoge (), client.fuga () and client.piyo (), and best of all, you don't have to maintain the Client class as more apis are added in the future. Also, depending on the implementation of `` `_func```, you are free to handle the parameters you pass to client.method.
First, let's do it in Ruby. This time, I will use method_missing because I want to not only reduce the amount of code but also dynamically correspond to unknown specifications.
a.rb
class A
def method_missing(name, *args, &blk)
p name.to_s
p args
return if not block_given?
blk.call
end
end
a = A.new
a.hoge
a.fuga(1, 2, 3)
a.piyo do |i, j|
p "piyopiyo"
end
When you run it, it looks like this
"hoge"
[]
"fuga"
[1, 2, 3]
"piyo"
[]
"piyopiyo"
Next is PHP. Use `__ call`
. I remembered it when I was writing at work ~~ when it was written ~~. It feels good when combined with Reflection. For static methods, there is another method called __callStatic
.
a.php
<?php
class A
{
public function __call($name, $args)
{
var_dump($name);
var_dump($args);
}
}
$a = new A();
$a->hoge();
$a->fuga("fugaarg");
When I run it with cli, it looks like this
string(4) "hoge"
array(0) {
}
string(4) "fuga"
array(1) {
[0]=>
string(7) "fugaarg"
}
If you use it too much, the code tends to be difficult to read. Use it systematically.
Recommended Posts