What is Proxy Pattern?
Proxy is one of famous structural design pattern to “provide a surrogate or placeholder for another object to control access to it” (G.o.F).
In this diagram, you can see that Client want to doSomething() by the RealSubject. But we don’t want Client to interact with ReadSubject directly. Maybe there’s security reason or RealSubject is very costly, we can not deliver it to each Client. Instead we provide a proxy to each client, and when the client request to doSomething(), the RealSubject will do then.
When to use Proxy Pattern?
You use Proxy Pattern when you want to provide a representative of another object, for reasons such as access, speed, or security.
Proxy Pattern Example
Let take ATM and Bank as our example. The bank has so many customers and the cost for accessing to the Bank is so high (cost for traveling to the bank, cost for paying receptionist…). So that they provide ATMs, and via the ATM, customer can use the IBank to interact with the bank. Forgive me if I’m not good at describing but I will explain this better in Java language ![]()
First I will create an iBank interface which define some basic interactions between customers and bank.
package net.searchdaily.java.design.pattern.proxy;
/**
* Proxy Pattern tutorial by http://java.searchdaily.net
*
* @author namnvhue
*
*/
public interface IBank {
/**
* Check account and return current balance
*
* @param accountId
* @return
*/
public double checkBalance(String accountId);
/**
* Withdraw amt $ from bank and return new balance if success, if not return
* current balance.
*
* @param accountId
* @param amt
* @return
*/
public double withDraw(String accountId, double amt);
/**
* Deposite $amt into account, if success return new balance, if not return
* current balance.
*
* @param accountId
* @param amt
* @return
*/
public double deposit(String accountId, double amt);
}
Then I will create a Central Computer for the Bank, this computer will hold all of account and do all transactions (supposed so)
package net.searchdaily.java.design.pattern.proxy;
import java.util.HashMap;
import java.util.Map;
/**
* Proxy Pattern tutorial by http://java.searchdaily.net
*
* @author namnvhue
*
*/
public class BankCentralComputer implements IBank {
Map accounts = new HashMap();
public BankCentralComputer() {
accounts.put("12345", 20000.0);
accounts.put("11223", 30000.0);
accounts.put("33112", 60000.0);
}
@Override
public double checkBalance(String accountId) {
if (validateAccount(accountId)) {
return this.accounts.get(accountId);
} else {
System.out.println("Invalid Account");
return 0.0;
}
}
@Override
public double withDraw(String accountId, double amt) {
if (validateAccount(accountId)) {
double currentBalance = this.accounts.get(accountId);
double newBalance = currentBalance - amt;
if (newBalance >= 0.0) {
// Save account data
this.accounts.put(accountId, newBalance);
return newBalance;
} else {
System.out.println("You don't have enough money");
return currentBalance;
}
} else {
System.out.println("Invalid Account");
return 0.0;
}
}
@Override
public double deposit(String accountId, double amt) {
if (validateAccount(accountId)) {
double currentBalance = this.accounts.get(accountId);
double newBalance = currentBalance + amt;
this.accounts.put(accountId, newBalance);
return newBalance;
} else {
System.out.println("Invalid Account");
return 0.0;
}
}
boolean validateAccount(String accountId) {
return this.accounts.containsKey(accountId);
}
}
Now as it’s costly to use the Bank, they provide ATM everywhere for their customers
package net.searchdaily.java.design.pattern.proxy;
/**
* Proxy Pattern tutorial by http://java.searchdaily.net
*
* @author namnvhue
*
*/
public class ATM implements IBank {
private IBank proxifiedBank;
public ATM(IBank realBank) {
if (realBank != null) {
this.proxifiedBank = realBank;
} else {
this.proxifiedBank = new BankCentralComputer();
}
}
@Override
public double checkBalance(String accountId) {
return this.proxifiedBank.checkBalance(accountId);
}
@Override
public double withDraw(String accountId, double amt) {
return this.proxifiedBank.withDraw(accountId, amt);
}
@Override
public double deposit(String accountId, double amt) {
return this.proxifiedBank.deposit(accountId, amt);
}
}
As you can see, the ATM can do exactly what the Central Computer of the Bank do. Actually, the ATM cannot do stuff like withDraw, deposit or checkBalance, but they have to ask another subject to do it. In this case if we construct the ATM without any specific real subject for it, it will use the Central Computer.
Now let’s see how some engineer will test the ATM at his weekend ![]()
package net.searchdaily.java.design.pattern.proxy;
/**
* Proxy Pattern tutorial by http://java.searchdaily.net
*
* @author namnvhue
*
*/
public class ATMTester {
/**
* @param args
*/
public static void main(String[] args) {
IBank atm = new ATM(null);
final String myAccountId = "12345";
double currentBalance = atm.checkBalance(myAccountId);
System.out.println("I have in my bank: $" + currentBalance);
// Money save after a week
double newBalance = atm.deposit(myAccountId, 500.5);
System.out.println("I deposit $" + 500.5
+ " after saving for a week. Now I have: $" + newBalance);
System.out
.println("it's weekend, I'll go to Las Vegas to have some fun");
atm.withDraw(myAccountId, 20000.0);
System.out
.println("The next Monday morning, I go back and check my account...and the ATM tell me");
newBalance = atm.checkBalance(myAccountId);
System.out.println("Your current balance: $" + newBalance);
}
}
Here is the result as you expect:
It’s the BankCentralComputer actually do the job can you see? Now I will turn this example into RMI stuff.
What is RMI btw?
Here is a brief from SUN: “The design goal for the RMI architecture was to create a Java distributed object model that integrates naturally into the Java programming language and the local object model. RMI architects have succeeded; creating a system that extends the safety and robustness of the Java architecture to the distributed computing world.”
How does RMI utilize the Proxy Pattern?
- Remember that a Java interface does not contain executable code. RMI supports two classes that implement the same interface. The first class is the implementation of the behavior, and it runs on the server. The second class acts as a proxy for the remote service and it runs on the client. This is shown in the following diagram. (from SUN)
- A client program makes method calls on the proxy object, RMI sends the request to the remote JVM, and forwards it to the implementation. Any return values provided by the implementation are sent back to the proxy and then to the client’s program. (from SUN)
Proxy Pattern Example to RMI (with Java 6)
First is the interface, the same thing as we got in the previous example
package net.searchdaily.java.design.pattern.proxy.rmi;
import java.rmi.Remote;
import java.rmi.RemoteException;
/**
* Proxy Pattern with RMI tutorial by http://java.searchdaily.net
*
* @author namnvhue
*
*/
public interface IBankRMI extends Remote {
/**
* Check account and return current balance
*
* @param accountId
* @return
*/
public double checkBalance(String accountId) throws RemoteException;
/**
* Withdraw amt $ from bank and return new balance if success, if not return
* current balance.
*
* @param accountId
* @param amt
* @return
*/
public double withDraw(String accountId, double amt) throws RemoteException;
/**
* Deposite $amt into account, if success return new balance, if not return
* current balance.
*
* @param accountId
* @param amt
* @return
*/
public double deposit(String accountId, double amt) throws RemoteException;
}
Then we will need an implementation of this interface, the BankCentralComputerRMI as what we did in the previous example.
package net.searchdaily.java.design.pattern.proxy.rmi;
import java.rmi.RemoteException;
import java.util.HashMap;
import java.util.Map;
/**
* Proxy Pattern with RMI tutorial by http://java.searchdaily.net
*
* @author namnvhue
*
*/
public class BankCentralComputerRMI implements IBankRMI {
/**
*
*/
private static final long serialVersionUID = 1L;
Map accounts = new HashMap();
protected BankCentralComputerRMI() throws RemoteException {
super();
accounts.put("12345", 20000.0);
accounts.put("11223", 30000.0);
accounts.put("33112", 60000.0);
}
@Override
public double checkBalance(String accountId) throws RemoteException {
if (validateAccount(accountId)) {
return this.accounts.get(accountId);
} else {
System.out.println("Invalid Account");
return 0.0;
}
}
@Override
public double withDraw(String accountId, double amt) throws RemoteException {
if (validateAccount(accountId)) {
double currentBalance = this.accounts.get(accountId);
double newBalance = currentBalance - amt;
if (newBalance >= 0.0) {
// Save account data
this.accounts.put(accountId, newBalance);
return newBalance;
} else {
System.out.println("You don't have enough money");
return currentBalance;
}
} else {
System.out.println("Invalid Account");
return 0.0;
}
}
@Override
public double deposit(String accountId, double amt) throws RemoteException {
if (validateAccount(accountId)) {
double currentBalance = this.accounts.get(accountId);
double newBalance = currentBalance + amt;
this.accounts.put(accountId, newBalance);
return newBalance;
} else {
System.out.println("Invalid Account");
return 0.0;
}
}
boolean validateAccount(String accountId) {
return this.accounts.containsKey(accountId);
}
}
Now let’s compile the two above java file with javac command.
With RMI, you won’t need to create the Proxy yourself as what you’ve done with Proxy Pattern, instead Java will do the rest for you. Have a look at the architecture:
In RMI’s use of the Proxy pattern, the stub class plays the role of the proxy, and the remote service implementation class plays the role of the RealSubject. A skeleton is a helper class that is generated for RMI to use. The skeleton understands how to communicate with the stub across the RMI link. The skeleton carries on a conversation with the stub; it reads the parameters for the method call from the link, makes the call to the remote service implementation object, accepts the return value, and then writes the return value back to the stub. (from SUN).
With Java 6 and the above implementation, you won’t need the RMIC command to build your stub, when you run the BankServer like below code, UnicastRemoteObject will export the Stub for you into the RMIRegistry.
package net.searchdaily.java.design.pattern.proxy.rmi;
import java.rmi.AlreadyBoundException;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.server.UnicastRemoteObject;
/**
* Proxy Pattern with RMI tutorial by http://java.searchdaily.net
*
* @author namnvhue
*
*/
public class BankServer {
public BankServer() {
}
public static void main(String[] args) {
try {
IBankRMI bank = new BankCentralComputerRMI();
IBankRMI stub = (IBankRMI) UnicastRemoteObject
.exportObject(bank, 0);
// Bind the remote object's stub in the registry
Registry registry = LocateRegistry.getRegistry();
registry.bind("bankService", stub);
System.out.println("The Bank server is running...");
} catch (RemoteException e) {
e.printStackTrace();
} catch (AlreadyBoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
Now let’s see how we start and use the BankServer:
The Server is up and ready, now let’s create a BankClient to use this BankRMI
package net.searchdaily.java.design.pattern.proxy.rmi;
import java.rmi.NotBoundException;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
/**
* Proxy Pattern with RMI tutorial by http://java.searchdaily.net
*
* @author namnvhue
*
*/
public class BankClient {
/**
* @param args
*/
public static void main(String[] args) {
try {
Registry registry = LocateRegistry.getRegistry("localhost");
IBankRMI bank = (IBankRMI) registry.lookup("bankService");
final String myAccountId = "12345";
double currentBalance = bank.checkBalance(myAccountId);
System.out.println("I have in my bank: $" + currentBalance);
// Money save after a week
double newBalance = bank.deposit(myAccountId, 500.5);
System.out.println("I deposit $" + 500.5
+ " after saving for a week. Now I have: $" + newBalance);
System.out
.println("it's weekend, I'll go to Las Vegas to have some fun");
bank.withDraw(myAccountId, 20000.0);
System.out
.println("The next Monday morning, I go back and check my account...and the ATM tell me");
newBalance = bank.checkBalance(myAccountId);
System.out.println("Your current balance: $" + newBalance);
} catch (RemoteException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (NotBoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
And here is the result:
That’s all for today, I’ll see you next time ![]()
thx,good example…thank you for your articles…l get a lot …
a javaer from china..