Learning Python Design Patterns

By Gennadiy Zlobin
    Advance your knowledge in tech with a Packt subscription

  • Instant online access to over 7,500+ books and videos
  • Constantly updated with 100+ new titles each month
  • Breadth and depth in over 1,000+ technologies

About this book

Design pattern is a well-known approach to solve some specific problems which each software developer comes across during his work. Design patterns capture higher-level constructs that commonly appear in programs. If you know how to implement the design pattern in one language, typically you will be able to port and use it in another object-oriented programming language.

The choice of implementation language affects the use of design patterns. Naturally, some languages are more applicable for certain tasks than others. Each language has its own set of strengths and weaknesses. In this book, we introduce some of the better known design patterns in Python. You will learn when and how to use the design patterns, and implement a real-world example which you can run and examine by yourself.

You will start with one of the most popular software architecture patterns which is the Model- View-Controller pattern. Then you will move on to learn about two creational design patterns which are Singleton and Factory, and two structural patterns which are Facade and Proxy. Finally, the book also explains three behavioural patterns which are Command, Observer, and Template.

Publication date:
November 2013
Publisher
Packt
Pages
100
ISBN
9781783283378

 

Chapter 1. Model-View-Controller

Many applications start from something small, such as several hundred lines of code prototype of a toy application written in one evening. When you add new features and the application code clutters, it becomes much harder to understand how it works and to modify it, especially for a newcomer. The Model-View-Controller (MVC) pattern serves as the basis for software architecture that will be easily maintained and modified.

The main idea of MVC is about separating an application into three parts: model, view, and controller. There is an easy way to understand MVC—the model is the data and its business logic, the view is the window on the screen, and the controller is the glue between the two.

While the view and controller depend on the model, the model is independent of the presentation or the controller. This is a key feature of the division. It allows you to work with the model, and hence, the business logic of the application, regardless of the visual presentation.

The following diagram shows the flow of interaction between the user, controller, model, and view. Here, a user makes a request to the application and the controller does the initial processing. After that it manipulates the model, creating, updating, or deleting some data there. The model returns some result to the controller, that passes the result to view, which renders data to the user.

The MVC pattern gained wide popularity in web development. Many Python web frameworks, such as web2py, Pyramid, Django (uses a flavor of MVC called MVP), Giotto, and Kiss use it.

Let's review key components of the MVC pattern in more detail.

 

Model – the knowledge of the application


The model is a cornerstone of the application because, while the view and controller depend on the model, the model is independent of the presentation or the controller.

The model provides knowledge: data, and how to work with that data. The model has a state and methods for changing its state but does not contain information on how this knowledge can be visualized.

This independence makes working independently, covering the model with tests and substituting the controllers/views without changing the business logic of an application.

The model is responsible for maintaining the integrity of the program's data, because if that gets corrupted then it's game over for everyone.

The following are recommendations for working with models:

  • Strive to perform the following for models:

    • Create data models and interface of work with them

    • Validate data and report all errors to the controller

  • Avoid working directly with the user interface

 

View – the appearance of knowledge


View receives data from the model through the controller and is responsible for its visualization. It should not contain complex logic; all such logic should go to the models and controllers.

If you need to change the method of visualization, for example, if you need your web application to be rendered differently depending on whether the user is using a mobile phone or desktop browser, you can change the view accordingly. This can include HTML, XML, console views, and so on.

The recommendation for working with views are as follows:

  • Strive to perform the following for views:

    • Try to keep them simple; use only simple comparisons and loops

  • Avoid doing the following in views:

    • Accessing the database directly

    • Using any logic other than loops and conditional statements (if-then-else) because the separation of concerns requires all such complex logic to be performed in models

 

Controller – the glue between the model and view


The direct responsibility of the controllers is to receive data from the request and send it to other parts of the system. Only in this case, the controller is "thin" and is intended only as a bridge (glue layer) between the individual components of the system.

Let's look at the following recommendations for working with controllers:

  • Strive to perform the following in controllers:

    • Pass data from user requests to the model for processing, retrieving and saving the data

    • Pass data to views for rendering

    • Handle all request errors and errors from models

  • Avoid the following in controllers:

    • Render data

    • Work with the database and business logic directly

Thus, in one statement:

We need smart models, thin controllers, and dumb views.

 

Benefits of using the MVC


MVC brings a lot of positive attributes to your software, including the following:

  1. Decomposition allows you to logically split the application into three relatively independent parts with loose coupling and will decrease its complexity.

  2. Developers typically specialize in one area, for example, a developer might create a user interface or modify the business logic. Thus, it's possible to limit their area of responsibility to only some part of code.

  3. MVC makes it possible to change visualization, thus modifying the view without changes in the business logic.

  4. MVC makes it possible to change business logic, thus modifying the model without changes in visualization.

  5. MVC makes it possible to change the response to a user action (clicking on the button with the mouse, data entry) without changing the implementation of views; it is sufficient to use a different controller.

 

Implementation in Python


For a practical example, we'll create a very simple but working URL shortening service with a Flask micro framework that is developed by Pocoo.

Flask is a micro framework that is intended to create simple and short applications. It provides API for handling typical web-development tasks. On the other hand, it does not have object-relational mapping, form validations, and other features typical to bigger frameworks such as Django or Pyramid. Flask is very expansible with third-party libraries and modules. It does not use MVC out of the box, but let's us take advantage of its high customization and allows us to use the MVC pattern in Flask.

First, you should have Flask installed. Any one of the following commands should be sufficient to install Flask:

  • $ sudo pip install Flask

  • $ sudo easy_install Flask

Let's create a model that contains all our data operations and business logic.

We create the Url class that represents the URL entity. This class will have two properties: full_url and short_url. If the user accessed our website with a short URL, we will find the Url instance using short_url and redirect the user there.

The shorten method provides an interface method for the controller. The controller will call this method by passing the full URL. The model will generate a short URL and will save it for further retrieval.

The get_by_short_url method provides the second interface method for the controller. The controller will call this method by passing the short_url value, and the model will retrieve the Url instance with short_url and return it to the controller.

The other methods are the helpers to process the business logic, for example, to generate a short URL, as shown in the following code, in order or to save the Url instance and retrieve it from storage.

The code for models.py is as follows:

import pickle

class Url(object):
   @classmethod
  def shorten(cls, full_url):
    """Shortens full url."""

    # Create an instance of Url class
    instance = cls()
    instance.full_url = full_url
    instance.short_url = instance.__create_short_url()
    Url.__save_url_mapping(instance)
    return instance

  @classmethod
  def get_by_short_url(cls, short_url):
    """Returns Url instance, corresponding to short_url."""
    url_mapping = Url.load_url_mapping()
    return url_mapping.get(short_url)

  def __create_short_url(self):
    """Creates short url, saves it and returns it."""
    last_short_url = Url.__load_last_short_url()
    short_url = self.__increment_string(last_short_url)
    Url.__save_last_short_url(short_url)
    return short_url

  def __increment_string(self, string):
    """Increments string, that is:
      a -> b
      z -> aa
      az -> ba
      empty string -> a

    """
    if string == '':
      return 'a'

    last_char = string[-1]

    if last_char != 'z':
      return string[:-1] + chr(ord(last_char) + 1)

    return self.__increment_string(string[:-1]) + 'a'

  @staticmethod
  def __load_last_short_url():
    """Returns last generated short url."""
    try:
      return pickle.load(open("last_short.p", "rb"))
    except IOError:
      return ''

  @staticmethod
  def __save_last_short_url(url):
    """Saves last generated short url."""
    pickle.dump(url, open("last_short.p", "wb"))

  @staticmethod
  def __load_url_mapping():
    """Returns short_url to Url instance mapping."""
    try:
      return pickle.load(open("short_to_url.p", "rb"))
    except IOError:
      return {}

  @staticmethod
  def __save_url_mapping(instance):
    """Saves short_url to Url instance mapping."""
    short_to_url = Url.__load_url_mapping()
    short_to_url[instance.short_url] = instance
    pickle.dump(short_to_url, open("short_to_url.p", "wb"))

Let's create our view. The view is responsible for rendering data from the model to the end users, and here we have several options. The first option is to create another class where every method is responsible to perform simple logic and call templates to render.

The second option is to use the templates directly. Flask uses the Jinja2 template engine that provides use of template tags. For example, to perform comparisons, we can use the following:

{% if var == True %}
...//some code
{% endif %}

Jinja2 also allows us to use passed variables from the controller, iterate loops, and inherit one template from another. So let's use this smart template engine as views, and write a couple of views to render to the user.

Create a views directory and the main_page.html and success.html files should be created in views directory.

The main_page.html file is the main page of the application that has a form with an input field to enter the full URL of website and the submit button, when clicked, sends full URL to controller.

The code for main_page.html is as follows:

<form action="/shorten/">
  <label>
  <input type="text" name="url" value="" />
    Link to shorten
  </label>
  <input type="submit" value="OK"/>
</form>

The success.html page is a view for rendering a success message with a short version of the URL that the user asked to shorten.

The code for the success.html page looks like this:

Congratulations! <br />
Your url: {{ short_url }}

The controller will need to process three types of requests:

  • Render the main page

  • Process the request to shorten the URL

  • Process the request to convert the URL from short to full and then redirect it

In the following code, the process function renders the main page. Please note how it works: it takes the full URL from the request arguments, passes them to the model, and then passes the returned data to the view.

The redirect_to_full_url method takes the short URL from the requests, gets the full URL from the model, makes very simple validations, and redirects the user to the full URL.

The code for controller.py is as follows:

# Redirect function is used to forward user to full url if he came 
# from shortened
# Request is used to encapsulate HTTP request. It will contain request 
# methods, request arguments and other related information
# from flask import redirect, render_template, request, Flask
# from werkzeug.exceptions import BadRequest, NotFound

import models

# Initialize Flask application
app = Flask(__name__, template_folder='views')


@app.route("/")
def index():
  """Renders main page."""
  return render_template('main_page.html')

@app.route("/shorten/")
def shorten():
  """Returns short_url of requested full_url."""
  # Validate user input
  full_url = request.args.get('url')
  if not full_url:
    raise BadRequest()

  # Model returns object with short_url property
  url_model = models.Url.shorten(full_url)
  url_model.short_url

  # Pass data to view and call its render method
  short_url = request.host + '/' + url_model.short_url
  return render_template('success.html', short_url=short_url)
@app.route('/<path:path>')
def redirect_to_full(path=''):
  """Gets short url and redirects user to corresponding full url if found."""
  # Model returns object with full_url property
  url_model = models.Url.get_by_short_url(path)

  # Validate model return
  if not url_model:
    raise NotFound()

  return redirect(url_model.full_url)

if __name__ == "__main__":
  app.run(debug=True)

To run the application, place these files in one directory, open the terminal, run the following command, and go to http://127.0.0.1:5000 in your browser:

$ python controller.py

You will get a view similar to the following screenshot:

Now fill the form with a URL, for example, http://www.packtpub.com, click on OK, and see its shortened version.

If you copy its shortened version and paste it to your browser, you should be redirected to www.packtub.com.

 

Summary


It is important to separate the areas of responsibility to maintain loose coupling and for the maintainability of the software. MVC divides the application into three relatively independent parts: model, view, and controller. The model is all about knowledge, data, and business logic. The view is about presentation to the end users, and it's important to keep it simple. The controller is the glue between the model and the view, and it's important to keep it thin. In the practical example, you created a simple but fully-functional URL shortening service with the MVC pattern.

In this chapter, you used the pickle module for conserving the application data. But what if you were to use the database to do it? You would need to connect to the database. Connecting to the database is a heavy operation, so it is better to connect to it once and then just use this connection during the working of the application. In the next chapter, you will learn about the Singleton pattern that allows you to create only one object even if the instantiation has been done several times.

About the Author

  • Gennadiy Zlobin

    Gennadiy Zlobin works as Lead Software Engineer and Technical Leader in the Russian music service, Zvooq.ru. His current employer is Zvooq Ltd. He has been using Python as the primary language for more than 4 years, enjoying its elegance and power every day. His professional interests include high-load software architectures, good engineering practices, Android OS, and natural language processing. Previously, he worked for the company that had the first search engine in Russia, called Rambler. He was engaged in airline tickets' meta search service and Rambler's index page.

    Browse publications by this author
Learning Python Design Patterns
Unlock this book and the full library for FREE
Start free trial