La programación modular es una técnica de diseño de software que enfatiza la separación de la funcionalidad de un programa en módulos independientes e intercambiables , de modo que cada uno contenga todo lo necesario para ejecutar solo un aspecto o "preocupación" de la funcionalidad deseada.
Una interfaz de módulo expresa los elementos que proporciona y requiere el módulo. Los elementos definidos en la interfaz son detectables por otros módulos. La implementación contiene el código de trabajo que corresponde a los elementos declarados en la interfaz. La programación modular está estrechamente relacionada con la programación estructurada y la programación orientada a objetos , todas con el mismo objetivo de facilitar la construcción de grandes programas y sistemas de software mediante la descomposición en piezas más pequeñas, y todas se originaron alrededor de la década de 1960. Si bien el uso histórico de estos términos ha sido inconsistente, "programación modular" ahora se refiere a la descomposición de alto nivel del código de un programa completo en piezas: programación estructurada al uso de código de bajo nivel de flujo de control estructurado , y programación orientada a objetos al uso de datos de objetos , un tipo de estructura de datos .
En la programación orientada a objetos, el uso de interfaces como patrón arquitectónico para construir módulos se conoce como programación basada en interfaces . [ cita requerida ]
La programación modular, en forma de subsistemas (particularmente para E/S) y bibliotecas de software, data de los primeros sistemas de software, donde se utilizaba para la reutilización de código . La programación modular per se, con un objetivo de modularidad, se desarrolló a fines de la década de 1960 y en la década de 1970, como un análogo a mayor escala del concepto de programación estructurada (década de 1960). El término "programación modular" data al menos del Simposio Nacional sobre Programación Modular, organizado en el Instituto de Información y Sistemas en julio de 1968 por Larry Constantine ; otros conceptos clave fueron el ocultamiento de información (1972) y la separación de intereses (SoC, 1974).
Los módulos no se incluyeron en la especificación original de ALGOL 68 (1968), pero se incluyeron como extensiones en las primeras implementaciones, ALGOL 68-R (1970) y ALGOL 68C (1970), y luego se formalizaron. [1] Uno de los primeros lenguajes diseñados desde el principio para la programación modular fue el efímero Modula (1975), de Niklaus Wirth . Otro lenguaje modular temprano fue Mesa (década de 1970), de Xerox PARC , y Wirth se basó en Mesa, así como en el Modula original en su sucesor, Modula-2 (1978), que influyó en los lenguajes posteriores, particularmente a través de su sucesor, Modula-3 (década de 1980). El uso de nombres calificados por punto en Modula , como M.a
para referirse a un objeto a
desde un módulo M
, coincide con la notación para acceder a un campo de un registro (y de manera similar para atributos o métodos de objetos), y ahora está muy extendido, viéndose en C++ , C# , Dart , Go , Java , OCaml y Python , entre otros. La programación modular se generalizó a partir de la década de 1980: el lenguaje Pascal original (1970) no incluía módulos, pero versiones posteriores, en particular UCSD Pascal (1978) y Turbo Pascal (1983) los incluyeron en forma de "unidades", al igual que Ada (1980) influenciado por Pascal . El estándar Extended Pascal ISO 10206:1990 se mantuvo más cerca de Modula2 en su soporte modular. El estándar ML (1984) [2] tiene uno de los sistemas de módulos más completos, incluyendo functores (módulos parametrizados) para mapear entre módulos.
En los años 1980 y 1990, la programación modular se vio eclipsada y a menudo confundida con la programación orientada a objetos , en particular debido a la popularidad de C++ y Java. Por ejemplo, la familia de lenguajes C tenía soporte para objetos y clases en C++ (originalmente C with Classes , 1980) y Objective-C (1983), y solo admitió módulos 30 años o más después. Java (1995) admite módulos en forma de paquetes, aunque la unidad principal de organización de código es una clase. Sin embargo, Python (1991) utilizó de manera destacada tanto módulos como objetos desde el principio, utilizando módulos como la unidad principal de organización de código y "paquetes" como una unidad de mayor escala; y Perl 5 (1994) incluye soporte para módulos y objetos, con una amplia gama de módulos disponibles en CPAN (1993). OCaml (1996) siguió a ML al admitir módulos y funtores.
La programación modular está muy extendida en la actualidad y se encuentra en prácticamente todos los lenguajes principales desarrollados desde los años 1990. La importancia relativa de los módulos varía entre lenguajes y en los lenguajes orientados a objetos basados en clases todavía hay superposición y confusión con las clases como unidad de organización y encapsulación, pero ambos están bien establecidos como conceptos distintos.
En ocasiones se utiliza el término ensamblaje (como en lenguajes .NET como C# , F# o Visual Basic .NET ) o paquete (como en Dart , Go o Java ) en lugar de módulo . En otras implementaciones, estos son conceptos distintos; en Python , un paquete es una colección de módulos, mientras que en Java 9 se implementó la introducción del nuevo concepto de módulo (una colección de paquetes con control de acceso mejorado).
Además, el término "paquete" tiene otros usos en el software (por ejemplo, los paquetes NuGet de .NET ). Un componente es un concepto similar, pero normalmente se refiere a un nivel superior; un componente es una parte de un sistema completo , mientras que un módulo es una parte de un programa individual. La escala del término "módulo" varía significativamente entre lenguajes; en Python es a muy pequeña escala y cada archivo es un módulo, mientras que en Java 9 está previsto que sea a gran escala, donde un módulo es una colección de paquetes, que a su vez son colecciones de archivos.
Otros términos para módulos incluyen unidad , utilizado en dialectos Pascal .
Los lenguajes que soportan formalmente el concepto de módulo incluyen Ada , ALGOL , BlitzMax , C++ , C# , Clojure , COBOL , Common Lisp , D , Dart , eC, Erlang , Elixir , Elm , F , F# , Fortran , Go , Haskell , IBM/360 Assembler , Control Language (CL), IBM RPG , Java , [a] Julia , MATLAB , ML , Modula , Modula-2 , Modula-3 , Morpho, NEWP , Oberon , Oberon-2 , Objective-C , OCaml , varios derivados de Pascal ( Component Pascal , Object Pascal , Turbo Pascal , UCSD Pascal ), Perl , PHP , PL/I , PureBasic , Python , R , Ruby , [4] Rust , JavaScript , [5] Visual Basic (.NET) y WebDNA .
Ejemplos notables de lenguajes que carecen de soporte para módulos son C y C++ y Pascal en su forma original, sin embargo, C y C++ permiten que se especifiquen compilaciones separadas e interfaces declarativas mediante archivos de encabezado . Los módulos se agregaron a Objective-C en iOS 7 (2013); a C++ con C++20 , [6] y Pascal fue reemplazado por Modula y Oberon , que incluían módulos desde el principio, y varios derivados que incluían módulos. JavaScript ha tenido módulos nativos desde ECMAScript 2015.
La programación modular se puede realizar incluso cuando el lenguaje de programación carece de características sintácticas explícitas para admitir módulos con nombre, como, por ejemplo, en C. Esto se hace utilizando características del lenguaje existentes, junto con, por ejemplo, convenciones de codificación , modismos de programación y la estructura física del código. IBM i también utiliza módulos al programar en el entorno de lenguaje integrado (ILE).
Con la programación modular, las preocupaciones se separan de tal manera que los módulos realizan funciones lógicamente discretas, interactuando a través de interfaces bien definidas. A menudo, los módulos forman un grafo acíclico dirigido (DAG); en este caso, una dependencia cíclica entre módulos se considera como una indicación de que estos deberían ser un solo módulo. En el caso en que los módulos formen un DAG, se pueden organizar como una jerarquía, donde los módulos de nivel más bajo son independientes, no dependen de otros módulos, y los módulos de nivel superior dependen de los de nivel inferior. Un programa o biblioteca en particular es un módulo de nivel superior de su propia jerarquía, pero a su vez puede verse como un módulo de nivel inferior de un programa, biblioteca o sistema de nivel superior.
Al crear un sistema modular, en lugar de crear una aplicación monolítica (donde el componente más pequeño es el todo), se escriben por separado varios módulos más pequeños para que, al componerse juntos, construyan el programa de aplicación ejecutable. Normalmente, estos también se compilan por separado, mediante una compilación independiente, y luego se vinculan mediante un enlazador . Un compilador justo a tiempo puede realizar parte de esta construcción "sobre la marcha" en tiempo de ejecución .
Estas funciones independientes se clasifican comúnmente como funciones de control de programa o funciones de tarea específica. Las funciones de control de programa están diseñadas para funcionar en un programa. Las funciones de tarea específica están cuidadosamente preparadas para ser aplicables a varios programas.
Esto hace que los sistemas diseñados modularmente, si se construyen correctamente, sean mucho más reutilizables que un diseño monolítico tradicional, ya que todos (o muchos) de estos módulos pueden reutilizarse (sin cambios) en otros proyectos. Esto también facilita la "división" de proyectos en varios proyectos más pequeños. En teoría, un proyecto de software modularizado será más fácil de ensamblar por equipos grandes, ya que ningún miembro del equipo crea el sistema completo, ni siquiera necesita saber sobre el sistema en su totalidad. Pueden concentrarse solo en la tarea más pequeña asignada.