Structural Patterns

Adapter

The Adapter is about getting the interface you want from the interface that you were given, in real world terms think of a plug adapter that you use in different countries. You create a component which aggregates (has a reference to...) the adaptee.

Adapter example
package uk.co.datadisk.Structural.adapter;

// getting the interface you want from the interface you were given

// the adapter pattern can create temporary objects, to avoid recreating those
// objects use a cache

import java.util.*;
import java.util.function.Consumer;

class Point {
    public int x, y;

    public Point(int x, int y) {
        this.x = x;
        this.y = y;
    }

    @Override
    public String toString() {
        return "Point{" +
                "x=" + x +
                ", y=" + y +
                '}';
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Point point = (Point) o;
        return x == point.x &&
                y == point.y;
    }

    @Override
    public int hashCode() {
        return Objects.hash(x, y);
    }
}

class Line {
    public Point start, end;

    public Line(Point start, Point end) {
        this.start = start;
        this.end = end;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Line line = (Line) o;
        return Objects.equals(start, line.start) &&
                Objects.equals(end, line.end);
    }

    @Override
    public int hashCode() {
        return Objects.hash(start, end);
    }
}

class VectorObject extends ArrayList<Line> { }

class VectorRectangle extends VectorObject {
    public VectorRectangle(int x, int y, int width, int height) {
        add(new Line(new Point(x,y), new Point(x+width, y) ));
        add(new Line(new Point(x+width,y), new Point(x+width, y+height) ));
        add(new Line(new Point(x,y), new Point(x, y+height) ));
        add(new Line(new Point(x,y+height), new Point(x+width, y+height) ));
    }
}

class LineToPointAdapter implements Iterable<Point> {
    private static int count = 0;
    private static Map<Integer, List<Point>> cache = new HashMap<>();
    private int hash;


    public LineToPointAdapter(Line line) {
        // DATA
        // (1,1,10,10)
        // (3,3,6,6)

        hash = line.hashCode();
        if(cache.get(hash) != null){
            System.out.println("Already in cache");
            return;
        }

        System.out.println(
                String.format("%d: Generating points for line [%d,%d]-[%d,%d] (with caching)",
                        ++count, line.start.x, line.start.y, line.end.x, line.end.y));

        ArrayList<Point> points = new ArrayList<>();

        int left = Math.min(line.start.x, line.end.x);
        int right = Math.max(line.start.x, line.end.x);
        int top = Math.min(line.start.y, line.end.y);
        int bottom = Math.max(line.start.y, line.end.y);
        int dx = right - left;
        int dy = line.end.y - line.start.y;

        if (dx == 0)
        {
            for (int y = top; y <= bottom; ++y)
            {
                points.add(new Point(left, y));
            }
        }
        else if (dy == 0)
        {
            for (int x = left; x <= right; ++x)
            {
                points.add(new Point(x, top));
            }
        }

        System.out.println("Adding to cache");
        cache.put(hash, points);
    }

    @Override
    public Iterator<Point> iterator() {
        return cache.get(hash).iterator();
    }

    @Override
    public void forEach(Consumer<? super Point> action) {
        cache.get(hash).forEach(action);
    }

    @Override
    public Spliterator<Point> spliterator() {
        return cache.get(hash).spliterator();
    }

    public void getCache() {
        System.out.println("Cache: " + String.valueOf(cache));
    }
}

public class Main_Adapter_1 {

    private final static List<VectorObject> vectorObjects = new ArrayList<>(Arrays.asList(
            new VectorRectangle(1,1,10,10),
            new VectorRectangle(3,3, 6,6)
    ));

    public static void drawPoint(Point p){
        System.out.print(".");
    }

    private static void draw()
    {
        for (VectorObject vo : vectorObjects)
        {
            for (Line line : vo)
            {
                LineToPointAdapter adapter = new LineToPointAdapter(line);
                //adapter.getCache();
                adapter.forEach(Main_Adapter_1::drawPoint);
                System.out.println();
            }
        }
    }

    public static void main(String[] args) {
        System.out.println("------------- First Draw -------------------");
        draw();
        System.out.println("------------- Second Draw -------------------");
        draw();
    }
}

Bridge

The Bridge Pattern is about connecting components together through abstractions, this pattern prevents a Cartesian product complexity explosion. The Bridge Pattern is used when we need to decouple an abstraction from its implementation so that the two can vary independently.So you have a class that other classes inherit and these classes run on two different operating systems then you need any different enties to cope with the different variations as per the below images.

Before After

Bridge example
package uk.co.datadisk.Structural.bridge;

// connecting components together through abstractions
// prevents a 'Cartesian product' complexity explosion
// a mechanism that decouples an interface (hierarchy)
// from an implementation (hierarchy)

// Bride would replace below:
// Shape object -> Circle, Square
// Rendering -> Vector, Raster
// You would create VectorCircleRender, VectorSquareRender, RasterCircleRender, etc

import com.google.inject.AbstractModule;
import com.google.inject.Guice;
import com.google.inject.Inject;
import com.google.inject.Injector;

interface Renderer {
    void renderCircle(float radius);
}

class VectorRenderer implements Renderer {
    @Override
    public void renderCircle(float radius) {
        System.out.println("Drawing a circle of radius " + radius);
    }
}

class RasterRenderer implements Renderer {
    @Override
    public void renderCircle(float radius) {
        System.out.println("Drawing pixels for a circle of radius " + radius);
    }
}

abstract class Shape {

    // Composition of the Renderer will be injected
    protected Renderer renderer;

    public Shape(Renderer renderer) {
        this.renderer = renderer;
    }

    public abstract void draw();
    public abstract void resize(float factor);
}

class Circle extends Shape {

    public float radius;

    @Inject // due to the inject a VectorRenderer or a RasterRenderer will be injected (depending on the binding)
    public Circle(Renderer renderer) { super(renderer); }

//    public Circle(Renderer renderer, float radius) {
//        super(renderer);
//        this.radius = radius;
//    }

    @Override
    public void draw() {
        renderer.renderCircle(radius);
    }

    @Override
    public void resize(float factor) {
        radius *= factor;
    }
}

class ShapeModule extends AbstractModule {
    @Override
    protected void configure() {
        // the below means when someone injects a Render a
        // new VectorRenderer class will be created

        // choose either Vector or Raster
        bind(Renderer.class).to(VectorRenderer.class);
        //bind(Renderer.class).to(RasterRenderer.class);
    }
}

public class Main_Bridge_1 {

    public static void main(String[] args) {
//        RasterRender raster = new RasterRender();
//        VectorRenderer vector = new VectorRenderer();
//
//        Circle circle = new Circle(vector, 5);
//        circle.draw();
//
//        circle.resize(2);
//        circle.draw();

        // Using google Guice (Javax/Inject and Org/AopAlliance)
        Injector injector1 = Guice.createInjector(new ShapeModule());
        Circle instance1 = injector1.getInstance(Circle.class);
        instance1.radius = 3;
        instance1.draw();
        instance1.resize(2);
        instance1.draw();
    }
}

Composite

The Composite Pattern is a structural design pattern that allows composing objects into a tree-like structure and works with it as if it was a singular object. We use composition (instance variables that refers to other objects) to make compound objects. In the below image we get a Institute object that contains a Department Object which contains a Student Object.


Composite example
package uk.co.datadisk.Structural.composite;

// Treating individual and aggregate objects uniformly.

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

// this can contain a single GraphicObject or a group of GraphicObjects
class GraphicObject {
    protected String name = "Group";
    public String color;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public GraphicObject() {}

    // the group of GraphicObjects
    public List<GraphicObject> children = new ArrayList<>();

    private void print(StringBuilder stringBuilder,  int depth)
    {
        stringBuilder.append(String.join("", Collections.nCopies(depth, "*")))
                .append(depth > 0 ? " " : "")
                .append((color == null || color.isEmpty()) ? "" : color + " ")
                .append(getName())
                .append(System.lineSeparator());
        for (GraphicObject child : children)
            child.print(stringBuilder,  depth+1);
    }

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        print(sb, 0);
        return sb.toString();
    }
}

class Circle extends GraphicObject
{
    public Circle(String color)
    {
        name = "Circle";
        this.color = color;
    }
}

class Square extends GraphicObject
{
    public Square(String color)
    {
        name = "Square";
        this.color = color;
    }
}

public class Main_Composite_1 {

    public static void main(String[] args)
    {
        GraphicObject drawing = new GraphicObject();
        drawing.setName("My Drawing");
        drawing.children.add(new Square("Red"));
        drawing.children.add(new Circle("Yellow"));

        GraphicObject group = new GraphicObject();
        group.children.add(new Circle("Blue"));
        group.children.add(new Square("Blue"));
        drawing.children.add(group);

        System.out.println(drawing);
    }
}
Composite example
package uk.co.datadisk.Structural.composite;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.Spliterator;
import java.util.function.Consumer;

interface SomeNeurons extends Iterable<Neuron> {

    // Both Neuron and NeuronLayer use the same method below
    default void connectTo(SomeNeurons other){
        if (this == other ) return;

        for(Neuron from : this)
            for (Neuron to : other){
                from.out.add(to);
                to.in.add(from);
            }
    }
}

class Neuron implements SomeNeurons {
    public ArrayList<Neuron> in, out;

    @Override
    public Iterator<Neuron> iterator() {
        // A single object can masquerade as a collection by returning a single-element
        // collection containing only this

        // A collection with a single element
        return Collections.singleton(this).iterator();
    }

    @Override
    public void forEach(Consumer<? super Neuron> action) {
        action.accept(this);
    }

    @Override
    public Spliterator<Neuron> spliterator() {
        return Collections.singleton(this).spliterator();
    }
}

class NeuronLayer extends ArrayList<Neuron> implements SomeNeurons {}

public class Main_Composite_2 {

    public static void main(String[] args) {
        Neuron neuron1 = new Neuron();
        Neuron neuron2 = new Neuron();

        NeuronLayer layer1 = new NeuronLayer();
        NeuronLayer layer2 = new NeuronLayer();

        neuron1.connectTo(neuron2);
        neuron1.connectTo(layer1);
        layer1.connectTo(neuron1);
        layer1.connectTo(layer2);

    }
}

Decorator

The Decorator Pattern adds behavior without altering the class itself, so you want to add additional functionality to an existing class but don't want to amend the existing class, thus breaking the Open-Closed Princple (OCP), you many want to keep the new functionality separate (SRP). To summarize it facilitates the addition of behaviors to individual objects with inheriting from them. You have two options:


Decorator example (when you cannot extend from a class as it's final)
package uk.co.datadisk.Structural.decorator;

// Adding behavior without altering the class itself
// you don't rewrite or alter existing code
// keep new functionality separate but be able to interact with existing structures

// TWO OPTIONS:
// 1. inherit from required object if possible, some classes are final
// 2. build a decorator which simply references the decorated object

// String object is final, so you cannot extend it

import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;
import java.util.Locale;
import java.util.stream.IntStream;

class MagicString {
    // WE REFERENCE THE OBJECT TO GET ACCESS TO ALL ITS OTHER METHODS
    private String string;

    public MagicString(String string) {
        this.string = string;
    }

    // Your own API methods to extend the decorated String
    public long getNumberOfVowels(){
        return string.chars()
                .mapToObj(c -> (char) c)
                .filter(c -> "aeiou".contains(c.toString()))
                .count();
    }

    @Override
    public String toString() {
        return string;
    }

    // All the String delegation method are below
    // Just pick and choose what you need
    ////////////////////////////////////////////////////////////////////
    public int length() {
        return string.length();
    }

    public boolean isEmpty() {
        return string.isEmpty();
    }

    public char charAt(int index) {
        return string.charAt(index);
    }

    public int codePointAt(int index) {
        return string.codePointAt(index);
    }

    public int codePointBefore(int index) {
        return string.codePointBefore(index);
    }

    public int codePointCount(int beginIndex, int endIndex) {
        return string.codePointCount(beginIndex, endIndex);
    }

    public int offsetByCodePoints(int index, int codePointOffset) {
        return string.offsetByCodePoints(index, codePointOffset);
    }

    // Many, Many more.............
}

public class Main_Decorator_1 {

    public static void main(String[] args) {
        MagicString s = new MagicString("hello");
        System.out.println(s + " has " + s.getNumberOfVowels() + " vowels");
    }
}
Dynamic Decorator Composition example
package uk.co.datadisk.Structural.decorator;
              
// You can build new decorators at runtime and hence why its DYNAMIC

interface Shape {
   String info();
}

class Circle implements Shape {
    private float radius;

    public Circle() {}

    public Circle(float radius) {
        this.radius = radius;
    }

    public void resize(float factor){
        radius *= factor;
    }

    @Override
    public String info() {
        return "A circle of radius " + radius;
    }
}

class Square implements Shape {
    private float side;

    public Square() {}

    public Square(float side) {
        this.side = side;
    }

    public void resize(float factor){
        side *= factor;
    }

    @Override
    public String info() {
        return "A Square with sides of " + side;
    }
}


// The DECORATORS
//  - We are adding extra functionality (color or transparency)
class ColoredShape implements Shape {

    private Shape shape;
    private String color;

    public ColoredShape(Shape shape, String color) {
        this.shape = shape;
        this.color = color;
    }

    @Override
    public String info() {
        return shape.info() +  " has the color " + color;
    }
}

class TransparentShape implements Shape {
    private Shape shape;
    private int transparency;

    public TransparentShape(Shape shape, int transparency) {
        this.shape = shape;
        this.transparency = transparency;
    }

    @Override
    public String info() {
        return shape.info() + " has " + transparency + "% transparency";
    }
}

public class Main_Decorator_2 {

    public static void main(String[] args) {

        Circle circle = new Circle(10);
        System.out.println(circle.info());

        ColoredShape blueSquare = new ColoredShape(new Square(20), "blue");
        System.out.println(blueSquare.info());

        TransparentShape transparentCircle = new TransparentShape(new Circle(10), 5);
        System.out.println(transparentCircle.info());

        TransparentShape greenCircle50Transparent = new TransparentShape(new ColoredShape(new Circle(5), "green"), 50);
        System.out.println(greenCircle50Transparent.info());
    }
}
Static Decorator Composite example
package uk.co.datadisk.Structural.decorator;

// Static Decorator Composite

import java.util.function.Supplier;

interface Shape3 {
   String info();
}

class Circle3 implements Shape3 {
    private float radius;

    public Circle3() {}

    public Circle3(float radius) {
        this.radius = radius;
    }

    public void resize(float factor){
        radius *= factor;
    }

    @Override
    public String info() {
        return "A circle of radius " + radius;
    }
}

class Square3 implements Shape3 {
    private float side;

    public Square3() {}

    public Square3(float side) {
        this.side = side;
    }

    public void resize(float factor){
        side *= factor;
    }

    @Override
    public String info() {
        return "A Square with sides of " + side;
    }
}


// The DECORATORS
//  - We are adding extra functionality (color or transparency)
class ColoredShape3<T extends Shape> implements Shape {
    private Shape shape;
    private String color;

    public ColoredShape3(Supplier<? extends T> ctor, String color){
        this.shape = ctor.get();
        this.color = color;
    }

    @Override
    public String info() {
        return shape.info() + " has the color " + color;
    }
}

class TransparentShape3<T extends Shape> implements Shape{
    private Shape shape;
    private int transparency;

    public TransparentShape3(Supplier<? extends T> ctor, int transparency){
        this.shape = ctor.get();
        this.transparency = transparency;
    }

    @Override
    public String info() {
        return shape.info() + " has " + transparency + "% transparency";
    }
}

public class Main_Decorator_3 {

    public static void main(String[] args) {

        ColoredShape3<Square> blueSquare = new ColoredShape3<>( () -> new Square(20), "blue");
        System.out.println(blueSquare.info());

        TransparentShape3<ColoredShape3<Circle>> myCircle =
                new TransparentShape3<>( () ->
                        new ColoredShape3<>( () ->
                                new Circle(10), "red"), 50);
        System.out.println(myCircle.info());
    }
}
Adaptor-Decorator example
package uk.co.datadisk.Structural.decorator;

// Adaptor-Decorator

import java.util.stream.IntStream;

class MyStringBuilder {
    private StringBuilder sb;

    public MyStringBuilder() {
        this.sb = new StringBuilder();
    }

    public MyStringBuilder(String string){
        this.sb = new StringBuilder(string);
    }

    // ADAPTOR part (methods)
    public MyStringBuilder concat(String string){
        return new MyStringBuilder(sb.toString().concat(string));
    }

    public MyStringBuilder appendLine(String string){
        sb.append(string).append(System.lineSeparator());
        return this;
    }

    public String toString(){
        return sb.toString();
    }

    ////////////////////////////////////////////////////////////////
    // DECORATOR part
    // Use intellij to auto generate StringBuilder methods
    public StringBuilder append(Object obj) {
        return sb.append(obj);
    }

    // You would have to change every method to return a MyStringBuilder
    public MyStringBuilder append(String str) {
        sb.append(str);
        return this;
    }

    public StringBuilder append(StringBuffer sb) {
        return this.sb.append(sb);
    }

    public StringBuilder append(CharSequence s) {
        return sb.append(s);
    }

    public StringBuilder append(CharSequence s, int start, int end) {
        return sb.append(s, start, end);
    }

    public StringBuilder append(char[] str) {
        return sb.append(str);
    }

    public StringBuilder append(char[] str, int offset, int len) {
        return sb.append(str, offset, len);
    }

    public StringBuilder append(boolean b) {
        return sb.append(b);
    }

    public StringBuilder append(char c) {
        return sb.append(c);
    }

    // Many, Many more.............
}

public class Main_Decorator_4 {
    public static void main(String[] args) {
        MyStringBuilder myStr = new MyStringBuilder();
        myStr.append("hello").appendLine(" world!!");
        System.out.println(myStr.concat(" and this too"));
    }
}

Facade

The Facade Pattern exposes several components through a single interface, think of a single universal remote controller that can control a TV, DVD Player, Amplifier, Projector, etc. To summarize you provide a simplified API over a set of classes. 

Facade example
package uk.co.datadisk.Structural.facade;

// Exposing several components through a single interface

import java.io.BufferedReader;
import java.util.ArrayList;
import java.util.List;

class Buffer{
    private char[] characters;
    private int lineWidth;

    public Buffer(int lineHeight, int lineWidth) {
        this.characters = new char[lineHeight*lineWidth];
        this.lineWidth = lineWidth;
    }

    public char charAt(int x, int y){
        return characters[y * lineWidth + x];
    }
}

class Viewport{
    private final Buffer buffer;
    private final int width;
    private final int height;
    private final int offsetX;
    private final int offsetY;

    public Viewport(Buffer buffer, int width, int height, int offsetX, int offsetY) {
        this.buffer = buffer;
        this.width = width;
        this.height = height;
        this.offsetX = offsetX;
        this.offsetY = offsetY;
    }

    public char charAt(int x, int y){
        return buffer.charAt(x+offsetX, y+offsetY);
    }
}

class Console{
    private List<Viewport> viewports = new ArrayList<>();

    int width, height;

    public Console(int width, int height) {
        this.width = width;
        this.height = height;
    }

    public void addViewport(Viewport viewport){
        viewports.add(viewport);
    }

    // The FACADE method (hide all complex operations into one method)
    public static Console newConsole(int width, int height){
        Buffer buffer = new Buffer(height, width);
        Viewport viewport = new Viewport(buffer, width, height, 0, 0);
        Console console = new Console(width, height);
        console.addViewport(viewport);
        return console;
    }

    public void render(){
        int count = 0;
        for(int y = 0; y < height; ++y){
            for (int x = 0; x < width; ++x) {
                for(Viewport vp : viewports){
                    System.out.println(vp.charAt(x,y) + ++count);
                }
            }
            System.out.println();
        }
    }
}

public class Main_Facade_1 {

    public static void main(String[] args) {
        // The facade will replace all the below into a simple request for a
        // buffer, viewport and console

//        Buffer buffer = new Buffer(30, 20);
//        Viewport viewport = new Viewport(buffer, 30, 20, 0, 0);
//        Console console = new Console(30, 20);
//        console.addViewport(viewport);
//        console.render();

        // use the FACADE
        Console console = Console.newConsole(30, 20);
        console.render();

    }
}

Flyweight

The Flyweight Pattern is a space optimization technique that lets us use less memory by storing externally the data associated with similar objects, the bottom line is to avoid redundancy when storing data, think of it like compressing a file or image. It allows programs to support vast quantities of objects by keeping their memory consumption low.

Flyweight example
package uk.co.datadisk.Structural.flyweight;

// A space optimization technique that lets us use less memory by
// storing externally the data associated with similar objects
// bottom line - avoid redundancy when storing data

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.function.Function;

class User2 {
    static List<String> strings = new ArrayList<>();
    static int[] names;

    public User2(String fullName) {

        Function<String, Integer> getOrAdd = (String s) -> {
            int idx = strings.indexOf(s);
            if (idx != -1) {
                System.out.println(s + " already been added");
                return idx;
            } else {
                strings.add(s);
                return strings.size() - 1;
            }
        };

        names = Arrays.stream(fullName.split(" "))
                .mapToInt(s -> getOrAdd.apply(s))
                .peek(idx -> System.out.println("Index: " + idx))
                .toArray();
    }

    public static void displayNames() {
        for (int idx : names) {
            System.out.println(idx);
        }
        for (String name : strings) {
            System.out.println(name);
        }

        System.out.println(names[0]);
    }
}

public class Main_Flyweight_1 {

    public static void main(String[] args) {

        // This not really good, should really only create one User2 object and use data
        User2 user1 = new User2("Paul Valle");
        User2 user2 = new User2("Lorraine Valle");
        User2 user3 = new User2("Dominic Valle");
        User2 user4 = new User2("Jessica Valle");

        User2.displayNames();
    }
}
Flyweight example
package uk.co.datadisk.Structural.flyweight;

import java.util.ArrayList;
import java.util.List;

// this is bad
class FormattedText {
    private String plainText;
    private boolean[] capitalize;

    public FormattedText(String plainText) {
        this.plainText = plainText;
        capitalize = new boolean[plainText.length()];
    }

    public void capitalize(int start, int end) {
        for (int i = start; i <= end; i++) {
            capitalize[i] = true;
        }
    }

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < plainText.length(); i++) {
            char c = plainText.charAt(i);
            sb.append(
                    capitalize[i] ? Character.toUpperCase(c) : c
            );
        }
        return sb.toString();
    }
}

// Much better way of using flyweight pattern
class BetterFormattedText {

    private String plainText;
    private List<TextRange> formatting = new ArrayList<>();

    public BetterFormattedText(String plainText) {
        this.plainText = plainText;
    }

    public TextRange getRange(int start, int end) {
        TextRange range = new TextRange(start, end);
        formatting.add(range);
        return range;
    }

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < plainText.length(); ++i) {
            char c = plainText.charAt(i);
            for (TextRange range : formatting) {
                if (range.covers(i) && range.capitalize) {
                    c = Character.toUpperCase(c);
                } else if (range.covers(i) && range.lowerCase) {
                    c = Character.toLowerCase(c);
                }
            }
            sb.append(c);
        }
        return sb.toString();
    }

    // TextRange CLASS
    public class TextRange {
        public int start, end;
        public boolean capitalize, lowerCase;

        public TextRange(int start, int end) {
            this.start = start;
            this.end = end;
        }

        public boolean covers(int position) {
            return position >= start && position <= end;
        }
    }
}


public class Main_Flyweight_2 {

    public static void main(String[] args) {
        FormattedText ft = new FormattedText("This is a brave new world");
        ft.capitalize(10, 15);
        System.out.println(ft.toString());

        BetterFormattedText bft1 = new BetterFormattedText("Make Great Britain Great Again");
        bft1.getRange(11, 18).capitalize = true;
        System.out.println(bft1);

        BetterFormattedText bft2 = new BetterFormattedText("MILTON KEYNES CITY CENTER");
        bft2.getRange(0, 12).lowerCase = true;
        System.out.println(bft2);
    }
}

Proxy

The Proxy Pattern is a interface for accessing a particular resource, allows access control by acting as a pass through entity or a placeholder object. Some examples of Proxies can be logging, security access, etc. Remember that will call the original underlying Objects code they are just a wrapper with additional functionality.

Proxy and Decorator patterns are very similar, the key difference is that the Proxy Pattern will have an identical interface where as the Decorator pattern provides an enhanced interface, also the Decorator Pattern has reference to what its decorating, the Proxy Pattern does not.

Proxy example
package uk.co.datadisk.Structural.proxy;

// An interface for accessing a particular resource
// the interface should look like the interface you are proxying
// the proxy can add an additional functionality

// Protection Proxy

interface Drivable {
    void drive();
}

class Car implements Drivable {
    protected Driver driver;

    public Car(Driver driver) {
        this.driver = driver;
    }

    @Override
    public void drive() {
        System.out.println("You are driving the car");
    }
}

class Driver {
    public int age;

    public Driver(int age) {
        this.age = age;
    }
}

class CarProxy extends Car {

    public CarProxy(Driver driver) {
        super(driver);
    }

    @Override
    public void drive() {
        if(driver.age >= 16) {
            super.drive();
        } else {
            System.out.println("You are to young to drive this car");
        }
    }
}

public class Main_Proxy_1 {

    public static void main(String[] args) {

        // No checks
        Car car1 = new Car(new Driver(10));
        car1.drive();

        // The proxy has additional checking
        Car car2 = new CarProxy(new Driver(10));
        car2.drive();
    }
}
Property Proxy example
package uk.co.datadisk.Structural.proxy;

// Property Proxy

import java.util.Objects;

class Property<T> {
    private T value;

    public Property(T value) {
        this.value = value;
    }

    public T getValue() {
        // do some logging if required
        return value;
    }

    public void setValue(T value) {
        // putting logging or have a configurable
        this.value = value;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Property property = (Property) o;
        return Objects.equals(value, property.value);
    }

    @Override
    public int hashCode() {
        return Objects.hash(value);
    }
}

class Creature {
    private Property<Integer> agility = new Property<>(10);

    public void setAgility(int value) {
        // the value would be proxied and logged
        agility.setValue(value);
    }

    public int getAgility(){
        return agility.getValue();
    }
}

public class Main_Proxy_2 {

    public static void main(String[] args) {
        Creature creature = new Creature();
        creature.setAgility(20);
        System.out.println(creature.getAgility());

        // You cannot set the agility directly, so below wont work
        // creature.agility = 50;
    }
}
Dynamic Proxy (logging) example
package uk.co.datadisk.Structural.proxy;

// Dynamic Proxy for logging

// Proxy V Decorator
//  Proxy provides an identical interface, decorator provides an enhanced interface (additional fields, methods, etc)
//  Decorator typically aggregates (or has reference to) what it is decorating, proxy does not have to
//  Proxy might not even be working with a materialized object (Virtual Proxy)

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.Map;

interface Human {
    void walk();
    void talk();
}

class Person implements Human {

    @Override
    public void walk() {
        System.out.println("I am walking");
    }

    @Override
    public void talk() {
        System.out.println("I am talking");
    }
}

class LoggingHandler implements InvocationHandler {

    private final Object target;
    private Map<String, Integer> calls = new HashMap<>();

    public LoggingHandler(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        String name = method.getName();

        if(name.contains("toString")){
            return calls.toString();
        }
        calls.merge(name, 1, Integer::sum);

        return method.invoke(target, args);
    }
}

public class Main_Proxy_3 {

    // parameterize by type <T> and returns type T
    @SuppressWarnings("unchecked")
    public static <T> T withLogging(T target, Class<T> itf){
        return (T) Proxy.newProxyInstance(itf.getClassLoader(),
                new Class<?>[]{itf},
                new LoggingHandler(target));
    }

    public static void main(String[] args) {

        Person person = new Person();
        Human logged = withLogging(person, Human.class);
        logged.talk();
        logged.walk();
        logged.walk();

        System.out.println("Count: " + logged.toString());
    }
}