Design Patterns: How to write reusable and tidy software?

Referred from : Python Unlocked 2015

Hello everyone. In this blog post I am going to tell about a important concept in software development which I came through called “Design Patterns”.

In software engineering, problems requiring similar solutions are very common. Therefore people generally tend to come up with a repeatable design specification to deal with such common problems. Studying design patterns helps one to have a basic idea of existing solutions to such problems.

Few advantages of design patterns are :

  1. They speed up the development process by providing tested and robust paradigms for solving a problem.
  2. Improves code readability for programmers
  3. Documenting the code also becomes easy as a lot of solutions are based on common design pattern. Therefore, less efforts are required to document code.

Let’s come to different design patterns used by people in software engineering. They are mostly classified as follows :

  1. Observer pattern
  2. Strategy pattern
  3. Singleton pattern
  4. Template pattern
  5. Adaptor pattern
  6. Facade pattern
  7. Flyweight pattern
  8. Command pattern
  9. Abstract factory
  10. Registry pattern
  11. State pattern

Let’s have a brief overview of the above-mentioned design patterns :

  1. Observer Pattern :  The key to the observer pattern is “Spreading information to all listeners“. In other words, when we need to deal with a lot of listeners ( which always waiting for a particular event to be invoked ) we need to keep track of them and inform them about the occurence of an event ( For example, change of state of variable ). Below code snippet will help to make the situation more clear :
    class Notifier():
        """
        Provider of notifications to other objects
        """
        def __init__(self, name):
            self.name = name
            self._observers = Set()
    
        def register_observer(self, observer):
            """
            Function to attach other observers to this notifier
            """
            self._observers.add(observer)
            print("observer {0} now listening on {1}".format(observer.name, self.name))
        
        def notify_observers(self, msg):
            """
            transmit event to all interested observers
            """
            print("subject notifying observers about {}".format(msg,))
    
            for observer in self._observers:
                observer.notify(self, msg)
    
    class Observer():
        
        def __init__(self, name):
            self.name = name
    
        def start_observing(self, subject):
            """
            register for getting event for a subject
            """
            subject.register_observer(self)
    
        def notify(self, subject, msg):
            """
            notify all observers
            """
            print("{0} got msg from {1} that {2}".format(self.name, subject.name,msg))

    The above code snippet provides a very simple implementation of the Observer pattern. There is a notifier object which provides a method to register the listeners. And in the listeners ( the Observer object ) there is start_observing function to register with the notifier.

Advertisements

Menace of Global Variables

Hello everyone. In this week I had learnt a few things about writing beautiful and organized code. In the end of first week I started to write code. Initially my code base was very small, therefore I went on writing and writing more and more code and repeating same design patterns.

Therefore, for these problems what I learnt was code refactoring and design patterns. The first is one somewhat obvious that we need to mainatin code’s modularity and arrange related and similar pieces of code with each other in order to ensure easier debugging and extension of other features. Therefore, I refacored my code in a number of files namely :

  1. __init__.py
  2. __main__.py
  3. And other files like install_page.py, manage_installed_package.py, pip_extensions.py, utils.py and many others to be added depending on further features to be added.

In this process of code refactoring, I realised the menace of using global variables. One of the global variables was a Python dictionary, which was accessed by multiple functions. Therefore, when I grouped those functions in different files, my whole application failed. Then, I had to remove that global variable and make it a member variable of a class. In this case, correcting code was easy, but if I were to realise it a week or two later, then I think using global variables might would have wasted my whole day at that time.

Recently, I also went through a dozen design patterns in reaation to my GUI application. Therefore, my next post will be on Design Patterns.

Page Layouts and Application Flow of Logic

We have prepared some of the design layouts and application flow of logic of the ‘pip’ GUI. Some of the page layouts designed for the ‘pip’ GUI are :

1. Welcome Page

welcome_v1
Page Layout : Welcome Page

2. Configuration Page

select_venv_system
Page Layout : Configure Environment

3. Search and Install Page

install_pyPI_v1
Page Layout : ‘pip’ Search and Install

4. Install from requirements file

install_requirement_v1
Page Layout : Install From Requirement File

5. Install From Local Archive

install_local_archive_v1
Page Layout : Install From Local Archive

6. List, Update and Uninstall Package

update_package_v1
Page Layout : ‘pip’ List, Update and Uninstall

 

Now, workflow for application :

design_flow
Application Flow of Logic

Ist Design Iteration of ‘pip’ GUI

This is my first iteration of ‘pip’ GUI project. It is based on the design process explained in my last blog post. I am re-writing the steps needed for the design of an application :

  • Define the product
  • Define the target users
  • Define the user goals
  • Define the top user tasks : Focus on top six tasks
  • Define the user’s context : Know user’s context
  • Explain each top task to a friend
  • Map your natural explanation into a page flow
  • Design each page
  • Simplify and optimize the task flow and pages
  • Review the communication
  • Verify that purpose of each page and control is clear
  • Review the results

In this design iteration I have tried to follow those steps which are relevant to my application :

Product:

The product developed will be a GUI for pip to make various functionalities of pip ( a command line tool for installing Python packages) easily accessible to users. Main motivation behind the need for GUI version of Python Package Manager is :

  • Make various functionalities provided by PIP easily accessible to Windows/LINUX/Mac based users
  • Help people to focus only on fulfilling the task of installing Python packages rather than getting in unavoidable trouble of configuring various paths, versions and configurations

Target Users:

  1. Windows Users ( who have difficulty using command prompt )
  2. In case of LINUX or MacOS, people who have little or no experience with Python packages
  3. Users who maintain multiple versions of Python in their system
  4. Users who need to manage Python packages in virtualenv ( Last priority )

User Goals:

  1. Avoid command line to manage ( i.e. install/update/uninstall) Python packages
  2. Manage packages for different versions of Python installed in the system

User Tasks:

  1. Search, select version and install package ( including dependencies : first tell user about total download size including dependencies )
  2. Check for new updates and install them.
  3. Uninstall a package
  4. Support different installation methods :
    1. Requirements.txt files
    2. .whl files
    3. From local archives
  5. Manage packages in virtual environment

My next blog post provides a peek of GUI layout and application flow of logic.

My First GSoC Post : Notes on process of designing a GUI application

Notes on UI Principles

  • Don’t design like a programmer

    Link : http://www.uxdesignedge.com/2010/03/dont-design-like-a-programmer/

    This post explains the process of UI design in three parts :

    1. General mistakes made by programmers like using variable names as control labels, letting variable types determine control types, exposing raw and unformatted data, forcing user to enter data in specific format ( i.e. exposing representation invariants) and overusing messages.Three types of issues are pointed out :
      1. Simplicity Issues : Most common and probable cases should be optimized and easily accessible. User should be asked only what is extremely necessary to fulfill the task. It is better to ask for input values which users can provide easily and confidently.
      2. Life Cycle Issues : Initial UI state shouldn’t be blank as it will confuse the user about where to start from. All options and fields shouldn’t be disclosed at once. We should use progressive disclosure to hide infrequently used settings. Data structures shouldn’t be mapped directly to a UI element, as there may be case that some fields are a part of single object, but they don’t necessarily have to be provided by the user at the same time.
      3. Efficiency Issues : Frequent actions should be minimized. Reasonable values can be assumed for frequently used options or user’s history can be saved for frequently used options. Unexpected input should be handled properly.
    2. Second post explains the some key points related to solving above mentioned problems :
      1. Identify target users for improving user experience. Understand their goals and preferences. Identify use cases.
      2. UI should be self explanatory. There should not be any need for documentation.
      3. UI’s should be task centric, not feature centric.

      Basic design process :

      • Define the product
      • Define the target users
      • Define the user goals
      • Define the top user tasks : Focus on top six tasks
      • Define the user’s context : Know user’s context
      • Explain each top task to a friend
      • Map your natural explanation into a page flow
      • Design each page
      • Simplify and optimize the task flow and pages
      • Review the communication
      • Verify that purpose of each page and control is clear
      • Review the results
    3. Third post is a worked out example of above steps for a simple product. It may act as a reference.
  • User Interface Design for Programmers by Joel Spolsky

    This book contains one of the excellent ideas of UI design explained with very simple and practical examples.

    Most of the chapters after very good and contain illustrative discussions which conclude with axioms like :

    1. “ A user interface is well designed when the program model conforms to the user model. “
    2. “ Users will assume the simplest model possible. ”
    3. “ Every time you provide an option, you’re asking the user to make a decision.” : They will have to stop and think. In general number of decisions to be taken by user should be minimum.
    4. Use real metaphors for objects to relate them with reality.
    5. UI design should be consistent. UI designers shouldn’t use their creativity to make an UI which works in totally different way than other programs intended for same purpose.
    6. And many others …
  • Designing Interfaces by Jenifer Tidwell

    It explains UI design in technical depth and explains some concepts like affordances, visual hierarchy, navigational distance and use of color. In addition to it, use case of almost all UI elements like selector, pagination, cancellability, button groups, geometry managers etc. are thoroughly explained. Most importantly almost all chapters are followed by a Patterns section which includes all requirements to be achieved by a good software. I think this is one of the best resources to learn UI design in a technical manner. This book explains almost everything from organizing the content, navigation, organizing a page, using different UI elements to fulfill tasks, handling “verbs” of a interface ( i.e how to present actions and commands), discussing cognitive effects of data representation ( information graphics ) and finally tells how to use graphic design patterns to improve aesthetics and look & feel of UI.

  • A Collection of many UI design resources :

    https://whitneyhess.com/blog/2009/06/30/so-you-wanna-be-a-user-experience-designer-step-1-resources/

  • UX Design Concepts:

    http://www.usability.gov/what-and-why/user-experience.html

Automated testing using unittest

Ref. from : https://pymotw.com/2/unittest/
unittest is a testing framework for Python. It supports :
1. Test automation
2. Sharing of setup and shutdown code for tests
3. Aggregation of tests into collections
4. Independence of tests from the reporting framework

To implement the above mentioned functionalities, it supports some OOP based concepts:

  1. test fixture : It is something like initializing and loading all variables(everything needed) before running the real logic for testing.
  2. test case : Here we generally hard code responses for particular critical test cases.
  3. test suite : It’s a collection of various test cases and test suites.
  4. test runner : It decides the execution order during testing, interface for the user and format of output to be generated after the testing is completed.

My notes on extending Python with C/C++

  1. Without third party tools : Using C and C++
  2. With third party tools:
    1. Cython
    2. cffi
    3. SWIG
    4. Numba

Extending Python with C or C++:

Prerequisites :

  1. Programming in C
  2. Basic knowledge of Python

Functions performed by extension modules in C and C++:

  1. Implement new built-in object types
  2. Can call C library functions and system calls

All user visible entities are defined by “Python.h” have prefix Py or PY. “Python.h” imports stdio.h, string.h, errno.h and stdlib.h automatically.

static PyObject * spam_system( PyObject * self, PyObject * args )
{
    const char * command;
    int sts;

    if(!PyArg_ParseTuple(args, "s", &command))
       return NULL;

    sts = system(command)
    return PyLong_FromLong(sts);
}

Here PyArg_ParseTuple() <-- checks the argument types and converts them to C values.
It returns True if arguments are of right type (nonzero), otherwise return False(zero)
if arguments list is invalid.