2012-06-08 9 views
6

yo estaba pasando por programación el libro de Martin Odersky en Scala con ella es la sección de módulos abstractos, y su papel escalables de componentes abstracciones:¿Módulos abstractos de estilo Scala en C# u otros idiomas?

http://lampwww.epfl.ch/~odersky/papers/ScalableComponent.pdf

Mi comida para llevar es que al hacer sus módulos clases abstractas en lugar de objetos (o clásicos , módulos globales estáticas como en Java):

abstract class myModule{ 
    // this is effectively an abstract module, whose concrete 
    // components can be configured by subclassing and instantiating it 
    class thing{} 
    class item{} 
    object stuff{} 
    class element{} 
    object utils{} 
} 

puede instanciar múltiples subclases e instancias de un módulo con diferentes características concretas. Esto le permite configurar el módulo de manera diferente dependiendo de las circunstancias (por ejemplo, sustituyendo el componente de la base de datos durante la prueba o el componente IO cuando se encuentra en un entorno de desarrollo) e instalando múltiples módulos, cada uno con su propio conjunto de estados modificados con ámbito de módulo.

Por lo que puedo entender, en un nivel básico, el único requisito importante es que pueda tener clases anidadas, de modo que la clase adjunta pueda actuar como un módulo.

Otro requisito práctico es que puede distribuir la definición de clase en varios archivos, ya que un módulo con un montón de clases probablemente contenga más líneas de código de las que la mayoría aceptaría en un único archivo fuente.

Scala hace esto con los rasgos, que traen algunos otros objetos que son agradables pero no centrales para todo el propagar abstract-module-class sobre varios archivos fuente idea. C# tiene partial classes, que proporciona la misma funcionalidad y también permite clases anidadas. Presumiblemente algunos otros lenguajes tienen soporte similar para clases anidadas, así como también para dividir una clase en múltiples archivos.

¿Este tipo de patrón se produce en cualquier lugar en C# o en otros idiomas? Creo que los grandes proyectos en muchos idiomas enfrentan el problema de que módulos abstractos están destinados a resolver. ¿Hay alguna razón para que esta cosa de "clase abstracta como módulo abstracto" no funcione y, por lo tanto, no se use? Me parece una solución mucho más limpia que los diversos marcos DI con el tipo de oferta de la misma funcionalidad.

+2

Creo que estás de suerte, y la razón es un malentendido: ** las clases parciales no tienen nada que ver con los rasgos **. Las clases parciales son simplemente una forma de propagar una definición de clase en varios archivos, mientras que los rasgos son un mecanismo poderoso (y no existente en C#) del sistema de tipos./EDITAR O tal vez no, si solo necesitas el mecanismo proporcionado por clases parciales. –

+1

Mi punto es que aunque los rasgos te permiten hacer todo tipo de cosas agradables con tus módulos abstractos, el núcleo del elemento 'clase-como-abstracto-módulo' solo requiere la capacidad de jerarquizar clases y extenderlas en múltiples archivos. Podría estar completamente equivocado en esto, así que por favor corrígeme si lo hago. –

Respuesta

2

El módulo abstracta describiría tiene las siguientes propiedades básicas:

  • Es un módulo cerrado, lo que significa que proporciona toda la interfaz que permite la interacción con el módulo
  • Puede especificar las operaciones de el módulo
  • Puede especificar los tipos que forman parte del módulo; por lo general, estos tipos son operados por los módulos antes mencionados
  • No se proporciona ninguna implementación, esto es la abstracta parte: no puede haber muchas implementaciones concretas y que en realidad es utilizado en el programa serán especificados y seleccionados por el programa, ya que mejor se adapte a la necesidad

La característica de ser capaz de especificar el módulo usando más de un archivo fuente no es un requisito básico, pero puede ser útil.

En su forma más básica, un módulo describe un tipo de datos abstracto (por ejemplo, cola): qué operaciones están disponibles para interactuar con el tipo de datos y cualquier tipo auxiliar que se necesite para la interacción.

En una forma más compleja, puede describir un subsistema completo (por ejemplo, redes).

En los lenguajes imperativos normalmente utiliza una interfazpara el mismo propósito:

  • Está encerrado
  • Puede especificar las operaciones
  • Puede especificar los tipos que son parte de la interfaz
  • Sin implementación

Como mencionas, si su módulo tiene una interfaz grande (p. describe un subsistema), por lo general no es práctico escribir las clases que implementan la interfaz enriquecida en un archivo. Si el lenguaje no proporciona soporte para dividir la misma clase en fuentes separadas (o más exactamente: para "pegar" partes separadas de la misma clase de diferentes archivos fuente juntos), la solución generalmente es perder el incluido requisito y proporcionar una serie de interfaces que especifican las interacciones entre ellos, por lo que obtienes una API para el subsistema (es una API en su sentido más puro: es una interfaz para interactuar con el subsistema, sin implementación aún).

En cierto modo, este último enfoque puede ser más genérico (genérico en el sentido de lo que puede lograr con él) que un tipo cerrado: puede proporcionar implementaciones de los diversos subtipos (especificados a través de interfaces) de diferentes autores : siempre que los subtipos solo dependan de la interfaz especificada para la interacción entre ellos, este enfoque de mezclar y combinar funcionará.

Uno de los puntos fuertes de la mayoría de los lenguajes de programación funcionales son los tipos de datos parametrizados, donde crea una instancia de un tipo de día con otro como su parámetro (por ejemplo, una cola de enteros). Generics logra la misma flexibilidad en Java/C# (y plantillas en C++). Por supuesto, las implicaciones exactas y el poder expresivo pueden diferir entre los idiomas según su sistema de tipos.

Todo este debate es por separado Inyección de dependencia (DI), que intenta aflojar la fuerte dependencia entre una implementación concreta de un tipo y sus partes de apoyo proporcionando explícitamente las partes necesarias (en lugar de elegir la implementación), ya que el usuario del tipo podría tener una mejor comprensión de qué implementación de esas partes es la mejor para lograr su objetivo, por ejemplo, proporcionando implementaciones simuladas para probar la funcionalidad. El problema que DI intenta resolver es exclusivo de los lenguajes imperativos, también puede tener los mismos problemas de dependencia en los lenguajes funcionales: la implementación de un módulo abstracto puede optar por usar una implementación específica de subtipos (por lo que se acopla a sí mismo). a esas implementaciones) en lugar de tomar las implementaciones de subtipo como parámetros (que es lo que DI está buscando)

8

La comparación habitual es con módulos ML, donde los rasgos de Scala (o clases abstractas) desempeñan el papel de las firmas ML y sus implementaciones concretas (típicamente objetos Scala) desempeñan el papel de estructuras ML. La discusión de los módulos de ML here debe hacer que la conexión sea razonablemente clara.

El análogo entre Scala y ML es deliberado y si observa la fuente del compilador de Scala verá que a los objetos de Scala a menudo se hace referencia usando nombres que contienen "Módulo" como una parte.

+1

Por ejemplo, el nombre de la variable en el código de bytes de Java que hace referencia a una instancia de 'objeto' de Scala se llama 'MODULE $'. Muchas personas que trabajan con la reflexión podrían haber tropezado con eso. – Madoc

Cuestiones relacionadas