A simple proxy
A simple proxy typically implements a single method. There are situations when multiple interfaces need to be implemented by a single class to behave in different ways. Dynamic proxy is a mechanism by which we can achieve the same, provided you follow certain guidelines and restrictions. While I was going thru the JavaWorld artcle (http://www.javaworld.com/javaworld/jw-11-2000/jw-1110-proxy.html) on DProxy, I noticed a case where the author is explaining the dynamic proxy with the example of automobiles.
If a vehicle is considered as a system primarily three categories of users can be thought of for the same – a passenger who rides, a mechanic who repairs and a driver who drives. Each one is concerned with the different perspective of the vehicle. On the other hand the vehicle should also be able to present three different interfaces for these users, such as the driver will be interested in the interface of driving which is presented by steering, gear, break, speedometer etc. Similarly the passenger will be presented with the passenger’s seat, door, window, music, television etc, while the mechanic should be able to inspect and repair the engine, chassis, tyres etc.
I tried implementing the case with Java. I will try to explain the same in this article.
To explain the situation I considered mainly six classes.
1. Drivable interface – To represent the interface of a vehicle designated for drivers.
2. Ridable interface - To represent the interface of a vehicle designated for passengers.
3. Repairable interface - To represent the interface of a vehicle designated for mechanics.
4. Vehicle class - To represent a vehicle that implements the three interfaces mentioned above.
5. VehicleInvocationHandler class - To represent the class responsible for creating the dynamic proxy class and map the methods defined in the above interfaces and the respective vehicle. This class in implementing java.lang.reflect. InvocationHandler class add the feature which will enable it to load the interfaces and the vehicle class dynamically at runtime and map them with each other.
6. DProxyTester class – Representing the client which uses the system as a client.
The figure below explains the structural aspect of the system.
The Java code for the classes and interfaces are given below.
Drivable.java
public interface Drivable {
public void drive(String source, String dest, int speed);
}
Ridable.java
public interface Ridable {
public void ride(String[] passengers);
}
Repairable.java
public interface Repairable {
public void repair(String[] parts);
}
Vehicle.java
public class Vehicle {
String name = null;
public Vehicle(String name){
this.name = name;
}
public void move(String start, String end, int speed) {
System.out.println(name +" is going from "+start+" to "+end+" at a speed of "+speed+"mph");
}
public void carry(String[] passengers) {
for (String str : passengers)
System.out.println(name+" is Carrying "+str);
}
public void getRepaired(String[] parts) {
for (String str : parts){
System.out.print(str+", ");
}
if(parts.length > 1){
System.out.println("are getting Repaired on "+name);
}
else{
System.out.println("is getting Repaired on "+name);
}
}
}
VehicleInvocationHandler.java
import java.lang.reflect.*;
class VehicleInvocationHandler implements InvocationHandler
{
Vehicle vehicle = null;
private VehicleInvocationHandler(Vehicle vehicle){
this.vehicle = vehicle;
}
public static Object getProxy(String name){
Vehicle vehicle = new Vehicle(name);
// the proxy can be provided as drivable, ridable or repairable.
Class[] interfaces = new Class[] { Drivable.class, Ridable.class, Repairable.class};
Object proxy = Proxy.newProxyInstance(vehicle.getClass().getClassLoader(), interfaces, new VehicleInvocationHandler(vehicle));
return proxy;
}
public Object invoke(Object proxy, Method method, Object[] args){
if(method.getName().equals("drive")){
vehicle.move((String)args[0], (String)args[1], ((Integer)args[2]).intValue());
}
if(method.getName().equals("ride")){
String[] passengers = (String[]) args[0];
vehicle.carry(passengers);
}
if(method.getName().equals("repair")){
String[] parts = (String[]) args[0];
vehicle.getRepaired(parts);
}
return null;
}
}
DProxyTester.java
public class DProxyTester
{
public static void main(String[] args){
Object proxy = VehicleInvocationHandler.getProxy("Bus");
Drivable d = (Drivable) proxy;
Ridable r = (Ridable) proxy;
Repairable p = (Repairable) proxy;
System.out.println("Dynamic Proxy created... ");
d.drive("Lombard", "Chicago", 60);
r.ride(new String[]{"Tom", "John", "Harry"});
p.repair(new String[]{"Wipper", "Head lights", "Gear Box"});
}
}
If a vehicle is considered as a system primarily three categories of users can be thought of for the same – a passenger who rides, a mechanic who repairs and a driver who drives. Each one is concerned with the different perspective of the vehicle. On the other hand the vehicle should also be able to present three different interfaces for these users, such as the driver will be interested in the interface of driving which is presented by steering, gear, break, speedometer etc. Similarly the passenger will be presented with the passenger’s seat, door, window, music, television etc, while the mechanic should be able to inspect and repair the engine, chassis, tyres etc.
I tried implementing the case with Java. I will try to explain the same in this article.
To explain the situation I considered mainly six classes.
1. Drivable interface – To represent the interface of a vehicle designated for drivers.
2. Ridable interface - To represent the interface of a vehicle designated for passengers.
3. Repairable interface - To represent the interface of a vehicle designated for mechanics.
4. Vehicle class - To represent a vehicle that implements the three interfaces mentioned above.
5. VehicleInvocationHandler class - To represent the class responsible for creating the dynamic proxy class and map the methods defined in the above interfaces and the respective vehicle. This class in implementing java.lang.reflect. InvocationHandler class add the feature which will enable it to load the interfaces and the vehicle class dynamically at runtime and map them with each other.
6. DProxyTester class – Representing the client which uses the system as a client.
The figure below explains the structural aspect of the system.
The figure below explains the structural aspect of the system.
Dynamic Proxy: The Automobile Case
From the above structure it’s obvious that the client i.e. DProxyTester will remain unaffected from any changes in Vehicle class.The Java code for the classes and interfaces are given below.
Drivable.java
public interface Drivable {
public void drive(String source, String dest, int speed);
}
Ridable.java
public interface Ridable {
public void ride(String[] passengers);
}
Repairable.java
public interface Repairable {
public void repair(String[] parts);
}
Vehicle.java
public class Vehicle {
String name = null;
public Vehicle(String name){
this.name = name;
}
public void move(String start, String end, int speed) {
System.out.println(name +" is going from "+start+" to "+end+" at a speed of "+speed+"mph");
}
public void carry(String[] passengers) {
for (String str : passengers)
System.out.println(name+" is Carrying "+str);
}
public void getRepaired(String[] parts) {
for (String str : parts){
System.out.print(str+", ");
}
if(parts.length > 1){
System.out.println("are getting Repaired on "+name);
}
else{
System.out.println("is getting Repaired on "+name);
}
}
}
VehicleInvocationHandler.java
import java.lang.reflect.*;
class VehicleInvocationHandler implements InvocationHandler
{
Vehicle vehicle = null;
private VehicleInvocationHandler(Vehicle vehicle){
this.vehicle = vehicle;
}
public static Object getProxy(String name){
Vehicle vehicle = new Vehicle(name);
// the proxy can be provided as drivable, ridable or repairable.
Class[] interfaces = new Class[] { Drivable.class, Ridable.class, Repairable.class};
Object proxy = Proxy.newProxyInstance(vehicle.getClass().getClassLoader(), interfaces, new VehicleInvocationHandler(vehicle));
return proxy;
}
public Object invoke(Object proxy, Method method, Object[] args){
if(method.getName().equals("drive")){
vehicle.move((String)args[0], (String)args[1], ((Integer)args[2]).intValue());
}
if(method.getName().equals("ride")){
String[] passengers = (String[]) args[0];
vehicle.carry(passengers);
}
if(method.getName().equals("repair")){
String[] parts = (String[]) args[0];
vehicle.getRepaired(parts);
}
return null;
}
}
DProxyTester.java
public class DProxyTester
{
public static void main(String[] args){
Object proxy = VehicleInvocationHandler.getProxy("Bus");
Drivable d = (Drivable) proxy;
Ridable r = (Ridable) proxy;
Repairable p = (Repairable) proxy;
System.out.println("Dynamic Proxy created... ");
d.drive("Lombard", "Chicago", 60);
r.ride(new String[]{"Tom", "John", "Harry"});
p.repair(new String[]{"Wipper", "Head lights", "Gear Box"});
}
}