Programación modular
Programación modular es una técnica de diseño de software que enfatiza separar 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 de la funcionalidad deseada.
La interfaz de un 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, y todas tienen el mismo objetivo de facilitar la construcción de grandes programas y sistemas de software mediante su 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, la "programación modular" ahora se refiere a la descomposición de alto nivel del código de un programa completo en partes: programación estructurada para el uso de código de bajo nivel de flujo de control estructurado y programación orientada a objetos para el uso de datos de objetos., una especie 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.
Historia
La programación modular, en forma de subsistemas (particularmente para E/S) y bibliotecas de software, se remonta a los primeros sistemas de software, donde se usaba para la reutilización de código. La programación modular per se, con el objetivo de modularidad, se desarrolló a finales de los años 1960 y 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 Sistemas e Información en julio de 1968 por Larry Constantine; otros conceptos clave fueron el ocultamiento de información (1972) y la separación de preocupaciones (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. 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 y en el Modula original en su sucesor, Modula-2 (1978), que influyó en lenguajes posteriores, particularmente a través de su sucesor, Modula-3 (década de 1980). El uso que hace Modula de nombres calificados con puntos, como M.a
para referirse al objeto a
del módulo M
, coincide con la notación para acceder a campo de un registro (y de manera similar para atributos o métodos de objetos), y ahora está muy extendido, visto en 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 incluían en forma de "unidades", al igual que Ada (1980), influenciada por Pascal. El estándar Extended Pascal ISO 10206:1990 se acercó más a Modula2 en su soporte modular. Standard ML (1984) tiene uno de los sistemas de módulos más completos, incluidos functores (módulos parametrizados) para mapear entre módulos.
En las décadas de 1980 y 1990, la programación modular fue eclipsada y a menudo mezclada con la programación orientada a objetos, particularmente debido a la popularidad de C++ y Java. Por ejemplo, la familia de lenguajes C admitía objetos y clases en C++ (originalmente C con clases, 1980) y Objective-C (1983), y solo admitía 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 del 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 unidad principal de organización del código y "paquetes" como unidad de mayor escala; y Perl 5 (1994) incluye soporte tanto para módulos como para objetos, con una amplia gama de módulos disponibles en CPAN (1993). OCaml (1996) siguió a ML apoyando módulos y functores.
La programación modular ahora está muy extendida y se encuentra prácticamente en todos los lenguajes principales desarrollados desde la década de 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.
Terminología
El término ensamblador (como en lenguajes.NET como C#, F# o Visual Basic.NET) o paquete (como en Dart, Go o Java) a veces se utiliza 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 software (por ejemplo, paquetes.NET NuGet). 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 idiomas; 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, usado en dialectos Pascal.
Soporte de idiomas
Los lenguajes que respaldan 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, Julia, MATLAB, ML, Modula, Modula-2, Modula-3, Morpho, NEWP, Oberon, Oberon-2, Objective-C, OCaml, varios derivados de Pascal (Componente Pascal, Objeto Pascal, Turbo Pascal, UCSD Pascal), Perl, PHP, PL/I, PureBasic, Python, R, Ruby, Rust, JavaScript, Visual Basic.NET y WebDNA.
Ejemplos visibles de lenguajes que carecen de soporte para módulos son C y han sido C++ y Pascal en su forma original; sin embargo, C y C++ permiten que se especifiquen interfaces declarativas y de compilación separadas mediante archivos de encabezado. Se agregaron módulos a Objective-C en iOS 7 (2013); a C++ con C++20, 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 existentes del lenguaje, junto con, por ejemplo, convenciones de codificación y modismos de programación. y la estructura del código físico. IBM i también utiliza módulos al programar en el entorno de lenguaje integrado (ILE).
Aspectos clave
Con programación modular, se separan preocupaciones 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 gráfico acíclico dirigido (DAG); en este caso se considera que una dependencia cíclica entre los módulos indica que éstos deben ser un solo módulo. En el caso en que los módulos forman un DAG se pueden organizar como una jerarquía, donde los módulos de menor nivel son independientes, dependiendo de ningún otro módulo, y los módulos de nivel superior dependen de los de menor nivel. Un programa o biblioteca particular es un módulo de alto nivel de su propia jerarquía, pero a su vez se puede ver como un módulo de menor nivel de un programa de alto nivel, biblioteca o sistema.
Al crear un sistema modular, en lugar de crear una aplicación monolítica (donde el componente más pequeño es el conjunto), varios módulos más pequeños se escriben por separado para que cuando se componen juntos, construyen el programa de aplicación ejecutable. Típicamente, estos también se compilan por separado, a través de compilación separada, y luego se vinculan por un linker. Un compilador justo a tiempo puede realizar algunas de estas construcciones "en el vuelo" a tiempo de ejecución.
Estas funciones independientes se clasifican comúnmente como funciones de control de programas o funciones de tareas específicas. Las funciones de control de programas están diseñadas para funcionar para un programa. Las funciones de tareas específicas están cuidadosamente preparadas para ser aplicables a varios programas.
Esto hace que los sistemas de diseño modular, 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 luego reutilizarse (sin cambios) en otros proyectos. Esto también facilita la "desintegració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 está creando todo el sistema, ni siquiera necesita conocer el sistema en su totalidad. Pueden concentrarse sólo en la tarea más pequeña asignada.