Structuring Code

Dr Andy Evans

[Fullscreen]

Structure

  • We saw early on the importance of algorithms, but what about structuring our code?
  • What's the best way to structure a larger project made of several files?

Structure we've seen

  • Clauses
  • Blocks (Functions; Classes)
  • Exception handling
  • Modules
  • Packages
  • How do we judge where code should go?

Coupling and Cohesion

  • Good OO design calls for LOOSE coupling and HIGH cohesion.
  • Coupling is the degree to which two classes 'know about each other'.
    • Just uses unobfuscated methods: low coupling.
    • Uses internal code, e.g. internal variables: high coupling.
  • Cohesion is the purpose of the class. Is it well focused?
  • We can talk about accessing a class through its methods that haven't been mangled: its "interface".

Coupling

  • Close coupling causes issues for code maintenance.
    • Enhancement or bug fixes to a class should be possible without impacting the interface of methods it presents.
    • Assumption is that changes will not impact anywhere else, why should it if the interface has not changed.
    • A close coupled class means if part of the code changed, the alteration has an undetected impact.
  • Close coupling limits code reuse because you can't just separate off one class.

Cohesion

  • Classes and packages with high cohesion have a well defined purpose.
  • They are:
    • Easier to maintain.
    • Less likely to require frequent changes.
    • Easier to reuse in multiple projects / applications.
    • Tend to be simpler to follow for other developers.
    • Generally have a well defined interface.

High Cohesion Loose Coupling

  • Remember when designing a class or package:
    • Define the purpose and don't let that creep.
    • Keep the inner variables of the class protected.
    • Think about how you want other classes to interact with this class and define its interface accordingly.
    • Ask yourself, how easy would this class or package be to reuse in another project?

Main script

  • Ideally this just wants to be class method calls, or, at worse, function calls. These should describe the story of the code, for example:
    io = IO()
    processor = Processor()

    data = io.read_data("data.txt")
    new_data = processor.process(data)
    io.write_data(new_data, "new_data.txt")
  • This is "Self-documenting code"; it needs no comments to tell the story.

Structure we've seen

  • Clauses
  • Blocks (Functions; Classes)
  • Exception handling
  • Modules
  • Packages
  • GUIs
  • Others we haven't: threads/concurrency, parallelisation, other programs.

Getting processor time

  • Usually a program will take over the processor once and run through the code a line at a time in one go.
  • However, you can force the processor to split up and run several copies of the same bit of code.
  • Each runs in its own processor "thread" of execution.
  • For example, you might want a web server to have one thread for each person accessing files so you can send the right file back to the right person, even though lots of requests are being made simultaneously.

Threads

Efficiency

  • In many languages, threading a program will allow chunks of it to run on separate processor units in the computer.
  • However, the standard CPython implementation allows only one thread (the thread holding the Global Interpreter Lock or GIL) at a time to access objects.
  • This means multi-threaded programs aren't generally more efficient, unless they are slowed by elements outside the GIL system, such as I/O or processing done in some libraries like numpy.
  • For details, see:
    https://wiki.python.org/moin/GlobalInterpreterLock

Parallel Supercomputing

  • Split up a program so it runs on multiple computers ("parallelize" or "parallel program" it).
  • For example, each computer does the calculation for a different geographical area.
  • A good starting point are the books on the reading list.

Starting other programs

  • To run other programs and read and write from and to them, use the subprocess library:
    https://docs.python.org/3/library/subprocess.html
    import subprocess

    program = subprocess.Popen("program", stdin=subprocess.PIPE, universal_newlines=True)
    program.stdin.write("Hello World") # Not on Windows unless program reads stdin.

Structure we've seen

  • Clauses
  • Blocks (Functions; Classes)
  • Exception handling
  • Modules
  • Packages
  • Threads / Parallelisation
  • Other programs
  • GUIs
  • Where do we get advice?

Design Patterns

  • "Design Patterns" or, more usually just "Patterns", are standard ways of solving a software problem that people come across again and again. They aren't specific to Python, or any other language.
  • They save us having to reinvent solutions.
  • The most influential book in the field was Design Patterns - Elements of Reusable Software by the "Gang of Four" Gamma, Helm, Johnson and Vlissides (1995).

The Gang of Four Patterns

  • They gave 23 patterns with C examples.
  • Creational Patterns - classes to make stuff. The Factory, Abstract Factory, Singleton, Builder, and Prototype.
  • Structural Patterns - software architecture. The Adapter, Bridge, Composite, Decorator, Facade, Flyweight and Proxy.
  • Behavioural Patterns - which classes do what. The Chain of Responsibility, Command, Interpreter, Iterator, Mediator, Memento, Observer, State, Strategy, Template, and Visitor.

Patterns we've seen

  • Iterator
  • Decorators (wrappers, not Python decorators)
  • Factory (class that quietly produces the right subclass of something)

Why Use Patterns?

  • Industry standard methods of doing something, therefore other programmers will understand your code.
  • They encapsulate solutions so you can get on with more tricky things.
  • They'll give you a deeper understanding of why Python is the way it is.
  • They're the advice of top experts with years of experience - they'll teach you a lot about good Object Orientated software design.

Book

  • "Head First Design Patterns": This book will certainly help you think like an OOProgrammer. However, the examples are written in Java (shouldn't be too bad to convert).
  • A good starting point for Python is Andrei Boyanov's post "Python Design Patterns: For Sleek And Fashionable Code" which gives a realistic overview of which patterns are needed in Python: https://www.toptal.com/python/python-design-patterns

Summary

  • Good Object Orientated code structure encourages robust and reusable software.
  • Read up on patterns: they'll teach you about good program style, and help solve major issues.

Unit Testing

  • A way to ensure that methods have the desired 'behaviour'
    • Decide on behaviour
    • Write tests
    • Write code to satisfy tests
  • Time consuming
  • Produces robust code
  • Future maintenance is easier

Other tests

  • Stress testing
  • Alpha/Beta releases
  • Usability
  • Regression testing
  • Software testing

Summary

  • Good code structure centres on building to interfaces: are the inputs and outputs to methods and classes reliable, and do they respond well if things go wrong.
  • Get this right, and you'll minimise bugs.
  • Unit testing formalises this.
  • However, also test usability and go through alpha and beta test releases.