Module pattern

Last updated

In software engineering, the module pattern is a design pattern used to implement the concept of software modules, defined by modular programming, in a programming language with incomplete direct support for the concept.

Contents

This pattern can be implemented in several ways depending on the host programming language, such as the singleton design pattern, object-oriented static members in a class and procedural global functions. In Python, the pattern is built into the language, and each .py file is automatically a module. The same applies to Ada, where the package can be considered a module (similar to a static class).

Definition & Structure

The module software design pattern provides the features and syntactic structure defined by the modular programming paradigm to programming languages that have incomplete support for the concept.

The object module pattern expressed in UML. Module-software-design-pattern.png
The object module pattern expressed in UML.

Concept

In software development, source code can be organized into components that accomplish a particular function or contain everything necessary to accomplish a particular task. Modular programming is one of those approaches.

The concept of a "module" is not fully supported in many common programming languages.

Features

In order to consider that a Singleton or any group of related code implements this pattern, the following features must be supplied:

Implementations

The semantics and syntax of each programming language may affect the implementation of this pattern.

Object-oriented programming languages

Java

Although Java supports the notion of a namespace, a reduced version of a module, some scenarios benefit from employing the design pattern instead of using namespaces.

The following example uses the singleton pattern.

Definition
packageorg.wikipedia.consoles;importjava.io.InputStream;importjava.io.PrintStream;publicfinalclassMainModule{privatestaticMainModulesingleton=null;publicInputStreaminput=null;publicPrintStreamoutput=null;publicPrintStreamerror=null;privateMainModule(){// does nothing on purpose !!!}// ...publicstaticMainModulegetSingleton(){if(MainModule.singleton==null){MainModule.singleton=newMainModule();}returnMainModule.singleton;}// ...publicvoidprepare(){//System.out.println("consoles::prepare();");this.input=newInputStream();this.output=newPrintStream();this.error=newPrintStream();}publicvoidunprepare(){this.output=null;this.input=null;this.error=null;// System.out.println("consoles::unprepare();");}// ...publicvoidprintNewLine(){System.out.println();}publicvoidprintString(Stringvalue){System.out.print(value);}publicvoidprintInteger(intvalue){System.out.print(value);}publicvoidprintBoolean(booleanvalue){System.out.print(value);}publicvoidscanNewLine(){// to-do: ...}publicvoidscanString(Stringvalue){// to-do: ...}publicvoidscanInteger(intvalue){// to-do: ...}publicvoidscanBoolean(booleanvalue){// to-do: ...}// ...}
Implementation
importorg.wikipedia.consoles.*;classConsoleDemo{publicstaticMainModuleconsole=null;publicstaticvoidprepare(){console=MainModule.getSingleton();console.prepare();}publicstaticvoidunprepare(){console.unprepare();}publicstaticvoidexecute(String[]args){console.printString("Hello World");console.printNewLine();console.scanNewLine();}publicstaticvoidmain(String[]args){prepare();execute(args);unprepare();}}

C# (C Sharp .NET)

C#, like Java, supports namespaces although the pattern remains useful in specific cases.

The following example uses the singleton pattern.

Definition
usingSystem;usingSystem.IO;usingSystem.Text;namespaceConsoles;publicsealedclassMainModule{privatestaticMainModuleSingleton=null;publicInputStreaminput=null;publicOutputStreamoutput=null;publicErrorStreamerror=null;// ...publicMainModule(){// does nothing on purpose !!!}// ...publicstaticMainModuleGetSingleton(){if(MainModule.Singleton==null){MainModule.Singleton=newMainModule();}returnMainModule.Singleton;}// ...publicvoidPrepare(){//System.WriteLine("console::prepare();");this.input=newInputStream();this.output=newOutputStream();this.error=newErrorStream();}publicvoidUnprepare(){this.output=null;this.input=null;this.error=null;// Console.WriteLine("console::unprepare();");}// ...publicvoidPrintNewLine(){Console.WriteLine("");}publicvoidPrintString(stringValue){Console.Write(Value);}publicvoidPrintInteger(intValue){Console.Write(Value);}publicvoidPrintBoolean(boolValue){Console.Write(Value);}publicvoidScanNewLine(){// to-do: ...}publicvoidScanString(stringValue){// to-do: ...}publicvoidScanInteger(intValue){// to-do: ...}publicvoidScanBoolean(boolValue){// to-do: ...}// ...}
Implementation
classConsoleDemo{publicstaticConsoles.MainModuleConsole=null;publicstaticvoidPrepare(){Console=Consoles.MainModule.GetSingleton();Console.Prepare();}publicstaticvoidUnprepare(){Console.Unprepare();}publicstaticvoidExecute(){Console.PrintString("Hello World");Console.PrintNewLine();Console.ScanNewLine();}publicstaticvoidMain(string[]args){Prepare();Execute(args);Unprepare();}}

Prototype-based programming languages

JavaScript

JavaScript is commonly used to automate web pages.

Definition
functionConsoleClass(){varInput=null;varOutput=null;varError=null;// ...this.prepare=function(){this.Input=newInputStream();this.Output=newOutputStream();this.Error=newErrorStream();}this.unprepare=function(){this.Input=null;this.Output=null;this.Error=null;}// ...varprintNewLine=function(){// code that prints a new line}varprintString=function(params){// code that prints parameters}varprintInteger=function(params){// code that prints parameters}varprintBoolean=function(params){// code that prints parameters}varScanNewLine=function(){// code that looks for a newline}varScanString=function(params){// code that inputs data into parameters}varScanInteger=function(params){// code that inputs data into parameters}varScanBoolean=function(params){// code that inputs data into parameters}// ...}
Implementation
functionConsoleDemo(){varConsole=null;varprepare=function(){Console=newConsoleClass();Console.prepare();}varunprepare=function(){Console.unprepare();}varrun=function(){Console.printString("Hello World");Console.printNewLine();}varmain=function(){this.prepare();this.run();this.unprepare();}}

Procedural programming languages

This pattern may be seen as a procedural extension to object-oriented languages.

Although the procedural and modular programming paradigms are often used together, there are cases where a procedural programming language may not fully support modules, hence requiring a design pattern implementation.

PHP (procedural)

This example applies to procedural PHP before namespaces (introduced in version 5.3.0). It is recommended that each member of a module is given a prefix related to the filename or module name in order to avoid identifier collisions.

Definition
<?php// filename: console.phpfunctionconsole_prepare(){// code that prepares a "console"}functionconsole_unprepare(){// code that unprepares a "console"}// ...functionconsole_printNewLine(){// code that outputs a new line}functionconsole_printString(/* String */Value){// code that prints parameters}functionconsole_printInteger(/* Integer */Value){// code that prints parameters}functionconsole_printBoolean(/* Boolean */Value){// code that prints parameters}functionconsole_scanNewLine(){// code that looks for a new line}functionconsole_scanString(/* String */Value){// code that stores data into parameters}functionconsole_scanInteger(/* Integer */Value){// code that stores data into parameters}functionconsole_scanBoolean(/* Boolean */Value){// code that stores data into parameters}
Implementation
// filename: consoledemo.phprequire_once("console.php");functionconsoledemo_prepare(){console_prepare();}functionconsoledemo_unprepare(){console_unprepare();}functionconsoledemo_execute(){console_printString("Hello World");console_printNewLine();console_scanNewLine();}functionconsoledemo_main(){consoledemo_prepare();consoledemo_execute();consoledemo_unprepare();}

C

Note that this example applies to procedural C without namespaces. It is recommended that each member of a module is given a prefix related to the filename or module name in order to avoid identifier collisions.

Definition header module
// filename: "consoles.h"#pragma oncevoidconsolesPrepare();voidconsolesUnprepare();// ...voidconsolesPrintNewLine();voidconsolesPrintString(char*value);voidconsolesPrintInteger(intvalue);voidconsolesPrintBoolean(boolvalue);voidconsolesScanNewLine();voidconsolesScanString(char*value);voidconsolesScanInteger(int*value);voidconsolesScanBoolean(bool*value);
Definition body module
// filename: "consoles.c"#include<ctype.h>#include<stdio.h>#include<string.h>#include"consoles.h"voidconsolesPrepare(){// code that prepares console}voidconsolesUnprepare(){// code that unprepares console}// ...voidconsolesPrintNewLine(){printf("\n");}voidconsolesPrintString(char*value){printf("%s",value);}voidconsolesPrintInteger(intvalue){printf("%d",&value);}voidconsolesPrintBoolean(boolvalue){printf((value)?("true"):("false"));}voidconsolesScanNewLine(){getch();}voidconsolesScanString(char*value){scanf("%s",value);}voidconsolesScanInteger(int*value){scanf("%d",Value);}voidconsolesScanBoolean(bool*value){chartemp[512];scanf("%s",temp);*value=(strcmp(temp,"true")==0);}
Implementation
// filename: "consoledemo.c"#include"consoles.h"voidconsoledemoPrepare(){consolesPrepare();}voidconsoledemoUnprepare(){consolesUnprepare();}intconsoledemoExecute(){consolesPrintString("Hello World");consolesPrintNewLine();consolesScanNewLine();return0;}intmain(){intresult=0;consoledemoPrepare();result=consoledemoExecute();consoledemoUnprepare();returnresult;}

Procedural Pascal

Note that this example applies to procedural non-modular Pascal. Many Pascal dialects have namespace support, called "unit (s)". Some dialects also support initialization and finalization.

If namespaces are not supported, it is recommended to give all member names a prefix related to the filename or module name in order to prevent identifier collisions.

Definition
unitconsoles;(* filename: "consoles.pas" *)usescrt;procedureprepare();begin(* code that prepares console *)end;procedureunprepare();begin(* code that unprepares console *)end;// ...procedureprintNewLine();beginWriteLn();end;procedureprintString(Value:string);beginWrite(Value);end;procedureprintInteger(Value:integer);beginWrite(Value);end;procedureprintBoolean(Value:boolean);beginif(Value)thenbeginWrite('true');endelsebeginWrite('false');end;end;procedurescanNewLine();beginSeekEoLn();end;procedurescanString(Value:string);beginReadLn(Value);end;procedurescanInteger(Value:Integer);beginReadLn(Value);end;procedurescanBoolean(Value:Boolean);vartemp:string;beginReadLn(temp);if(Temp='true')thenbeginValue:=true;endelsebeginValue:=false;end;end;
Implementation
programconsoledemo;// filename: "consoles.pas"usesconsoles;procedureprepare();beginconsoles.prepare();end;procedureunprepare();beginconsoles.unprepare();end;functionexecute():Integer;beginconsoles.printString('Hello World');consoles.printNewLine();consoles.scanNewLine();execute:=0;end;beginprepare();execute();unprepare();end.

Comparisons to other concepts

Namespaces

Both namespaces and modules allow to group several related entities by a single identifier, and in some situations, used interchangeably. Those entities can be globally accessed. The main purpose of both concepts is the same.

In some scenarios a namespace requires that the global elements that compose it are initialized and finalized by a function or method call.

In many programming languages, namespaces are not directly intended to support an initialization process nor a finalization process, and are therefore not equivalent to modules. That limitation can be worked around in two ways. In namespaces that support global functions, a function for initialization and a function for finalization are coded directly, and called directly in the main program code.

Classes and namespaces

Classes are used sometimes used as or with namespaces. In programming languages that don't support namespaces (e.g., JavaScript) but do support classes and objects, classes are often used to substitute for namespaces. These classes are usually not instantiated and consist exclusively of static members.

Singleton classes and namespaces

In object-oriented programming languages where namespaces are incompletely supported, the singleton pattern may be used instead of static members within a non-instantiable class.

Relationship with other design patterns

The module pattern can be implemented using a specialization of the singleton pattern. However, other design patterns may be applied and combined, in the same class.

This pattern can be used as a decorator , a flyweight , or an adapter .

Module as a design pattern

The Module pattern can be considered a creational pattern and a structural pattern. It manages the creation and organization of other elements, and groups them as the structural pattern does.

An object that applies this pattern can provide the equivalent of a namespace, providing the initialization and finalization process of a static class or a class with static members with cleaner, more concise syntax and semantics.

It supports specific cases where a class or object can be considered structured, procedural data. And, vice versa, migrate structured, procedural data, and considered as object-oriented.

See also