Alternate / Proxy Design Pattern

    Read the description of other patterns.

    Problem


    It is necessary to control access to the object without changing the behavior of the client.

    Description


    When designing complex systems, quite often there is a need to provide controlled access to certain system objects. The motivation for this is a number of acquired advantages. Such as, lazy on-demand initialization for "bulky" objects, counting the number of references to the object, etc. etc. However, the need for controlled access to an object is not always based only on benefits. As a rule, the complexity of real-world processes, the limitations of computing resources, do not leave the designer any choice than how to use the “Deputy” (“Surogat”) pattern.

    The idea of ​​the “Deputy” pattern is to provide the client with another object (alternate), in return for the object with controlled access. At the same time, the substitute object implements the same interface as the original object, as a result of which, the client behavior does not require changes. In other words, the client interacts with the substitute exactly as with the original object through a single interface. The client also does not make assumptions about whether he is working with a real object or his deputy. At the same time, access control to the object is achieved through the use of a link to it in the substitute, due to which the substitute forwards external calls to the controlled object, possibly accompanying them with additional operations.

    This approach allows the client to implicitly control access to the object.

    Practical task


    Consider the task of implementing the Minesweeper game. We will assume that for cells (Cell), which are mined (Mine) and empty (Empty), there are some cumbersome graphic images. For a mined cell - mine, for an empty cell an image with the number of mines in neighboring cells. At the same time, the image itself is stored in each cell and is instantiated at the time of its creation. The player, however, sees the image of the cell only after it is opened (operation open ()). Therefore, it would be prudent to instantiate cells at the moment the player tries to open them in order to reduce the cost of shared memory for storing images. However, this approach cannot be applied here. The fact is that before the open () operation, each cell calls getTop (), getLeft () to get the coordinates of the cell. But if the cell has not yet been created,

    Using a proxy pattern solves this problem. Instead of the original objects, the client will use their surveyors (MineProxy, EmptyProxy). In this case, lazy initialization of the cells becomes possible, since the original object is created only when the open () operation is called on the proxy, and the proxy responds to requests for coordinates (getTop (), getLeft ()) independently, at least until the original object.

    Class diagram




    Java implementation


    /**
    * Абстрактный класс ячейки минного поля
    */
    public abstract class Cell {
      public static final int OPENED = 0;
      public static final int CLOSED = 1;
      
      protected int status;

      protected int left, top;

      public Cell(int left, int top) {
        super();
        
        this.left = left;
        this.top = top;
        this.status = Cell.CLOSED;
      }
      
      /**
       * Открыть данную ячейку. Будем считать, что в этой операции происходит некоторая
       * ресурсоемкая операция. Например, загрузка изображения, для отображения содержимого ячейки.
       */
      public void open() {  
        this.status = Cell.OPENED;
      }

      public int getLeft() {
        return left;
      }
      
      public int getTop() {
        return top;
      }
      
      public int getStatus() {
        return status;
      }

      /**
       * Единственная абстрактная операция, возвращаяет количество очков за открытие данной ячейки.
       */
      public abstract int getPoints();  
    }

    /**
    * Уточнение ячейки минного поля, в качестве пустой ячейки
    */
    public class Empty extends Cell {
      
      public Empty(int left, int top) {
        super(left, top);
        
        // загружаем тяжелое изображение пустой ячейки.
      }

      @Override
      public int getPoints() {
        return 10;  // 10 очков за открытую пустую ячейку
      }
    }

    /**
    * Уточнение ячейки, как ячейки с миной.
    */
    public class Mine extends Cell {
      
      public Mine(int left, int top) {
        super(left, top);
        
        // загружаем тяжелое изображение ячейки c миной
      }

      @Override
      public int getPoints() {
        return 100;   // 100 очков за открытую мину
      }
    }

    /**
    * Прокси для пустой ячейки
    */
    public class EmptyProxy extends Cell {
      private Empty proxy; // ссылка на пустую ячейку

      public EmptyProxy(int left, int top) {
        super(left, top);
        this.proxy = null;
      }

      /**
       * Ленивая инициализация пустой ячейки
       */
      @Override
      public void open() {
        if (proxy == null) {
          proxy = new Empty(left, top);
        }
        
        proxy.open();
      }

      @Override
      public int getLeft() {
        if (proxy == null) {
          return left;
        } else {
          return proxy.getLeft();
        }
      
      }
      
      @Override
      public int getTop() {
        if (proxy == null) {
          return top;
        } else {
          return proxy.getTop();
        }
      }

      @Override
      public int getStatus() {
        if (proxy == null) {
          return status;
        } else {
          return proxy.getStatus();
        }
      }

      @Override
      public int getPoints() {
        if (proxy == null) {
          return 10;
        } else {
          return proxy.getPoints();
        }
      }
    }

    /**
    * Прокси для ячейки с миной
    */
    public class MineProxy extends Cell {
      private Mine proxy;

      public MineProxy(int left, int top) {
        super(left, top);
        
        this.proxy = null;
      }
      
      /**
       * Ленивая инициализация ячейки с миной
       */
      @Override
      public void open() {
        if (proxy == null) {
          proxy = new Mine(left, top);
        }
        
        proxy.open();
      }

      @Override
      public int getLeft() {
        if (proxy == null) {
          return left;
        } else {
          return proxy.getLeft();
        }
      
      }
      
      @Override
      public int getTop() {
        if (proxy == null) {
          return top;
        } else {
          return proxy.getTop();
        }
      }

      @Override
      public int getStatus() {
        if (proxy == null) {
          return status;
        } else {
          return proxy.getStatus();
        }
      }

      @Override
      public int getPoints() {
        if (proxy == null) {
          return 100;
        } else {
          return proxy.getPoints();
        }
      }
    }

    /**
    * Использование
    */
    public class Main {
      public static void main(String[] args) {
        Cell cells[][] = new Cell[10][10];
        
        for (int i=0; i<10; i++) {
          for (int j=0; j<10; j++) {
            
            if (i+j % 2 == 0) {
              cells[i][j] = new MineProxy(i, j);
            } else {
              cells[i][j] = new EmptyProxy(i, j);
            }
          }
        }
        
        for (int i=0; i<10; i++) {
          for (int j=0; j<10; j++) {
            cells[i][j].open();
          }
        }
      }
    }

    * This source code was highlighted with Source Code Highlighter.

    Also popular now: