Wednesday, February 9, 2011

Structural Patterns - Proxy Pattern

The proxy pattern is used when you need to represent a complex with a simpler one. If creation of object is expensive, its creation can be postponed till the very need arises and till then, a simple object can represent it. This simple object is called the “Proxy” for the complex object

 

Definition

Use a simple object to represent a complex one or provide a placeholder for another object to control access to it. 


Intent

  • Provide a surrogate or placeholder for another object to control access to it.
  • Use an extra level of indirection to support distributed, controlled, or intelligent access.
  • Add a wrapper and delegation to protect the real component from undue complexity.

Problem

You need to support resource-hungry objects, and you do not want to instantiate such objects unless and until they are actually requested by the client.

Where to use & benefits

Structure

By defining a Subject interface, the presence of the Proxy object standing in place of the RealSubject is transparent to the client.
Proxy scheme

 

Intent

  • The intent of this pattern is to provide a “Placeholder” for an object to control references to it.

Implementation

The figure below shows a UML class diagram for the Proxy Pattern:
Proxy Pattern Implementation - UML Class Diagram

 

 

Example

The Proxy provides a surrogate or place holder to provide access to an object. A check or bank draft is a proxy for funds in an account. A check can be used in place of cash for making purchases and ultimately controls access to cash in the issuer’s account.
Proxy example 
 
 
 
Bank.java
package structural.proxy;
/**
* Bank.java
* The class acts as the main object for which
* the proxy has to be created. As described, going
* to bank for withdrawal is very costly time wise.
*/
public class Bank {
  private int numberInQueue;
/**
* Method getMoneyForPurchase
* This method is responsible for the entire banking
* operation described in the write-up
*/
public double getMoneyForPurchase(double amountNeeded) {

// get object for person
You you = new You("Prashant");
// get obj for account
Account account = new Account();
// get person's account number
String accountNumber = you.getAccountNumber();
// passbook got.
boolean gotPassbook = you.getPassbook();
// get number in queue
int number = getNumberInQueue();

// the number will decrease every few mins
while (number != 0) {
number--;
}

// now when the number = 0, check if balance is sufficient
boolean isBalanceSufficient = account.checkBalance(accountNumber, amountNeeded);

if(isBalanceSufficient)
return amountNeeded;
else
return 0;
}

/**
* returns the number in the queue
*/
private int getNumberInQueue() {
return numberInQueue;
}

}// End of class
Also, the second class is ATMProxy. This also defines the way the transaction can be handled for withdrawal of money.

ATMProxy.java

package structural.proxy;

public class ATMProxy {

  /**
* Method getMoneyForPurchase
* This method is responsible for the entire banking
* operation described in the write-up
*/
public double getMoneyForPurchase(double amountNeeded) {

// get obj of You to get card
You you = new You("Prashant");
// get obj for account
Account account = new Account();

boolean isBalanceAvailable = false;
// if card there, go ahead
if(you.getCard()) {
isBalanceAvailable = account.checkBalance(you.getAccountNumber(), amountNeeded);
}

if(isBalanceAvailable)
return amountNeeded;
else
return 0;
}
}// End of class
Here, we can also create another proxy called ChequeProxy. I am not creating it here as the message I wanted to send across has been conveyed with the help of one proxy only. We can see here that creation of object of Bank is very costly, effort and time wise, and so, we can as well use a proxy called ATM to get the result. ATM can internally communicate with the Bank object. So, ATM here is also acting like a façade.
This might and might not happen. It can happen that we at a later stage have to create the same heavy object. We just want to postpone the creation of object to the last minute so that the application is not loaded by resources utilized for creating the object.
 
 
 
 

Proxy design pattern

  1. Create a “wrapper” for a remote, or expensive, or sensitive target
  2. Encapsulate the complexity/overhead of the target in the wrapper
  3. The client deals with the wrapper
  4. The wrapper delegates to the target
  5. To support plug-compatibility of wrapper and target, create an interface
import java.io.*;  import java.net.*;

// 5. To support plug-compatibility between
// the wrapper and the target, create an interface
interface SocketInterface {
  String readLine();
  void  writeLine( String str );
  void  dispose();
}

public class ProxyDemo {
  public static void main( String[] args ) {

    // 3. The client deals with the wrapper
    SocketInterface socket = new SocketProxy( "127.0.0.1", 8189,
      args[0].equals("first") ? true : false );

    String  str = null;
    boolean skip = true;
    while (true) {
      if (args[0].equals("second")  &&  skip) {
        skip = ! skip;
      }
      else {
        str = socket.readLine();
        System.out.println( "Receive - " + str );  // java ProxyDemo first
        if (str.equals("quit")) break;             // Receive - 123 456
      }                                            // Send ---- 234 567
      System.out.print( "Send ---- " );            // Receive - 345 678
      str = Read.aString();                        //
      socket.writeLine( str );                     // java ProxyDemo second
      if (str.equals("quit")) break;               // Send ---- 123 456
    }                                              // Receive - 234 567
    socket.dispose();                              // Send ---- 345 678
  }
}

class SocketProxy implements SocketInterface {
  // 1. Create a "wrapper" for a remote,
  // or expensive, or sensitive target
  private Socket      socket;
  private BufferedReader in;
  private PrintWriter   out;

  public SocketProxy( String host, int port, boolean wait ) {
    try {
      if (wait) {
        // 2. Encapsulate the complexity/overhead of the target in the wrapper
        ServerSocket server = new ServerSocket( port );
        socket = server.accept();
      } else
        socket = new Socket( host, port );
        in  = new BufferedReader( new InputStreamReader(
                                        socket.getInputStream()));
        out = new PrintWriter( socket.getOutputStream(), true );
      } catch( IOException e ) {
        e.printStackTrace();
      }
  }
  public String readLine() {
    String str = null;
    try {
      str = in.readLine();
    } catch( IOException e ) {
      e.printStackTrace();
    }
    return str;
  }
  public void writeLine( String str ) {
    // 4. The wrapper delegates to the target
    out.println( str );
  }
  public void dispose() {
    try {
      socket.close();
    } catch( IOException e ) {
      e.printStackTrace();
    }
  }
}
 

No comments:

Post a Comment