Patrón de módulo - Module pattern

En ingeniería de software , el patrón de módulo es un patrón de diseño utilizado para implementar el concepto de módulos de software , definido por programación modular , en un lenguaje de programación con soporte directo incompleto para el concepto.

Este patrón se puede implementar de varias formas dependiendo del lenguaje de programación del host, como el patrón de diseño singleton , miembros estáticos orientados a objetos en una clase y funciones globales de procedimiento. En Python, el patrón está integrado en el lenguaje y cada archivo .py es automáticamente un módulo.

Definición y estructura

El patrón de diseño del software del módulo proporciona las características y la estructura sintáctica definidas por el paradigma de programación modular a los lenguajes de programación que tienen un soporte incompleto para el concepto.

El patrón de módulo de objeto expresado en UML.
El patrón de módulo de objeto expresado en UML .

Concepto

En el desarrollo de software, el código fuente se puede organizar en componentes que cumplen una función en particular o contienen todo lo necesario para realizar una tarea en particular. La programación modular es uno de esos enfoques.

El concepto de "módulo" no es totalmente compatible con muchos lenguajes de programación comunes.

Características

Para considerar que un Singleton o cualquier grupo de código relacionado implementa este patrón, se deben proporcionar las siguientes características:

  • Una parte del código debe tener acceso público o global y estar diseñada para usarse como código público o global. El código público principal puede ejecutar código privado o protegido adicional.
  • Un módulo debe tener una función inicializadora que sea equivalente o complementaria a un método constructor de objetos . Esta función no es compatible con los espacios de nombres habituales .
  • Un módulo debe tener una función de finalizador que sea equivalente o complementaria a un método destructor de objetos. Esta función no es compatible con los espacios de nombres habituales.
  • Los miembros de apoyo pueden requerir un código de inicialización / finalización que se ejecuta mediante la función de inicialización / finalización del módulo.
  • La mayoría de los miembros son funciones que realizan operaciones en elementos externos a la clase, proporcionados como argumentos mediante funciones de llamada. Estas funciones son "utilidades", "herramientas" o "bibliotecas".

Implementaciones

La semántica y la sintaxis de cada lenguaje de programación pueden afectar la implementación de este patrón.

Lenguajes de programación orientados a objetos

Java

Aunque Java admite la noción de un espacio de nombres , una versión reducida de un módulo, algunos escenarios se benefician de emplear el patrón de diseño en lugar de usar espacios de nombres.

El siguiente ejemplo usa el patrón singleton.

Definición
package consoles;

import java.io.InputStream;
import java.io.PrintStream;

public final class MainModule {

  private static MainModule singleton = null;

  public InputStream input = null;
  public PrintStream output = null;
  public PrintStream error = null;

  private MainModule() {
    // does nothing on purpose !!!
  }

  // ...

  public static MainModule getSingleton() {
    if (MainModule.singleton == null) {
       MainModule.singleton = new MainModule();
    }
 
    return MainModule.singleton;
  }

  // ...

  public void prepare() {
    //System.out.println("consoles::prepare();");

    this.input = new InputStream();
    this.output = new PrintStream();
    this.error = new PrintStream();
  }
  
  public void unprepare() {
    this.output = null;
    this.input = null;
    this.error = null;
  
    //System.out.println("consoles::unprepare();");
  }
  
  // ...
  
  public void printNewLine() {
    System.out.println();
  }

  public void printString(String value) {
    System.out.print(value);
  }

  public void printInteger(int value) {
    System.out.print(value);
  }

  public void printBoolean(boolean value) {
    System.out.print(value);
  }
  
  public void scanNewLine() {
    // to-do: ...
  }
  
  public void scanString(String value) {
    // to-do: ...
  }

  public void scanInteger(int value) {
    // to-do: ...
  }

  public void scanBoolean(boolean value) {
    // to-do: ...
  }
  
  // ...
  
}
Implementación
import consoles.*;

class ConsoleDemo {
  public static MainModule console = null;

  public static void prepare() {
    console = MainModule.getSingleton();

    console.prepare();
  }

  public static void unprepare() {
    console.unprepare();
  }

  public static void execute(String[] args) {
    console.printString("Hello World");
    console.printNewLine();
    console.scanNewLine();
  }

  public static void main(String[] args) {
    prepare();
    execute(args);
    unprepare();
  }
}

C # (C Sharp .NET)

C # , como Java, admite espacios de nombres, aunque el patrón sigue siendo útil en casos específicos.

El siguiente ejemplo usa el patrón singleton.

Definición
using System;
using System.IO;
using System.Text;

namespace Consoles {

    public sealed class MainModule {
        private static MainModule Singleton = null;
        public InputStream input = null;
        public OutputStream output = null;
        public ErrorStream error = null;

        // ...

        public MainModule () {
            // does nothing on purpose !!!
        }

        // ...

        public static MainModule GetSingleton() {
            if (MainModule.Singleton == null)
            {
                 MainModule.Singleton = new MainModule();
            }

            return MainModule.Singleton;
        }

        // ...

        public void Prepare() {
            //System.WriteLine("console::prepare();");

            this.input    = new InputStream();
            this.output = new OutputStream();
            this.error    = new ErrorStream();
        }
        
        public void Unprepare() {
            this.output = null;
            this.input    = null;
            this.error    = null;
        
            //System.WriteLine("console::unprepare();");
        }
        
        // ...
    
        public void PrintNewLine() {
            System.Console.WriteLine("");
        }
    
        public void PrintString(String Value) {
            System.Console.Write(Value);
        }
    
        public void PrintInteger(Integer Value) {
            System.Console.Write(Value);
        }
    
        public void PrintBoolean(Boolean Value) {
            System.Console.Write(Value);
        }
        
        public void ScanNewLine() {
            // to-do: ...
        }
        
        public void ScanString(String Value) {
            // to-do: ...
        }
    
        public void ScanInteger(Integer Value) {
            // to-do: ...
        }
    
        public void ScanBoolean(Boolean Value) {
            // to-do: ...
        }
        
        // ...
    
    }
}
Implementación
    class ConsoleDemo {
        public static Consoles.MainModule Console = null;
     
        public static void Prepare()
        {
            Console = Consoles.MainModule.GetSingleton();
     
            Console.Prepare();
        }
     
        public static void Unprepare()
        {
            Console.Unprepare();
        }
     
        public static void Execute()
        {
            Console.PrintString("Hello World");
            Console.PrintNewLine();
            Console.ScanNewLine();
        }
     
        public static void Main()
        {
            Prepare();
            Execute(args);
            Unprepare();
        }
    }

Lenguajes de programación basados ​​en prototipos

JavaScript

JavaScript se usa comúnmente para automatizar páginas web.

Definición
function ConsoleClass() {
  var Input  = null;
  var Output = null;
  var Error  = null;

  // ...
  
  this.prepare = function() {
    this.Input  = new InputStream();
    this.Output = new OutputStream();
    this.Error  = new ErrorStream();
  }

  this.unprepare = function() {
    this.Input  = null;
    this.Output = null;
    this.Error  = null;
  }
  
  // ...
  
  var printNewLine = function() {
    // code that prints a new line
  }

  var printString = function(params) {
    // code that prints parameters
  }

  var printInteger = function(params) {
    // code that prints parameters
  }

  var printBoolean = function(params) {
    // code that prints parameters
  }

  var ScanNewLine = function() {
    // code that looks for a newline
  }

  var ScanString = function(params) {
    // code that inputs data into parameters
  }
  
  var ScanInteger = function(params) {
    // code that inputs data into parameters
  }

  var ScanBoolean = function(params) {
    // code that inputs data into parameters
  }
  
  // ...
  
}
Implementación
function ConsoleDemo() {
  var Console  = null;

  var prepare = function() {
    Console  = new ConsoleClass();

    Console.prepare();
  }
  
  var unprepare = function() {
    Console.unprepare();  
  }

  var run = function() {
    Console.printString("Hello World");
    Console.printNewLine();
  }

  var main = function() {
    this.prepare();
    this.run();
    this.unprepare();
  }  
}

Lenguajes de programación procedimental

Este patrón puede verse como una extensión de procedimiento a los lenguajes orientados a objetos.

Aunque los paradigmas de programación procedimental y modular a menudo se usan juntos, hay casos en los que un lenguaje de programación procedimental puede no admitir completamente los módulos, por lo que se requiere una implementación de patrón de diseño.

PHP (de procedimiento)

Este ejemplo se aplica a PHP procedimental antes de los espacios de nombres (introducido en la versión 5.3.0). Se recomienda que a cada miembro de un módulo se le asigne un prefijo relacionado con el nombre del archivo o el nombre del módulo para evitar colisiones de identificadores.

Definición
<?php
// filename: console.php

function console_prepare()
{
    // code that prepares a "console"
}

function console_unprepare()
{
    // code that unprepares a "console"
}

// ...

function console_printNewLine()
{
    // code that outputs a new line
}

function console_printString(/* String */ Value)
{
    // code that prints parameters
}

function console_printInteger(/* Integer */ Value)
{
    // code that prints parameters
}

function console_printBoolean(/* Boolean */ Value)
{
    // code that prints parameters
}

function console_scanNewLine()
{
    // code that looks for a new line
}

function console_scanString(/* String */ Value)
{
    // code that stores data into parameters
}

function console_scanInteger(/* Integer */ Value)
{
    // code that stores data into parameters
}

function console_scanBoolean(/* Boolean */ Value)
{
    // code that stores data into parameters
}
Implementación
// filename: consoledemo.php

require_once("console.php");

function consoledemo_prepare()
{
    console_prepare();
}

function consoledemo_unprepare()
{
    console_unprepare();
}

function consoledemo_execute()
{
    console_printString("Hello World");
    console_printNewLine();
    console_scanNewLine();
}

function consoledemo_main()
{
    consoledemo_prepare();
    consoledemo_execute();
    consoledemo_unprepare();
}

C

Tenga en cuenta que este ejemplo se aplica al procedimiento C sin espacios de nombres. Se recomienda que a cada miembro de un módulo se le asigne un prefijo relacionado con el nombre del archivo o el nombre del módulo para evitar colisiones de identificadores.

Módulo de encabezado de definición
  // filename: "consoles.h"

  #include <stdio.h>
  #include <string.h>
  #include <ctype.h>
  
  void consoles_prepare(); 
  void consoles_unprepare();

  // ...
  
  void consoles_printNewLine();
  
  void consoles_printString(char* Value);  
  void consoles_printInteger(int Value);  
  void consoles_printBoolean(bool Value);
  
  void consoles_scanNewLine(); 
  
  void consoles_scanString(char* Value);  
  void consoles_scanInteger(int* Value);  
  void consoles_scanBoolean(bool* Value);
Módulo de cuerpo de definición
  // filename: "consoles.c"

  #include <stdio.h>
  #include <string.h>
  #include <ctype.h>
  #include <consoles.h>
  
  void consoles_prepare() {
    // code that prepares console
  }
 
  void consoles_unprepare() {
    // code that unprepares console
  }

  // ...
  
  void consoles_printNewLine() {
    printf("\n");
  }
  
  void consoles_printString(char* Value) {
    printf("%s", Value);
  }
  
  void consoles_printInteger(int Value) {
    printf("%d", &Value);
  }
  
  void consoles_printBoolean(bool Value) {
    if (Value)
    {
      printf("true");
    }
    else
    {
      printf("false");
    }
  }
  
  void consoles_scanNewLine() {
    getch();
  }
  
  void consoles_scanString(char* Value) {
    scanf("%s", Value);
  }
  
  void consoles_scanInteger(int* Value) {
    scanf("%d", Value);
  }
  
  void consoles_scanBoolean(bool* Value) {
    char temp[512];
    scanf("%s", temp);
 
    *Value = (strcmp(Temp, "true") == 0);
  }
Implementación
  // filename: "consoledemo.c"

  #include <stdio.h>
  #include <string.h>
  #include <ctype.h>
  #include <consoles.h>

  void consoledemo_prepare()
  {
    consoles_prepare();
  }
   
  void consoledemo_unprepare()
  {
    consoles_unprepare();
  }
   
  int consoledemo_execute()
  {
    consoles_printString("Hello World");
    consoles_printNewLine();
      consoles_scanNewLine();
   
  return 0;
  }
   
  int main()
  {
    ErrorCode Result = 0;
  
    consoledemo_prepare();
    ErrorCode = consoledemo_execute();
    consoledemo_unprepare();
 
 return ErrorCode;
  }

Pascal procesal

Tenga en cuenta que este ejemplo se aplica a Pascal procedimental no modular. Muchos dialectos de Pascal tienen soporte de espacio de nombres, llamado "unidad (es)". Algunos dialectos también admiten la inicialización y finalización.

Si los espacios de nombres no son compatibles, se recomienda dar a todos los nombres de miembros un prefijo relacionado con el nombre del archivo o del módulo para evitar colisiones de identificadores.

Definición
  unit consoles;
  (* filename: "consoles.pas" *)

  uses crt;

  procedure prepare();
  begin
    (* code that prepares console *)
  end;
 
  procedure unprepare();
  begin
    (* code that unprepares console *)
  end;

  // ...
  
  procedure printNewLine();
  begin
    WriteLn();
  end;
  
  procedure printString(Value: string);
  begin
 Write(Value);
  end;
  
  procedure printInteger(Value: integer);
  begin
 Write(Value);
  end;
  
  procedure printBoolean(Value: boolean);
  begin
    if (Value) then
 begin
     Write('true');
 end else
 begin
     Write('false');
 end;
  end;
  
  procedure scanNewLine();
  begin
    SeekEoLn();
  end;
  
  procedure scanString(Value: string);
  begin
    ReadLn(Value);
  end;
  
  procedure scanInteger(Value: Integer);
  begin
    ReadLn(Value);
  end;
  
  procedure scanBoolean(Value: Boolean);
    var temp: string;
  begin
    ReadLn(temp);
 
    if (Temp = 'true') then
    begin
      Value := true;
    end else
    begin
      Value := false;
    end;
  end;
Implementación
  program consoledemo;
  // filename: "consoles.pas"

  uses consoles;

  procedure prepare();
  begin
    consoles.prepare();
  end;
   
  procedure unprepare();
  begin
    consoles.unprepare();
  end;

  function execute(): Integer;
  begin
    consoles.printString('Hello World');
    consoles.printNewLine();
      consoles.scanNewLine();
   
    execute := 0;
  end;
   
  begin
    prepare();
    execute();
    unprepare();
  end.

Comparaciones con otros conceptos

Espacios de nombres

Tanto los espacios de nombres como los módulos permiten agrupar varias entidades relacionadas mediante un solo identificador y, en algunas situaciones, se utilizan indistintamente. Se puede acceder a esas entidades de forma global. El propósito principal de ambos conceptos es el mismo.

En algunos escenarios, un espacio de nombres requiere que los elementos globales que lo componen se inicialicen y finalicen mediante una llamada a una función o método.

En muchos lenguajes de programación, los espacios de nombres no están destinados directamente a admitir un proceso de inicialización ni un proceso de finalización y, por lo tanto, no son equivalentes a módulos. Esa limitación se puede solucionar de dos maneras. En los espacios de nombres que admiten funciones globales, una función para la inicialización y una función para la finalización se codifican directamente y se llaman directamente en el código del programa principal.

Clases y espacios de nombres

Las clases se utilizan a veces como espacios de nombres o con ellos . En los lenguajes de programación que no admiten espacios de nombres (por ejemplo, JavaScript) pero sí admiten clases y objetos, las clases se utilizan a menudo para sustituir espacios de nombres. Por lo general, estas clases no se instancian y consisten exclusivamente en miembros estáticos.

Espacios de nombres y clases de singleton

En los lenguajes de programación orientados a objetos donde los espacios de nombres no son completamente compatibles, el patrón singleton puede usarse en lugar de miembros estáticos dentro de una clase no instanciable.

Relación con otros patrones de diseño

El patrón del módulo se puede implementar mediante una especialización del patrón singleton. Sin embargo, se pueden aplicar y combinar otros patrones de diseño en la misma clase.

Este patrón se puede utilizar como decorador , peso mosca o adaptador .

Módulo como patrón de diseño

El patrón del módulo se puede considerar un patrón de creación y un patrón estructural . Gestiona la creación y organización de otros elementos y los agrupa como lo hace el patrón estructural.

Un objeto que aplica este patrón puede proporcionar el equivalente de un espacio de nombres , proporcionando el proceso de inicialización y finalización de una clase estática o una clase con miembros estáticos con una sintaxis y una semántica más limpias y concisas .

Admite casos específicos en los que una clase u objeto pueden considerarse datos de procedimiento estructurados. Y viceversa, migrar datos estructurados, procedimentales y considerados como orientados a objetos.

Ver también