Datos, contexto e interacción ( DCI ) es un paradigma utilizado en software informático para programar sistemas de objetos que se comunican . Sus objetivos son:
El paradigma separa el modelo de dominio (datos) de los casos de uso (contexto) y los roles que desempeñan los objetos (interacción). DCI es complementario a modelo-vista-controlador (MVC). MVC como lenguaje de patrones todavía se utiliza para separar los datos y su procesamiento de la presentación.
Los datos siguen siendo "lo que es el sistema ". [1] La parte de datos de la arquitectura DCI es su modelo de datos (relativamente) estático con relaciones. El diseño de datos suele estar codificado como clases convencionales que representan la estructura básica del dominio del sistema. Estas clases son apenas datos inteligentes, [1] [2] y carecen explícitamente de la funcionalidad que es peculiar para soportar cualquier caso de uso particular . Estas clases comúnmente encapsulan el almacenamiento físico de los datos. Estos datos implementan una estructura de información que proviene del modelo mental de los usuarios finales, expertos en el dominio, programadores y otras personas en el sistema. Pueden corresponder estrechamente a los objetos del modelo de MVC. [1]
Un ejemplo de un objeto de datos podría ser una cuenta bancaria. Su interfaz tendría operaciones básicas para aumentar y disminuir el saldo y para consultar el saldo actual. La interfaz probablemente no ofrecería operaciones que involucren transacciones o que de alguna manera involucren otros objetos o cualquier interacción del usuario. Por lo tanto, por ejemplo, si bien una cuenta bancaria puede ofrecer una primitiva para aumentar el saldo, no tendría ningún método llamado deposit
. Tales operaciones pertenecen en cambio a la parte de interacción de DCI. [1]
Los objetos de datos son instancias de clases que pueden provenir de un diseño impulsado por el dominio , y dichas clases pueden usar relaciones de subtipificación para organizar los datos del dominio. Aunque al final se reduce a clases, DCI refleja un modelo computacional dominado por el pensamiento de objetos en lugar del pensamiento de clases. Por lo tanto, cuando se piensa en "datos" en DCI, significa pensar más en las instancias en tiempo de ejecución que en las clases a partir de las cuales se crearon. [3]
El contexto es la clase (o su instancia) cuyo código incluye los Roles para un algoritmo, escenario o caso de uso determinado, así como el código para mapear estos Roles en objetos en tiempo de ejecución y para implementar el caso de uso. Cada Rol está vinculado a exactamente un objeto durante la implementación de cualquier caso de uso determinado; sin embargo, un solo objeto puede desempeñar simultáneamente varios Roles. [4] Un contexto se instancia al comienzo de la implementación de un algoritmo, escenario o caso de uso. En resumen, un contexto comprende casos de uso y algoritmos en los que se utilizan objetos de datos a través de Roles específicos. [1]
Cada contexto representa uno o más casos de uso. [1] Se crea una instancia de un objeto de contexto para cada implementación de un caso de uso del que es responsable. Su función principal es identificar los objetos que participarán en el caso de uso y asignarles los roles que llevan a cabo el caso de uso a través de sus responsabilidades. Un rol puede incluir métodos, y cada método es una pequeña parte de la lógica de un algoritmo que implementa un caso de uso. Los métodos de rol se ejecutan en el contexto de un objeto que el contexto selecciona para desempeñar ese rol en la implementación del caso de uso actual. Las vinculaciones de rol a objeto que tienen lugar en un contexto se pueden contrastar con el polimorfismo de la programación orientada a objetos vernácula. [5] La funcionalidad empresarial general es la suma de redes complejas y dinámicas de métodos descentralizados en múltiples contextos y sus roles.
Cada contexto es un ámbito que incluye identificadores que corresponden a sus Roles. [1] Cualquier Rol que se ejecute dentro de ese contexto puede hacer referencia a los otros Roles en ese contexto a través de estos identificadores. [1] Estos identificadores han llegado a llamarse RoleMethods . [5] En el momento de la implementación del caso de uso, todos y cada uno de estos identificadores se vinculan a un objeto que desempeña el Rol correspondiente para este contexto.
Un ejemplo de contexto podría ser una transferencia bancaria entre dos cuentas, donde los modelos de datos (las cuentas bancarias) se utilizan a través de roles denominados SourceAccount y DestinationAccount.
La interacción es "lo que hace el sistema ". [1] La interacción se implementa como Roles que son desempeñados por objetos en tiempo de ejecución. Estos objetos combinan el estado y los métodos de un objeto de datos (dominio) con métodos (pero sin estado, ya que los Roles no tienen estado [6] ) de uno o más Roles. [1] En un buen estilo DCI, un Rol se dirige a otro objeto solo en términos de su Rol (sin método). Hay un Rol especial llamado self
que se vincula al objeto que desempeña el Rol actual. El código dentro de un método de Rol puede invocar un método en self
y, por lo tanto, invocar un método de la parte de datos del objeto actual. Un aspecto curioso de DCI es que se garantiza que estos enlaces estén en su lugar solo en tiempo de ejecución (usando una variedad de enfoques y convenciones; se pueden usar plantillas de C++ para garantizar que los enlaces tendrán éxito [7] ). Esto significa que las interacciones, los métodos de Rol, son genéricos . De hecho, algunas implementaciones de DCI usan genéricos o plantillas para Roles.
Un Rol es una construcción de programación sin estado que corresponde al modelo mental del usuario final de alguna entidad en el sistema. [3] Un Rol representa una colección de responsabilidades. Mientras que la programación orientada a objetos vernácula habla de objetos o clases como una multiplicidad de responsabilidades, DCI los atribuye a Roles. Un objeto que participa en un caso de uso tiene responsabilidades: aquellas que asume como resultado de desempeñar un Rol particular. La mayoría de los lenguajes de programación modernos tienen una forma de expresar Roles y de expresar la inyección de métodos de Rol en objetos, y las técnicas de implementación varían según el lenguaje. La inyección puede ser completamente dinámica en tiempo de ejecución en lenguajes como Ruby y Python ; es más estática en lenguajes como Smalltalk - Squeak , Scala y C++ . El entorno de programación Qi4j ofrece una forma de expresar la inyección de métodos de Rol en objetos Java. [8] El método predeterminado de Java 8 en las interfaces se puede utilizar para implementar Roles de una manera segura para los tipos.
En el caso de uso de transferencia de dinero mencionado anteriormente, por ejemplo, los métodos Role en SourceAccount y DestinationAccount ejecutan la transferencia real.
La DCI limita todas las redes permisibles de objetos en comunicación a redes que comparten topologías comunes, una para cada caso de uso. [9] [3] Estas redes son explícitas en las interacciones entre Roles de DCI, mientras que en la orientación clásica a objetos son emergentes. Un Rol es un nodo en dicha topología; es una clasificación parcial de los comportamientos de todos los objetos que pueden ocupar este nodo. La topología es una descripción de la estructura de tiempo de ejecución de un sistema. [10]
Un programa orientado a objetos es una red compleja y dinámica de objetos, en el mismo sentido en que las relaciones entre objetos del mundo real son complejas y dinámicas. Consideremos un camarero de un restaurante. El propio camarero es un objeto complejo que puede verse de muchas maneras: como mi camarero (por ejemplo, que describe el menú de esta noche y toma mi pedido), como un empleado del restaurante (con un salario y un horario de trabajo determinados) y como una persona del restaurante (que tiene un límite de ocupación de 150 personas). Si se escribiera una clase de camarero para capturar la esencia del mundo real de los camareros (que es de lo que se trata realmente la orientación a objetos), tendría que ser muy compleja para admitir todas estas perspectivas.
En DCI, estas diferentes vistas se factorizan en Roles. En tiempo de ejecución, el Rol es la identidad del objeto. [5] Durante la promulgación de un caso de uso (como Serve the Wine ) el Rol Waiter identifica inequívocamente un solo objeto en un momento dado. [4] Se podría argumentar que puede haber varios Waiters en la mesa. Sin embargo, es probable que difieran en sus responsabilidades dentro de un caso de uso , como se encuentra en los nombres de Role HeadWaiter y Busboy. Incluso si sus responsabilidades son idénticas, aún se describirían como Waiter-1 y Waiter-2, o como elementos individuales (nombrados) de un vector Waiter, si alguien tuviera la intención de escribir software para ellos. Entonces, un Rol como HeadWaiter se convierte en el identificador, el identificador, por el cual los objetos se refieren entre sí en un caso de uso .
DCI reconoce al Camarero como un objeto en lugar de, por ejemplo, una composición de una parte de Empleado, una parte de Camarero y una parte de Persona. El objeto tiene su propia identidad independiente del caso de uso ; esta es la faceta de Datos de DCI. Los Roles son nombres alias para sus objetos pero nunca son objetos separados en sí mismos; eso causaría esquizofrenia personal . En este sentido, cada Camarero es un homo sapiens . Esta es la parte rudimentaria de qué-es-el-sistema del Camarero. El objeto tiene muchas identidades posibles dependiendo del caso de uso en el que esté involucrado; esto surge en las identidades de Rol que forman parte de la faceta de Interacción de DCI. Estas son la parte (generalmente más interesante) de qué-hace-el-sistema. Sin embargo, en DCI, solo hay un único objeto que lleva ambas perspectivas en tiempo de ejecución. Estas perspectivas se pueden agrupar de manera diferente en el momento de la codificación. El código está dominado por la estructura del caso de uso , que abarca todos los objetos y que también es parte de la faceta de Interacción de DCI.
DCI permite que un objeto asuma uno o más Roles durante la implementación de un caso de uso . En otras palabras, un objeto se vuelve a vincular a identificadores de Roles en cada implementación de un caso de uso . [4] Estos Roles infieren una interfaz, denominada tipo de Rol. Cada objeto se "reestructura" (en el sentido teatral) nuevamente en cada caso de uso . Aunque un Rol está vinculado solo a un único objeto, un objeto puede desempeñar varios Roles. Por ejemplo, un Jefe de Camareros puede participar en un caso de uso para contar a todos los ocupantes del restaurante durante una inspección de incendios, y desempeñará el Rol de Persona así como el Rol de Jefe de Camareros. El objeto único admite los comportamientos de ambos Roles necesarios para llevar a cabo el caso de uso .
En resumen, las arquitecturas DCI tienden a caracterizarse por las siguientes propiedades: [1] [10]
Se puede pensar en DCI como un paradigma de programación impulsado por eventos , donde algún evento (como un gesto humano en una arquitectura modelo-vista-controlador (MVC)) activa un caso de uso . [3] El caso de uso puede ser de corta o larga duración. Los eventos se denominan activadores y se manejan en el entorno en el que está integrado DCI. Este entorno puede ser el controlador de una arquitectura MVC convencional o cualquier otro código a nivel de sistema.
El disparador hace que el entorno cree una instancia de un objeto de contexto . El tipo de objeto se elige según el tipo de caso de uso que se producirá, en función de la información sobre el disparador o el estado del sistema o ambos. Por ejemplo, un cajero automático puede tener clases de contexto independientes para transferencia de dinero, retiro, depósito, consulta de saldo, etc. Una vez que el entorno crea una instancia del objeto de contexto, invoca su método disparador para iniciar la ejecución del caso de uso. [4]
Como se describió anteriormente, cada contexto proporciona un alcance de diseño para los roles que participan en la implementación del caso de uso . La tarea del contexto es asignar objetos para desempeñar estos roles. [5]
MESSAGE NOT UNDERSTOOD
un tiempo de ejecución si se invocara el método Rol). En lenguajes con tipos más estáticos (Scala, C++) tiene que haber habido algún arreglo previo para que el objeto admita los métodos de Rol. Por ejemplo, Scala crea una clase anónima que combina la lógica rudimentaria de una clase de dominio con la lógica de caso de uso del rasgo utilizado para implementar un Rol; los Roles se asignan efectivamente a objetos de dominio cuando se instancian.self
que, de hecho, es manejado por el objeto que actualmente desempeña el Rol. Así es como los Roles invocan las operaciones de datos rudimentarias de los objetos que los están desempeñando en ese momento.La DCI depende de un proceso de diseño que separa los casos de uso del modelo de datos. El modelo de datos suele basarse en un análisis de dominio informal. Los roles que caracterizan el modelo de funcionalidad del sistema del usuario final provienen de los casos de uso . [3]
Las técnicas de implementación difieren entre los lenguajes de programación. Lo que es común a muchos enfoques es que los Roles están representados por construcciones tales como genéricos, plantillas, clases o rasgos . El código para la lógica básica del dominio se implementa por separado, siguiendo la práctica convencional orientada a objetos y más comúnmente usando clases. El código de cada Rol se inyecta en el objeto de dominio que lo reproducirá durante la promulgación del caso de uso . Para implementar Roles , generalmente se necesita la inyección de métodos. Los rasgos son una técnica común del lenguaje de programación para admitir la inyección de métodos. Algunos lenguajes, como Scala , tienen soporte nativo para rasgos , mientras que otros lenguajes (por ejemplo, Ruby y Python ) permiten la inyección de métodos en tiempo de ejecución. En Java , se necesitan trucos de precompilador basados en anotaciones para admitir DCI. Haxe usa su función de macro en tiempo de compilación para transformar la semántica de DCI al código del lenguaje de destino.
Existen varias implementaciones de ejemplo en el sitio fulloo.info mantenido por los creadores de DCI.
El DCI fue inventado por Trygve Reenskaug , también inventor de MVC. La formulación actual del DCI es en su mayor parte obra de Reenskaug y James O. Coplien . [1] [3] [9]
DCI surgió en gran medida como resultado del trabajo de Trygve Reenskaug sobre el modelado basado en roles. [11] Trygve había reconocido desde hacía tiempo que los roles desempeñaban un papel central en la forma en que los programadores pensaban sobre los objetos, y que la progresión basada en clases de la tecnología de lenguajes de programación eliminaba gran parte de la motivación para pensar sobre los objetos en un programa. Eso, a su vez, dificultaba razonar sobre el programa en tiempo de ejecución. Además, el hecho de que los lenguajes de programación orientados a objetos ofrecieran solo clases para expresar la lógica del programa dejaba al programador a merced de la disposición estructural de los datos para delinear el comportamiento, lo que es poco natural en comparación con un comportamiento delineante en los límites de los roles. Esto, a su vez, hacía que el comportamiento del programa fuera más difícil de razonar que, por ejemplo, en un programa procedimental en Fortran . [ cita requerida ]
Trygve consideró que era importante crear estructuras de programas sobre las que se pudiera razonar, y comenzó a socializar estas ideas ya en el año 2000. En 2006 ya contaba con un modelo de diseño funcional, y su descubrimiento en 2008 del trabajo de Schärli sobre Traits proporcionó la piedra angular que proporcionaría una expresión natural de lenguaje de programación de estas ideas. Prototipó las ideas en el entorno de programación Baby, escrito en Squeak. James Coplien se unió a Trygve en este esfuerzo en 2007 aproximadamente y a mediados de 2008 ya tenía un prototipo funcionando en C++ . Steen Lehmann, Rickard Öberg y Niclas Hedhman aceleraron la adaptación de estas ideas a Ruby y Java durante el año siguiente aproximadamente con el marco Qi4j. [8] Muchas adaptaciones de lenguaje adicionales siguieron a una sesión en la conferencia JaOO en Dinamarca en septiembre de 2008. En 2010, Rune Lund-Søltoft creó el lenguaje Marvin. Fue el primer lenguaje creado con soporte nativo para DCI. Marvin fue pensado principalmente como una prueba de concepto para mostrar la idea de "DCI sin inyección". La mayoría de las implementaciones anteriores modificaron los objetos de los jugadores de rol de una manera que fuera visible fuera del contexto. James Coplien creó el lenguaje trygve basado en la misma idea, el primer lenguaje creado desde cero para soportar DCI.
Los diferentes enfoques adoptados para la evolución de la programación orientada a objetos, tanto a nivel de lenguaje como de patrones , coinciden en diversos grados con DCI:
Los desafíos de la programación orientada a objetos también pueden afrontarse abordando sus problemas a nivel de paradigma.
Un estudio de 2017 realizado por Héctor Valdecantos et. al. mostró que “el análisis de corrección muestra que los programadores del grupo DCI se desempeñaron mejor que los programadores del grupo OOjava. Esto está en línea con las teorías detrás de DCI y debería alentar investigaciones más sistemáticas sobre las posibilidades de este paradigma”. [15]
Según los investigadores Bluemke y Stepień del Instituto de Ciencias Informáticas de la Universidad Tecnológica de Varsovia, "los sistemas basados en DCI son mucho más flexibles que los tradicionales, esto se debe al hecho de que las partes estáticas (datos) y dinámicas (contexto, interacción) del sistema están separadas y la separación de preocupaciones es una estrategia muy poderosa para dominar la complejidad". [23]
{{cite book}}
: |website=
ignorado ( ayuda ){{cite book}}
: |website=
ignorado ( ayuda )