Proxy (patró de disseny)

En programació, el patró proxy és un patró de disseny de programari.

Proxy en UML
Proxy en LePUS3 (llegenda Arxivat 2018-03-14 a Wayback Machine.)

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 modifica

Java modifica

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 a retardar la costosa operació de càrrega d'un fitxer des del disc fins que aquesta operació és realment necessària. Si l'arxiu no és necessari, doncs, la costosa càrrega s'ha evitat.

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!
 }
}

Python modifica

#!/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()

C# modifica

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.

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

C++ modifica

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

Perl modifica

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.

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;

Vegeu també modifica

Enllaços externs modifica

A Wikimedia Commons hi ha contingut multimèdia relatiu a: Proxy