Table of Contents
Is used to form large object structures across many disparate objects. It allows for object level access control by acting as a pass through entity or a placeholder object.
It is quite a simple concept: to save on the amount of memory used, you might use a Proxy. Similarly if you want to control access to an object, this pattern becomes useful.
Proxy provides a barrier between client and the real implementation.
There are many different flavors of Proxy, depending on its purpose. You may have a protection proxy, to control access rights to an object. A virtual proxy handles the case where an object might be expensive to create, and a remote proxy controls access to a remote object.
This pattern is recommended when:
- object being represented is external to the system (network, database)
- objects need to be created on demand
- access control for original object is required
- added functionality is required when an object is accessed
Typically, you will want to use a proxy when communication with a third party is an expensive operation, perhaps over a network. The proxy would allow you to hold your data until you are ready to commit, and can limit the amount of times that communication is called.
Proxy is also useful if you want to decouple actual implementation code from access to a particular library. Proxy is also useful for access to large files, or graphics. By using a proxy, you can delay loading the resource until you really need the data inside.
Client
-----> Proxy
-----> Real Object
Let's continue with the idea of using proxy for loading images. First, we should create a common interface for the real and proxy implementations to use:
public interface Image {
public void displayImage();
}
The RealImage
implementation of this interface works as you would expect:
public class RealImage implements Image {
public RealImage(URL url) {
//load up the image
loadImage(url);
}
public void displayImage() {
//display the image
}
//a method that only the real image has
private void loadImage(URL url) {
//do resource intensive operation to load image
}
}
Now the Proxy implementation can be written, which provides access to the RealImage
class. Note that it is only when we call displayImage()
method that it actually uses the RealImage
. Until then, we don't need the data.
public class ProxyImage implements Image {
private URL url;
ReadlImage realImg;
public ProxyImage(URL url) {
this.url = url;
}
//this method delegates to the real image
public void displayImage() {
if(realImg == null) {
realImg = new RealImage(url);
}
realImg.displayImage();
}
}
And it is really as simple as that. As far as the client is concerned, they will just deal with the interface.
Type | Description |
---|---|
Remote Proxy | Represents an object locally which belongs to a different address space. Think of an ATM implementation, it will hold proxy objects for bank information that exists in the remote server. RMI is an example of proxy implementation for this type in java |
Virtual Proxy | In pace of a complex or heavy object, use a skeleton representation. When an underlying image is huge in size, just represent it using a virtual proxy object and on demand load the real object. You feel that the real object is expensive in terms of instantiation and so without the real need, we are not going to use the real object. Until the need arises, we will use virtual proxy object. |
Protection Proxy | In a corporate environment, servers uses censorship proxies, which limits the content and provides only work related web pages. |
Smart Reference | Just we keep a link/reference to the real object, a kind of pointer. |
To extend or modify the behavior of 'an instance' at runtime, decorator pattern is used. Inheritance is used to extend the abilities of 'a class'. Unlike inheritance, you can choose any single object of a class and modify its behavior leaving the other instances unmodified.
In implementing the decorator pattern, you construct a wrapper around an object by extending its behavior. The wrapper will do its job before or after and delegate the call to the wrapped instance.
Decorator pattern allows behavior to be added to an individual object, either statically or dynamically, without affecting the behavior of other objects from the same class. Decorator pattern is often useful for adhering to the Single Responsibility Principle, as it allows functionality to be divided between classes with unique areas of concern.
This pattern is designed so that multiple decorators can be stacked on top of each other, each time adding a new functionality to the overridden method(s).
Following example illustrates the use of decorators using coffee making scenario. In this example, the scenario only includes cost and ingredients.
// The abstract Coffee class defines the functionality of Coffee implemented by decorator
public abstract class Coffee {
public abstract double getCost(); // Returns the cost of the coffee
public abstract String getIngredients(); // Returns the ingredients of the coffee
}
// Extension of a simple coffee without any extra ingredients
public class SimpleCoffee extends Coffee {
public double getCost() {
return 1;
}
public String getIngredients() {
return "Coffee";
}
}
The following classes contain the decorators for all Coffee classes, including the decorator classes themselves.
// Abstract decorator class - note that it extends Coffee abstract class
public abstract class CoffeeDecorator extends Coffee {
protected final Coffee decoratedCoffee;
public CoffeeDecorator(Coffee c) {
this.decoratedCoffee = c;
}
public double getCost() { // Implementing methods of the abstract class
return decoratedCoffee.getCost();
}
public String getIngredients() {
return decoratedCoffee.getIngredients();
}
}
// Decorator WithMilk mixes milk into coffee.
// Note it extends CoffeeDecorator.
class WithMilk extends CoffeeDecorator {
public WithMilk(Coffee c) {
super(c);
}
public double getCost() { // Overriding methods defined in the abstract superclass
return super.getCost() + 0.5;
}
public String getIngredients() {
return super.getIngredients() + ", Milk";
}
}
// Decorator WithSprinkles mixes sprinkles onto coffee.
// Note it extends CoffeeDecorator.
class WithSprinkles extends CoffeeDecorator {
public WithSprinkles(Coffee c) {
super(c);
}
public double getCost() {
return super.getCost() + 0.2;
}
public String getIngredients() {
return super.getIngredients() + ", Sprinkles";
}
}
Here's a test program that creates a Coffee instance which is fully decorated (with milk and sprinkles), and calculate cost of coffee and prints its ingredients:
public class Main {
public static void printInfo(Coffee c) {
System.out.println("Cost: " + c.getCost() + "; Ingredients: " + c.getIngredients());
}
public static void main(String[] args) {
Coffee c = new SimpleCoffee();
printInfo(c);
c = new WithMilk(c);
printInfo(c);
c = new WithSprinkles(c);
printInfo(c);
}
}
The output of this program is as follows:
Cost: 1.0; Ingredients: Coffee
Cost: 1.5; Ingredients: Coffee, Milk
Cost: 1.7; Ingredients: Coffee, Milk, Sprinkles
Composite pattern is used where we need to treat a group of objects in a similar way as a single object. Composite pattern composes objects in terms of a tree structure to represent part as well as whole hierarchy. This type of design pattern comes under structural pattern as this pattern creates a tree structure of group of objects.
This pattern creates a class that contains group of its own objects. This class provides ways to modify its group of same objects.
For example, graphics primitives such as lines or text must be drawn, moved, and resized. But we also want to perform the same operation on composites, such as drawings, that are composed of those primitives. Ideally, we'd like to perform operations on both primitive objects and composites in exactly the same manner, without distinguishing between the two. If we must distinguish between primitive objects and composites to perform the same operations on those two types of objects, our code would become more complex and more difficult to implement, maintain and extend.