Structural Design Patterns provide different ways to create a class structure, for example using inheritance and composition to create a large object from small objects. Following design patterns come under this category.
Using the proxy pattern, a class represents the functionality of another class.
The Proxy Pattern is used to create a representative object that controls access to another object, which may be remote, expensive to create or in need of being secured. The Proxy can be very useful in controlling the access to the original object, especially when objects should have different access rights.
In the Proxy Pattern, a client does not directly talk to the original object, it delegates it calls to the proxy object which calls the methods of the original object. The important point is that the client does not know about the proxy, the proxy acts as an original object for the client.
The participants classes in the proxy pattern are:
RealSubject
and representing its services. The interface must be implemented by the proxy as well so that the proxy can be used in any location where the RealSubject
can be used.RealSubject
. Implements the same interface implemented by the RealSubject
so that the Proxy can be substituted for the RealSubject
. Controls access to the RealSubject
and may be responsible for its creation and deletion. Other responsibilities depend on the kind of proxy.There are three main variations to the Proxy pattern
Let's take a look at this in action with a sequence diagram.
When to use the Proxy pattern
Proxy is applicable whenever there is a need for a more versatile or sophisticated reference to an object than a simple pointer. Here are several common situations in which the Proxy pattern is applicable:
Java
Let’s say we have a class that can run some command on the system. Now if we are using it, its fine but if we want to give this program to a client application, it can have severe issues because client program can issue command to delete some system files or change some settings that you don’t want.
Here a proxy class can be created to provide controlled access of the program.
public interface CommandExecutor { public void runCommand(String cmd) throws Exception; }
import java.io.IOException; public class CommandExecutorImpl implements CommandExecutor { @Override public void runCommand(String cmd) throws IOException { //some heavy implementation Runtime.getRuntime().exec(cmd); System.out.println("'" + cmd + "' command executed."); } }
Now we want to provide only admin users to have full access of above class, if the user is not admin then only limited commands will be allowed. Here is our very simple proxy class implementation.
public class CommandExecutorProxy implements CommandExecutor { private boolean isAdmin; private CommandExecutor executor; public CommandExecutorProxy(String user, String pwd){ if("Admin".equals(user) && "qwerty".equals(pwd)) isAdmin = true; executor = new CommandExecutorImpl(); } @Override public void runCommand(String cmd) throws Exception { if(isAdmin){ executor.runCommand(cmd); }else{ if(cmd.trim().startsWith("rm")){ throw new Exception("rm command is not allowed for non-admin users."); }else{ executor.runCommand(cmd); } } } }
public class ProxyPatternTest { public static void main(String[] args){ CommandExecutor executor = new CommandExecutorProxy("Admin", "123456"); try { executor.runCommand("ls -ltr"); executor.runCommand("rm -rf abc.pdf"); } catch (Exception e) { System.out.println("Exception Message:: " + e.getMessage()); } } }
Python
import abc class CommandExecutor(metaclass=abc.ABCMeta): @abc.abstractmethod def runCommand(self, cmd): pass class CommandExecutorImpl(CommandExecutor): def runCommand(self, cmd): print("{0} command executed.".format(cmd)) class CommandExecutorProxy(CommandExecutor): def __init__(self, user, pwd): self.is_admin = False if "Admin" == user and "qwerty" == pwd: self.is_admin = True self.executor = CommandExecutorImpl() def runCommand(self, cmd): if self.is_admin: self.executor.runCommand(cmd) else: if cmd.strip().startswith("rm"): raise Exception("rm command is not allowed for non-admin users.") else: self.executor.runCommand(cmd) if __name__ == '__main__': executor = CommandExecutorProxy("Admin", "123456") try: executor.runCommand("ls -ltr"); executor.runCommand("rm -rf abc.pdf"); except Exception as e: print(e)