oop - sustitucion - ¿La coincidencia de patrones de Scala viola el principio abierto/cerrado?
principios solid (3)
La coincidencia de patrones definitivamente es buena si está haciendo una programación funcional. En el caso de OO, hay algunos casos en los que es bueno. En el ejemplo de Cedric en sí mismo, depende de cómo ve el método print()
conceptualmente. ¿Es un comportamiento de cada objeto de Term
? ¿O es algo fuera de eso? Yo diría que está afuera, y tiene sentido hacer una coincidencia de patrones. Por otro lado, si tiene una clase Employee
con varias subclases, es una opción de diseño deficiente hacer la coincidencia de patrones en un atributo de la misma (por ejemplo, nombre) en la clase base.
También la coincidencia de patrones ofrece una manera elegante de desempaquetar miembros de una clase.
Si agrego una nueva clase de caso, ¿eso significa que necesito buscar a través de todo el código de coincidencia de patrón y averiguar dónde se debe manejar la nueva clase? He estado aprendiendo el idioma recientemente, y cuando leí sobre algunos de los argumentos a favor y en contra de la coincidencia de patrones, he estado confundido acerca de dónde debería usarse. Vea lo siguiente:
Con: Beust
Los comentarios son bastante buenos en cada caso, también. Entonces, ¿el patrón coincide con algo para entusiasmar o algo que debería evitar usar? En realidad, me imagino que la respuesta es "depende de cuándo la usas", pero ¿cuáles son algunos casos de uso positivo y cuáles son algunos negativos?
Si bien respeto a Cedric, él está completamente equivocado sobre este tema. La coincidencia de patrones de Scala se puede encapsular por completo a partir de los cambios de clase cuando se desee. Si bien es cierto que un cambio en una clase de caso requeriría cambiar cualquier instancia de coincidencia de patrón correspondiente, esto es solo cuando se usan tales clases de una manera ingenua.
La coincidencia de patrones de Scala siempre delega al deconstructor del objeto complementario de una clase. Con una clase de caso, este deconstructor se genera automáticamente (junto con un método de fábrica en el objeto complementario), aunque aún es posible anular esta versión generada automáticamente. En todo momento, puede ejercer un control total sobre el proceso de coincidencia de patrones, aislando cualquier patrón de posibles cambios en la clase misma. Por lo tanto, la coincidencia de patrones es simplemente otra forma de acceder a los datos de clase a través del filtro seguro de encapsulación, como cualquier otro método.
Entonces, la opinión del Dr. Odersky sería en la que confiaría, particularmente dado el gran volumen de investigación que ha realizado en el área de programación y diseño orientado a objetos.
En cuanto a dónde debe usarse, eso es completamente de acuerdo con el gusto. Si hace que su código sea más conciso y fácil de mantener, úselo. De lo contrario, no. Para la mayoría de los programas orientados a objetos, la coincidencia de patrones es innecesaria. Sin embargo, una vez que comiences a integrar expresiones idiomáticas más funcionales ( Option
, List
, etc.), creo que encontrarás que la coincidencia de patrones reducirá significativamente los gastos indirectos sintácticos y mejorará la seguridad ofrecida por el sistema de tipos. En general, cada vez que desee extraer datos mientras prueba simultáneamente alguna condición (por ejemplo, extraer un valor de Some
), es probable que la coincidencia de patrones sea útil.
Jeff, creo que tienes la intuición correcta: depende.
Las jerarquías de clases orientadas a objetos con envío de métodos virtuales son buenas cuando tienes un conjunto relativamente fijo de métodos que deben implementarse, pero muchas subclases potenciales que pueden heredar de la raíz de la jerarquía e implementar esos métodos. En dicha configuración, es relativamente fácil agregar nuevas subclases (solo implementar todos los métodos), pero es relativamente difícil agregar nuevos métodos (debe modificar todas las subclases para asegurarse de que implementen correctamente el nuevo método).
Los tipos de datos con funcionalidad basada en la coincidencia de patrones son buenos cuando tiene un conjunto relativamente fijo de clases que pertenecen a un tipo de datos, pero muchas funciones potenciales que operan en ese tipo de datos. En dicha configuración, es relativamente fácil agregar nuevas funcionalidades para un tipo de datos (solo coincidencia de patrones en todas sus clases), pero es relativamente difícil agregar nuevas clases que forman parte del tipo de datos (debe modificar todas las funciones que coinciden) en el tipo de datos para asegurarse de que sean compatibles con la nueva clase).
El ejemplo canónico para el enfoque OO es la programación GUI. Los elementos de GUI deben admitir muy poca funcionalidad (dibujarse en la pantalla es el mínimo indispensable), pero se agregan nuevos elementos de GUI todo el tiempo (botones, tablas, gráficos, controles deslizantes, etc.). El ejemplo canónico para el enfoque de coincidencia de patrones es un compilador. Los lenguajes de programación generalmente tienen una sintaxis relativamente fija, por lo que los elementos del árbol de sintaxis cambian raramente (si es que lo hacen), pero se agregan constantemente nuevas operaciones en árboles de sintaxis (optimizaciones más rápidas, análisis de tipo más exhaustivos, etc.).
Afortunadamente, Scala te permite combinar ambos enfoques. Las clases de casos pueden combinarse con patrones y admitir el envío de métodos virtuales. Las clases regulares admiten el envío de métodos virtuales y se pueden combinar con el patrón definiendo un extractor en el objeto complementario correspondiente. Depende del programador decidir cuándo cada enfoque es apropiado, pero creo que ambos son útiles.