Deep Dive into Structural Patterns - The Proxy Pattern.

Hey software designers! Today, we’re diving into the Proxy pattern. This pattern provides a surrogate or placeholder for another object to control access to it. Let’s explore its workings, benefits, and real-world applications with detailed examples.

What is the Proxy Pattern?

The Proxy pattern is a structural design pattern that provides a surrogate or placeholder for another object to control access to it. It acts as an intermediary that can add functionality before or after delegating the request to the real object.

Real-World Scenario

Imagine you’re developing an image viewer application that loads high-resolution images. Loading these images can be time-consuming, and you want to display a placeholder image while the high-resolution image is being loaded in the background.

The Problem

When dealing with resource-intensive objects, directly accessing these objects can lead to performance issues and unnecessary resource consumption. Hardcoding the logic to manage these objects can result in a tightly coupled and inefficient codebase.

Without Proxy Pattern

class HighResolutionImage
  def initialize(filename)
    @filename = filename
    load_image
  end

  def load_image
    puts "Loading high-resolution image: #{@filename}"
    # Simulate a time-consuming loading process
    sleep(2)
    puts "Image loaded: #{@filename}"
  end

  def display
    puts "Displaying image: #{@filename}"
  end
end

image = HighResolutionImage.new('large_image.jpg')
image.display

Drawbacks: The image is loaded immediately, leading to a delay before it can be displayed.

The Solution: Proxy Pattern

Using the Proxy pattern, we can control access to the high-resolution image and display a placeholder image while the real image is being loaded in the background.

With Proxy Pattern

Step 1: Define the Subject Interface

class Image
  def display
    raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'"
  end
end

Step 2: Create the Real Subject

class HighResolutionImage < Image
  def initialize(filename)
    @filename = filename
    load_image
  end

  def load_image
    puts "Loading high-resolution image: #{@filename}"
    # Simulate a time-consuming loading process
    sleep(2)
    puts "Image loaded: #{@filename}"
  end

  def display
    puts "Displaying image: #{@filename}"
  end
end

Step 3: Create the Proxy

class ImageProxy < Image
  def initialize(filename)
    @filename = filename
    @real_image = nil
  end

  def display
    if @real_image.nil?
      puts "Displaying placeholder image"
      @real_image = HighResolutionImage.new(@filename)
    end
    @real_image.display
  end
end

Step 4: Implement Client Code

image = ImageProxy.new('large_image.jpg')
image.display

Benefits: Controls access to the high-resolution image, displaying a placeholder image while the real image is being loaded.

Real-World Benefits

Scenario: Managing Access to Resource-Intensive Objects

Imagine you need to manage access to resource-intensive objects in a large-scale application. Using the Proxy pattern, you can control access to these objects and add functionality before or after delegating the request to the real object.

Without Proxy Pattern:

class DatabaseConnection
  def connect
    puts 'Establishing database connection...'
    # Simulate a time-consuming connection process
    sleep(2)
    puts 'Database connection established.'
  end

  def execute_query(query)
    puts "Executing query: #{query}"
  end
end

connection = DatabaseConnection.new
connection.connect
connection.execute_query('SELECT * FROM users')

Drawbacks: The connection is established immediately, leading to a delay before the query can be executed.

With Proxy Pattern:

class DatabaseConnectionProxy
  def initialize
    @real_connection = nil
  end

  def connect
    if @real_connection.nil?
      puts 'Displaying placeholder message'
      @real_connection = DatabaseConnection.new
      @real_connection.connect
    end
  end

  def execute_query(query)
    connect
    @real_connection.execute_query(query)
  end
end

connection = DatabaseConnectionProxy.new
connection.execute_query('SELECT * FROM users')

Benefits: Controls access to the database connection, displaying a placeholder message while the connection is being established.

Conclusion

The Proxy pattern is a powerful tool for controlling access to objects and adding functionality before or after delegating the request to the real object. It promotes efficiency, scalability, and maintainability in your code. By using the Proxy pattern, you can manage access to resource-intensive objects and improve the performance of your application. Incorporate the Proxy pattern into your design strategies to build more robust and adaptable software systems.

Stay tuned for more insights into software design principles and patterns.

Thôi Lo Code Đi Kẻo Sếp nạt!