Proxy (patró de disseny)


En programació, el patró proxy és un patró de disseny de programari.
Un proxy, en la seva forma més general, és una classe que funciona com una interfície a una altra cosa. L'altra cosa podria ser qualsevol: una connexió a la xarxa, un gran objecte en la memòria, un arxiu, o algun altre recurs que és car o impossible de duplicar.
Un exemple ben conegut dels proxys és d'intermediari en un model referenciat amb punters a objectes.
En els casos que hi ha vaires còpies d'un objecte complex el patró proxy pot ser adaptat per incorporar al patró Flyweight per tal de reduir la necessitat de memòria.
Codis d'exemple
Java
El següent exemple en Java implementa el patró "virtual proxy". La sortida del programa és:
Loading HiRes_10MB_Photo1 Displaying HiRes_10MB_Photo1 Loading HiRes_10MB_Photo2 Displaying HiRes_10MB_Photo2 Displaying HiRes_10MB_Photo1
La classe ProxyImage és usada per per retardar la costosa operació de càrrega d'un fitxer des del disc fins que aquesta operació és realment necessaria. Si l'arxiu no és necessari, doncs, la costosa càrrega s'ha evitat.
<source lang="java">
import java.util.*;
interface Image {
public void displayImage();
}
class RealImage implements Image {
private String filename;
public RealImage(String filename) {
this.filename = filename;
loadImageFromDisk();
}
private void loadImageFromDisk() {
// Potentially expensive operation
// ...
System.out.println("Loading "+filename);
}
public void displayImage() { System.out.println("Displaying "+filename); }
}
class ProxyImage implements Image {
private String filename;
private Image image;
public ProxyImage(String filename) { this.filename = filename; }
public void displayImage() {
if (image == null) {
image = new RealImage(filename); // load only on demand
}
image.displayImage();
}
}
class ProxyExample {
public static void main(String[] args) {
Image image1 = new ProxyImage("HiRes_10MB_Photo1");
Image image2 = new ProxyImage("HiRes_10MB_Photo2");
Image image3 = new ProxyImage("HiRes_10MB_Photo3");
image1.displayImage(); // loading necessary
image2.displayImage(); // loading necessary
image1.displayImage(); // no loading necessary; already done
// the third image will never be loaded - time saved!
}
} </source>
Python
<source lang="python">
- !/opt/env/python
class Image(object): def display(self): pass
class RealImage(Image): def __init__(self, name): self.name = name self.load() def load(self): print "Loading...", self.name def display(self): print "Displaying...", self.name
class ProxyImage(Image): def __init__(self, name): self.name = name self.test = None def display(self): if not self.test: self.test = RealImage(self.name) self.test.display()
def main(): t1 = ProxyImage("big_image1.png") t2 = ProxyImage("big_image2.png") t3 = ProxyImage("big_image3.png")
t1.display() # loads, displays t2.display() # loads, displays t1.display() # displays
- t3.display() # no display, no load
return
if __name__ == "__main__": main() </source>
C#
En aquest exemple de C # , el RealClient emmagatzema un número de compte. Només els usuaris que coneixen una contrasenya vàlida poden accedir a aquest número de compte. El RealClient està protegit per una ProtectionProxy que sap la contrasenya. Si un usuari vol obtenir un número de compte, en primer lloc, el proxy demana a l'usuari autenticar-se, i només si l'usuari posa una contrasenya correcta el proxy no invoca el RealClient per obtenir un número de compte per a l'usuari.
En aquest exemple, thePassword és la contrasenya correcta. <source lang="csharp"> using System;
namespace ConsoleApplicationTest.FundamentalPatterns.ProtectionProxyPattern {
public interface IClient {
string GetAccountNo();
}
public class RealClient : IClient {
private string accountNo = "12345";
public RealClient() {
Console.WriteLine("RealClient: Initialized");
}
public string GetAccountNo() {
Console.WriteLine("RealClient's AccountNo: " + accountNo);
return accountNo;
}
}
public class ProtectionProxy : IClient
{
private string password; //password to get secret
RealClient client;
public ProtectionProxy(string pwd) {
Console.WriteLine("ProtectionProxy: Initialized");
password = pwd;
client = new RealClient();
}
// Authenticate the user and return the Account Number
public String GetAccountNo() {
Console.Write("Password: ");
string tmpPwd = Console.ReadLine();
if (tmpPwd == password) {
return client.GetAccountNo();
} else {
Console.WriteLine("ProtectionProxy: Illegal password!");
return "";
}
}
}
class ProtectionProxyExample
{
[STAThread]
public static void Main(string[] args) {
IClient client = new ProtectionProxy("thePassword");
Console.WriteLine();
Console.WriteLine("main received: " + client.GetAccountNo());
Console.WriteLine("\nPress any key to continue . . .");
Console.Read();
}
}
} </source>
C++
<source lang="cpp">
- include <string>
- include <iostream>
class Image //abstract { protected:
Image() {}
public:
virtual ~Image() {}
virtual void displayImage() = 0;
};
class RealImage : public Image { public:
explicit RealImage(std::string filename)
{
this->filename = filename;
loadImageFromDisk();
}
void displayImage()
{
std::cout<<"Displaying "<<filename<<std::endl;
}
private:
void loadImageFromDisk()
{
// Potentially expensive operation
// ...
std::cout<<"Loading "<<filename<<std::endl;
}
std::string filename;
};
class ProxyImage : public Image { public:
explicit ProxyImage(std::string filename)
{
this->filename = filename;
this->image = nullptr;
}
~ProxyImage() { delete image; }
void displayImage()
{
if (image == nullptr) {
image = new RealImage(filename); // load only on demand
}
image->displayImage(); }
private:
std::string filename; Image* image;
};
int main(int argc, char* argv[]) {
std::cout<<"main"<<std::endl;
Image* image1 = new ProxyImage("HiRes_10MB_Photo1");
Image* image2 = new ProxyImage("HiRes_10MB_Photo2");
Image* image3 = new ProxyImage("HiRes_10MB_Photo3");
image1->displayImage(); // loading necessary
image2->displayImage(); // loading necessary
image1->displayImage(); // no loading necessary; already done
// the third image will never be loaded - time saved!
delete image1; delete image2; delete image3;
return 0;
} </source>
Perl
The Perl with Moose implementation. The "lazy" option is used to delay the expensive operation. The "handles" option is used to create the delegation to proxied class.
<source lang="perl"> use MooseX::Declare;
role Image {
requires 'display_image';
}
class RealImage with Image {
has 'filename' => (
isa => 'Str',
);
# Moose's constructor
method BUILD ($params) {
$self->_load_image_from_disk;
}
method _load_image_from_disk () {
# Expensive operation
print "Loading " . $self->{filename} . "\n";
}
method display_image () {
print "Displaying " . $self->{filename} . "\n";
}
}
class ProxyImage with Image {
has 'filename' => (
isa => 'Str',
);
has 'image' => (
does => 'Image',
lazy => 1, # default value is loaded only on demand
default => sub { RealImage->new( filename => $_[0]->{filename} ) },
handles => [ 'display_image' ], # automatic delegation
);
}
class ProxyExample {
# Moose's constructor
method BUILD ($params) {
my $image1 = ProxyImage->new( filename=>'HiRes_10MB_Photo1' );
my $image2 = ProxyImage->new( filename=>'HiRes_10MB_Photo2' );
my $image3 = ProxyImage->new( filename=>'HiRes_10MB_Photo3' );
$image1->display_image; # loading necessary
$image2->display_image; # loading necessary
$image1->display_image; # already done
# the third image will never be loaded - time saved!
}
}
ProxyExample->new; </source>
Vegeu també
- Composite
- Decorator
- Servidor proxy (Proxy server)
Enllaços externs
- Patró Proxy en Java(anglès)
- Patró Proxy en UML en LePUS3 (modelatge formal)(anglès)
- Take control with the Proxy design pattern by David Geary, JavaWorld.com(anglès)
- Jt Patró J2EE Orientat en Framework (anglès)