Flutter Design Patterns: 15 — Proxy

An overview of the Proxy design pattern and its implementation in Dart and Flutter

Image for post
Image for post

In the last article, I have analysed a relatively simple design pattern comparing to the other patterns in the series — Prototype. In this article, I would like to analyse and implement a structural design pattern which is very practical and could be used in a lot of cases when developing applications with Dart and Flutter — it is Proxy.

Table of Contents

  • What is the Proxy design pattern?
  • Analysis
  • Implementation
  • Other articles in this series
  • Your contribution

What is the Proxy design pattern?

Image for post
Image for post
If the proxy service were a person… (source)

Proxy, also known as Surrogate, belongs to the category of structural design patterns. The intention of this design pattern is described in the GoF book:

Provide a surrogate or placeholder for another object to control access to it.

The key idea in this pattern is to work through a separate proxy object that performs additional functionality when accessing an (already existing) object. For instance, the user’s rights should be validated before accessing the object or the object’s creation is very expensive so it makes sense to defer its creation until the object is actually needed. Also, if you need to execute something either before or after the primary logic of the class, the proxy lets you do this without changing that class. Since the proxy implements the same interface as the original class, it can be passed to any client that expects a real service object.

To understand the Proxy design pattern better, let’s dive in by analysing its structure, types and implementation in more detail!

Analysis

The general structure of the Proxy design pattern looks like this:

Image for post
Image for post
Structure of the Proxy design pattern (source)
  • ServiceInterface — defines the common interface for Service and Proxy that the proxy service could be used instead of the real one;
  • Service — defines the real object which contains some useful business logic. This is the service that the proxy represents;
  • Proxy — implements the same interface as the real service. Also, has a reference field that points to a service object which allows controlling the access to it. The Proxy class may be responsible for creating and deleting the real service’s object;
  • Client — should work with both services and proxies via the same interface. This way you can pass a proxy into any code that expects a service object.

Applicability

There are dozens of ways to utilize the Proxy pattern:

  • Lazy initialization (virtual proxy) — instead of creating the object when the app launches, you can delay the object’s initialization to a time when it’s really needed;
  • Access control (protection proxy) — the proxy can pass the request to the service object only if the client’s credentials match some criteria;
  • Local execution of a remote service (remote proxy) — the proxy passes the client request over the network, handling all of the nasty details of working with the network;
  • Logging requests (logging proxy) — the proxy can log each request before passing it to the service;
  • Caching request results (caching proxy) — the proxy can implement caching for recurring requests that always yield the same results. The proxy may use the parameters of requests as the cache keys.

Implementation

Image for post
Image for post
I’m ready — let’s implement it! (source)

For the following example of the Proxy design pattern, we will implement the caching proxy.

In my opinion, in any application which loads resources from an external service, it is quite a common problem to improve the performance and reduce load times of the data. It is possible to optimise and at least partially resolve it by implementing the caching layer.

Let’s say we have a list of customers with some basic information — customer’s id and name. Any additional customer data should be loaded from an external web service. When providing the general list of customers, additional information is not loaded nor used. However, it could be accessed by selecting a specific customer and loading its data from the customer details service. To reduce the number of requests sent to the external service, it makes sense to introduce a caching layer to the application and provide the already loaded information from the cache for future requests.

To achieve this, the Proxy design pattern is a great choice! Let’s check the class diagram first and then implement a proxy for the customer details service.

Class diagram

The class diagram below shows the implementation of the Proxy design pattern:

Image for post
Image for post
Class Diagram — Implementation of the Proxy design pattern

Customer class is used to store information about the customer. One of its properties is the CustomerDetails which stores additional data about the customer e.g. its email, hobby and position.

ICustomerDetailsService is an abstract class which is used as an interface for the customer details service:

  • getCustomerDetails() — an abstract method which returns details for the specific customer.

CustomerDetailsService is the “real” customer details service which implements the abstract class ICustomerDetailsService and its methods.

CustomerDetailsServiceProxy is a proxy service which contains the cache (dictionary object) and sends the request to the real CustomerDetailsService only if the customer details object is not available in the cache.

ProxyExample initialises and contains the proxy object of the real customer details service. When a user selects the option to see more details about the customer, the dialog window appears and loads details about the customer. If the details object is already stored inside the cache, the proxy service returns that object instantly. Otherwise, a request is sent to the real customer details service and the details object is returned from there.

Customer

A simple class to store information about the customer: its id, name and details. Also, the constructor generates random id and name values when initialising the Customer object.

Image for post
Image for post
customer.dart

CustomerDetails

A simple class to store information about customer details: id to map the details information with the corresponding customer, e-mail address, hobby and the current position (job title).

Image for post
Image for post
customer_details.dart

ICustomerDetailsService

An interface which defines the getCustomerDetails() method to be implemented by the customer details service and its proxy. Dart language does not support the interface as a class type, so we define an interface by creating an abstract class and providing a method header (name, return type, parameters) without the default implementation.

Image for post
Image for post
icustomer_details_service.dart

CustomerDetailsService

A specific implementation of the ICustomerDetailsService interface — the real customer details service. The getCustomerDetails() method mocks the real behaviour of the service and generates random values of customer details.

Image for post
Image for post
customer_details_service.dart

CustomerDetailsServiceProxy

A specific implementation of the ICustomerDetailsService interface — a proxy for the real customer details service. Before making a call to the customer details service, the proxy service checks whether the customer details are already fetched and saved in the cache. If yes, the customer details object is returned from the cache, otherwise, a request is sent to the real customer service and its value is saved to the cache and returned.

Image for post
Image for post
customer_details_service_proxy.dart

Example

First of all, a markdown file is prepared and provided as a pattern’s description:

Image for post
Image for post

ProxyExample contains the proxy object of the real customer details service. When the user wants to see customer details, the showDialog() method is triggered (via the showCustomerDetails() method) which opens the dialog window of type CustomerDetailsDialog and passes the proxy object via its constructor as well as the selected customer’s information — the Customer object.

Image for post
Image for post
proxy_example.dart

The CustomerDetailsDialog class uses the passed proxy service on its state’s initialisation, hence loading details of the selected customer.

Image for post
Image for post
customer_details_dialog.dart

The CustomerDetailsDialog class does not care about the specific type of customer details service as long as it implements the ICustomerDetailsService interface. As a result, an additional caching layer could be used by sending the request through the proxy service, hence improving the general performance of the application, possibly saving some additional network data and reducing the number of requests sent to the real customer details service as well. Also, if you want to call the real customer details service directly, you can just simply pass it via the CustomerDetailsDialog constructor — no additional changes are needed in the UI code since both the real service and its proxy implements the same interface.

Image for post
Image for post

As you can see in the example, when trying to load the specific customer’s details for the first time, it takes some time for the information to load from the service. However, when the same information is accessed once again, it is provided from the cache stored in the proxy service, hence the request is not sent to the real customer details service — the customer details information is provided instantly.

All of the code changes for the Proxy design pattern and its example implementation could be found here.

Your contribution

👏 Press the clap button below to show your support and motivate me to write better!
💬 Leave a response to this article by providing your insights, comments or wishes for the series.
📢 Share this article with your friends, colleagues in social media.
➕ Follow me on Medium.
⭐ Star the Github repository.

Flutter Community

Articles and Stories from the Flutter Community

Medium is an open platform where 170 million readers come to find insightful and dynamic thinking. Here, expert and undiscovered voices alike dive into the heart of any topic and bring new ideas to the surface. Learn more

Follow the writers, publications, and topics that matter to you, and you’ll see them on your homepage and in your inbox. Explore

If you have a story to tell, knowledge to share, or a perspective to offer — welcome home. It’s easy and free to post your thinking on any topic. Write on Medium

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store