¿Qué debería tener prioridad: YAGNI o Good Design?

76

¿En qué punto debería YAGNI tener prioridad sobre las buenas prácticas de codificación y viceversa? Estoy trabajando en un proyecto en el trabajo y quiero introducir lentamente buenos estándares de código para mis compañeros de trabajo (actualmente no hay ninguno y todo está pirateado sin ton ni son), pero después de crear una serie de clases (nosotros no haga TDD, o lamentablemente ningún tipo de prueba de unidad) Di un paso atrás y pensé que está violando YAGNI porque sé con certeza que no necesitamos la necesidad de extender algunas de estas clases.

Aquí hay un ejemplo concreto de lo que quiero decir: tengo una capa de acceso a datos que envuelve un conjunto de procedimientos almacenados, que utiliza un patrón de estilo de repositorio rudimentario con funciones CRUD básicas. Como hay un puñado de métodos que necesitan todas mis clases de repositorio, creé una interfaz genérica para mis repositorios, llamada IRepository. Sin embargo, luego creé una interfaz "marcador" (es decir, una interfaz que no agrega ninguna funcionalidad nueva) para cada tipo de repositorio (por ejemplo ICustomerRepository) y la clase concreta lo implementa. He hecho lo mismo con una implementación de Fábrica para construir los objetos comerciales de DataReaders / DataSets devueltos por el Procedimiento almacenado; la firma de mi clase de repositorio tiende a parecerse a esto:

public class CustomerRepository : ICustomerRepository
{
    ICustomerFactory factory = null;

    public CustomerRepository() : this(new CustomerFactory() { }

    public CustomerRepository(ICustomerFactory factory) {
        this.factory = factory;
    }      

    public Customer Find(int customerID)
    {
        // data access stuff here
        return factory.Build(ds.Tables[0].Rows[0]);
    }
}

Mi preocupación aquí es que yo estoy violando YAGNI porque sé con 99% de certeza de que nunca va a ser una razón para dar más que un algo concreto CustomerFactorya este repositorio; Como no tenemos pruebas unitarias, no necesito una MockCustomerFactoryo cosas similares, y tener tantas interfaces podría confundir a mis compañeros de trabajo. Por otro lado, usar una implementación concreta de la fábrica parece un olor a diseño.

¿Hay una buena manera de llegar a un compromiso entre el diseño de software adecuado y no sobreasignar la solución? Me pregunto si necesito tener todas las "interfaces de implementación única" o si podría sacrificar un poco de buen diseño y solo tener, por ejemplo, la interfaz base y luego el concreto único, y no preocuparme por programar interfaz si la implementación es que alguna vez se utilizará.

Wayne Molina
fuente
17
Usted dice "como no tenemos pruebas unitarias, no necesito MockX", lo que naturalmente lleva a "No necesito IX, solo necesito X". Lo cambio, el hecho de que no tienes pruebas unitarias resalta el hecho de que necesitas IX y MockX, porque estas cosas te ayudarán a tener pruebas unitarias. No acepte la realidad de no tener pruebas, trátelo como un problema temporal que se solucionará durante (probablemente un buen, largo) tiempo.
Anthony Pegram
10
Aunque es trivial buscarlo en Google, alguien debería mencionar que YAGNI significa "No lo vas a necesitar"
thedaian
1
Creo que si estuviera escribiendo nuevas clases como esta, le gustaría agregar algunas pruebas unitarias. Incluso si tus compañeros de trabajo no los ejecutan. Al menos más tarde puedes decir: "¡Mira! ¡Mis pruebas unitarias lo detectaron cuando descifraste mi código! ¡Mira lo geniales que son las pruebas unitarias!" En ese caso, hacer que sea simulable podría valer la pena aquí. (Aunque, preferiría que el objeto pudiera ser burlado sin definir una interfaz)
Winston Ewert
¿Las pruebas unitarias no me obligarían a crear (o usar) un marco simulado para no acceder a los procedimientos almacenados en vivo? Esa es la razón principal por la que tiendo a no agregar pruebas: cada uno de nosotros tiene una copia local de la base de datos de producción con la que probamos y escribimos código.
Wayne Molina
3
@Anthony ¿Y la burla siempre justifica la sobrecarga de complejidad adicional que conlleva? Burlarse es una buena herramienta, pero su utilidad también debe sopesarse con los costos y, a veces, la balanza se inclina hacia el otro lado por un exceso de indirección. Claro, hay herramientas para ayudar con la complejidad adicional, pero no harán que la complejidad desaparezca. Parece que hay una tendencia creciente de tratar las "pruebas a toda costa" como un hecho. Creo que esto es falso.
Konrad Rudolph

Respuestas:

75

¿Hay una buena manera de llegar a un compromiso entre el diseño de software adecuado y no sobreasignar la solución?

YAGNI

Podría sacrificar un poco de buen diseño

Suposición falsa.

y la interfaz base y luego el concreto simple,

Eso no es un "sacrificio". Ese es un buen diseño.

S.Lott
fuente
72
La perfección se logra, no cuando no hay nada más que agregar, sino cuando no hay nada más que quitar. Antoine De St-Exupery
Newtopian
55
@Newtopian: las personas tienen sentimientos encontrados acerca de eso con respecto a los últimos productos de Apple. :)
Roy Tinker
14
Tengo un gran problema con este tipo de respuestas. ¿Es "X es verdadero porque Y lo dice y Y está bien respaldado por la comunidad" razonamiento válido? Si alguien en la vida real despotricara contra YAGNI, ¿le mostrarías esta respuesta como argumento?
vemv
2
@vemv. Nadie aquí está despotricando contra YAGNI. Todo lo que S.Lott dijo fue que la contradicción percibida por el OP entre dos objetivos dignos es un error. Por cierto, ¿conoces a algún desarrollador activo en el mundo del software actual que se quejara contra YAGNI? Si está trabajando en proyectos reales, sabe que los requisitos cambian constantemente y que los usuarios y gerentes generalmente no saben lo que quieren o no quieren hasta que se los ponga delante. Implementar lo necesario es MUCHO trabajo: ¿por qué tomar tiempo, energía y desperdiciar dinero (o arriesgar su trabajo) escribiendo un código que intente predecir el futuro?
Vector
66
Mi punto no es sobre YAGNI, sino sobre la calidad de la respuesta. No diría que alguien en particular estaba despotricando, fue parte de mi razonamiento. Por favor léelo de nuevo.
vemv
74

En la mayoría de los casos, evitar el código que no va a necesitar conduce a un mejor diseño. El diseño más fácil de mantener y a prueba de futuro es el que utiliza la menor cantidad de código simple y bien nombrado que satisface los requisitos.

Los diseños más simples son los más fáciles de evolucionar. Nada mata la mantenibilidad como las capas de abstracción inutilizadas y sobredimensionadas.

Michael Borgwardt
fuente
8
Encuentro 'evitar el código YAGNI' inquietantemente ambiguo. Podría significar un código que no va a necesitar o un código que se adhiera al principio YAGNI . (Cf. 'Código KISS')
sehe
2
@sehe: no es ambiguo en absoluto (mientras que "adherirse al principio YAGNI" es totalmente contradictorio) si lo deletrea: ¿qué podría significar "código de" no lo vas a necesitar "? no vas a necesitar
Michael Borgwardt
1
Voy a imprimir esta respuesta en una fuente grande y colgarla donde todos los astronautas de la arquitectura puedan leerla. +1!
kirk.burleson
1
+1. Todavía puedo recordar mi primer proyecto corporativo que me dieron para apoyar y agregar algunas características nuevas. Lo primero que hice fue eliminar 32,000 líneas de código inútil de un programa de 40,000 líneas sin perder ninguna funcionalidad. El programador original fue despedido poco después.
EJ Brennan
44
@vemv: lea "inútil" como "no se usa actualmente, excepto trivialmente", es decir, un caso de YAGNI. Y el "exceso de ingeniería" es bastante más específico que "malo". Específicamente significa "complejidad causada por conceptos teóricos o posibles requisitos imaginados, en lugar de requisitos actuales concretos".
Michael Borgwardt
62

YAGNI y SOLID (o cualquier otra metodología de diseño) no son mutuamente excluyentes. Sin embargo, son opuestos casi polares. No tiene que adherirse al 100% a ninguno de los dos, pero habrá algo de toma y daca; cuanto más miras un patrón altamente abstraído utilizado por una clase en un lugar, y dices YAGNI y lo simplificas, menos SÓLIDO se vuelve el diseño. Lo contrario también puede ser cierto; muchas veces en desarrollo, un diseño se implementa SOLIDAMENTE "por fe"; no ves cómo lo necesitarás, pero solo tienes una corazonada. Esto podría ser cierto (y es cada vez más probable que sea cierto cuanto más experiencia gane), pero también podría ponerle tanta deuda técnica como un enfoque de "hazlo ligero"; en lugar de una base de código de "código de espagueti" DIL, puede terminar con "código de lasaña", tener tantas capas que simplemente agregar un método o un nuevo campo de datos se convierte en un proceso de varios días de vadeo a través de servidores proxy de servicio y dependencias poco acopladas con una sola implementación. O podría terminar con el "código de ravioles", que viene en trozos tan pequeños que al moverse hacia arriba, hacia abajo, hacia la izquierda o hacia la derecha en la arquitectura lo lleva a través de 50 métodos con 3 líneas cada uno.

Lo he dicho en otras respuestas, pero aquí está: en la primera pasada, haz que funcione. En el segundo pase, que sea elegante. En el tercer pase, hazlo SÓLIDO.

Desglosando eso:

Cuando escribe una línea de código por primera vez, simplemente tiene que funcionar. En este punto, por lo que sabes, es único. Por lo tanto, no obtienes puntos de estilo para construir una arquitectura de "torre de marfil" para agregar 2 y 2. Haz lo que tienes que hacer y asume que nunca lo volverás a ver.

La próxima vez que su cursor vaya en esa línea de código, ahora ha refutado su hipótesis desde la primera vez que la escribió. Está volviendo a visitar ese código, probablemente ya sea para extenderlo o para usarlo en otro lugar, por lo que no es único. Ahora, se deben implementar algunos principios básicos como DRY (no se repita) y otras reglas simples para el diseño de código; extraiga métodos y / o bucles de forma para código repetido, extraiga variables para literales o expresiones comunes, tal vez agregue algunos comentarios, pero en general su código debe autodocumentarse. Ahora, su código está bien organizado, aunque todavía esté estrechamente acoplado, y cualquiera que lo vea puede aprender fácilmente lo que está haciendo leyendo el código, en lugar de rastrearlo línea por línea.

La tercera vez que el cursor ingresa ese código, probablemente sea un gran problema; lo está ampliando una vez más o se ha vuelto útil en al menos otros tres lugares diferentes en la base de código. En este punto, es un elemento clave, si no el núcleo, de su sistema, y ​​debe ser diseñado como tal. En este punto, generalmente también tiene el conocimiento de cómo se ha utilizado hasta ahora, lo que le permitirá tomar buenas decisiones de diseño con respecto a cómo diseñar el diseño para simplificar esos usos y los nuevos. Ahora las reglas SÓLIDAS deberían entrar en la ecuación; extraiga clases que contengan código con propósitos específicos, defina interfaces comunes para cualquier clase que tenga propósitos o funcionalidades similares, configure dependencias sueltas entre clases y diseñe las dependencias de manera que pueda agregarlas, eliminarlas o intercambiarlas fácilmente.

A partir de este momento, si necesita ampliar, volver a implementar o reutilizar este código, todo está muy bien empaquetado y resumido en el formato de "recuadro negro" que todos conocemos y amamos; conéctelo donde más lo necesite o agregue una nueva variación en el tema como una nueva implementación de la interfaz sin tener que cambiar el uso de dicha interfaz.

KeithS
fuente
Secundo la genialidad.
Filip Dupanović
2
Si. Cuando solía diseñar por adelantado para reutilizar / ampliar, encontraba que cuando quería reutilizarlo o extenderlo, sería de una manera diferente a la que había previsto. La predicción es difícil, especialmente en lo que respecta al futuro. Así que apoyo su regla de 3 golpes: para ese momento, tiene una idea razonable de cómo se reutilizará / extenderá. NB: una excepción es si ya sabe cómo será (por ejemplo, de proyectos anteriores, conocimiento del dominio o ya especificado).
13ren
En el cuarto pase, hazlo IMPRESIONANTE.
Richard Neil Ilagan
@KeithS: Parece que John Carmack hizo algo similar: "código fuente de Quake II ... unifique Quake 1, Quake World y QuakeGL en una hermosa arquitectura de código". fabiensanglard.net/quake2/index.php
13ren
+1 Creo que nombraré tu regla: "Refactorización práctica" :)
Songo
31

En lugar de cualquiera de esos, prefiero WTSTWCDTUAWCROT?

(¿Qué es lo más simple que podemos hacer que es útil y podemos lanzar el jueves?)

Acrónimos más simples están en mi lista de cosas que hacer, pero no son una prioridad.

Mike Sherrill 'Cat Recall'
fuente
8
Ese acrónimo viola el principio YAGNI :)
riwalk
2
Usé exactamente las letras que necesitaba, ni más ni menos. De esa manera, soy como Mozart. Sí. Soy el Mozart de las siglas.
Mike Sherrill 'Cat Recall'
44
Nunca supe que Mozart era terrible haciendo acrónimos. Aprendo algo nuevo todos los días en un sitio de SE. : P
Cameron MacFarland
3
@Mike Sherrill 'CatRecall': Tal vez uno debería extender eso a WTSTWCDTUWCROTAWBOF = "¿Cuál es la cosa más simple que podemos hacer que sea útil, podemos lanzar el jueves y no saldremos el viernes?" ;-)
Giorgio
1
@ Stargazer712 no :) viola POLA.
v.oddou
25

YAGNI y el buen diseño no son contradictorios. YAGNI trata sobre (no) apoyar necesidades futuras. Un buen diseño consiste en hacer transparente lo que hace su software en este momento y cómo lo hace.

¿Introducir una fábrica simplificará su código existente? Si no, no lo agregue. Si lo hace, por ejemplo, cuando agrega pruebas (¡lo que debe hacer!), Agréguelo.

YAGNI se trata de no agregar complejidad para admitir funciones futuras.
Un buen diseño consiste en eliminar la complejidad mientras se admiten todas las funciones actuales.

Jaap
fuente
15

No están en conflicto, tus objetivos están equivocados.

¿Qué intentas lograr?

Desea escribir software de calidad y, para hacerlo, desea mantener su base de código pequeña y no tener problemas.

Ahora que llegamos a un conflicto, ¿cómo cubrimos todos los casos si no escribimos casos que no vamos a utilizar?

Así es como se ve tu problema.

ingrese la descripción de la imagen aquí (cualquiera interesado, esto se llama nubes de evaporación )

Entonces, ¿qué está impulsando esto?

  1. No sabes lo que no vas a necesitar
  2. No quieres perder el tiempo e inflar tu código

¿Cuál de estos podemos resolver? Bueno, parece que no quiere perder el tiempo y el código hinchado es un gran objetivo, y tiene sentido. ¿Qué hay de ese primero? ¿Podemos averiguar qué vamos a necesitar para codificar?

Estoy trabajando en un proyecto en el trabajo y quiero introducir lentamente buenos estándares de código para mis compañeros de trabajo (actualmente no hay ninguno y todo está pirateado sin ton ni son) [...] di un paso atrás y pensé que está violando YAGNI porque sé con certeza que no necesitamos la necesidad de extender algunas de estas clases.

Reformulemos todo eso

  • No hay estándares de código
  • No hay planificación de proyectos en curso
  • Vaqueros en todas partes haciendo su propia maldita cosa (y estás tratando de jugar al sheriff en el salvaje oeste salvaje), sí, sí.

¿Hay una buena manera de llegar a un compromiso entre el diseño de software adecuado y no sobreasignar la solución?

No necesita un compromiso, necesita a alguien para administrar el equipo que sea competente y tenga una visión de todo el proyecto. Necesitas a alguien que pueda planificar lo que vas a necesitar, en lugar de que cada uno de ustedes arroje cosas que NO vas a necesitar porque no estás seguro del futuro porque ... ¿por qué? Te diré por qué, es porque nadie tiene un maldito plan entre todos ustedes. Estás intentando incorporar estándares de código para solucionar un problema completamente diferente. Su problema PRIMARIO que necesita resolver es una hoja de ruta y un proyecto claros. Una vez que tenga eso, puede decir "los estándares de código nos ayudan a alcanzar este objetivo de manera más efectiva como equipo", lo cual es la verdad absoluta pero está fuera del alcance de esta pregunta.

Obtenga un gerente de proyecto / equipo que pueda hacer estas cosas. Si tiene uno, debe pedirles un mapa y explicarles el problema de YAGNI que no presenta un mapa. Si son extremadamente incompetentes, escriba el plan usted mismo y diga "aquí está mi informe para usted sobre las cosas que necesitamos, revíselo y háganos saber su decisión".

Incógnito
fuente
No tenemos uno, lamentablemente. El Gerente de desarrollo está más preocupado por hacer las cosas rápidamente que por los estándares / calidad, o haría que las prácticas estándar como el uso de DataSets sin procesar en todas partes se devuelvan directamente de las clases, la codificación de estilo VB6 y todo en código subyacente con lógica duplicada copiada y pegada. terminado.
Wayne Molina
Está bien, va a ser un poco más lento, pero entonces debes hablar sobre sus preocupaciones. Explique que este problema de YAGNI / código inútil está perdiendo el tiempo, sugiera la hoja de ruta, dele una hoja de ruta, explíquele que hará que el trabajo sea más rápido. Cuando haya comprado, entérese de los problemas que tienen los malos estándares para la velocidad del desarrollo, sugiera los mejores. Quieren que se realice el proyecto, simplemente no saben cómo manejarlo, o bien deberán reemplazarlo o cuidarlo ... o renunciar.
Incognito
Erm ..... Creo que el objetivo principal sería '¿quieres tener un producto bueno y valioso'?
sehe
@sehe esa no es su decisión: p.
Incognito
3
Gran respuesta. Se enfrenta a una batalla cuesta arriba. Hacerlo será difícil si no hay una aceptación de la gerencia. Y tiene razón, esa pregunta es prematura y no aborda el problema real.
Seth Spearman
10

Permitir que su código para ser extendido con pruebas de unidad no va a ser cubiertos por YAGNI, porque usted va a necesitar. Sin embargo, no estoy convencido de que sus cambios de diseño en una interfaz de implementación única realmente estén aumentando la capacidad de prueba del código porque CustomerFactory ya hereda de una interfaz y de todos modos puede cambiarse por una MockCustomerFactory en cualquier momento.

DeadMG
fuente
1
+1 para comentarios útiles sobre el problema real en lugar de solo sobre la batalla cósmica y / o el amor entre Good Design y YAGNI.
psr
10

La pregunta presenta un falso dilema. La aplicación adecuada del principio YAGNI no es algo sin relación. Es un aspecto del buen diseño. Cada uno de los principios SÓLIDOS también son aspectos del buen diseño. No siempre se puede aplicar completamente todos los principios en cualquier disciplina. Los problemas del mundo real ejercen muchas fuerzas en su código, y algunos de ellos empujan en direcciones opuestas. Los principios de diseño tienen que dar cuenta de todos ellos, pero ningún puñado de principios puede adaptarse a todas las situaciones.

Ahora, echemos un vistazo a cada principio con el entendimiento de que, si bien a veces pueden tomar diferentes direcciones, de ninguna manera están inherentemente en conflicto.

YAGNI fue concebido para ayudar a los desarrolladores a evitar un tipo particular de retrabajo: lo que viene de construir algo incorrecto. Lo hace al guiarnos para evitar tomar decisiones errantes demasiado pronto en base a suposiciones o predicciones sobre lo que creemos que cambiará o será necesario en el futuro. La experiencia colectiva nos dice que cuando hacemos esto, generalmente estamos equivocados. Por ejemplo, YAGNI le diría que no cree una interfaz con el propósito de reutilización , a menos que sepa en este momento que necesita múltiples implementadores. De manera similar, YAGNI diría que no cree un "ScreenManager" para administrar el formulario único en una aplicación a menos que sepa en este momento que tendrá más de una pantalla.

Al contrario de lo que mucha gente piensa, SOLID no se trata de reutilización, genérico o incluso abstracción. SOLID tiene la intención de ayudarlo a escribir código que esté preparado para el cambio , sin decir nada sobre cuál podría ser ese cambio específico. Los cinco principios de SOLID crean una estrategia para construir código que es flexible sin ser demasiado genérico y simple sin ser ingenuo. La aplicación adecuada del código SOLID produce clases pequeñas y enfocadas con roles y límites bien definidos. El resultado práctico es que para cualquier cambio de requisitos necesarios, se debe tocar un número mínimo de clases. Y de manera similar, para cualquier cambio de código, hay una cantidad minimizada de "ondulación" a través de otras clases.

Mirando la situación de ejemplo que tiene, veamos qué podrían decir YAGNI y SOLID. Está considerando una interfaz de repositorio común debido al hecho de que todos los repositorios se ven iguales desde el exterior. Pero el valor de una interfaz genérica común es la capacidad de usar cualquiera de los implementadores sin necesidad de saber cuál es en particular. A menos que haya algún lugar en su aplicación donde esto sea necesario o útil, YAGNI dice que no lo haga.

Hay 5 principios SÓLIDOS para ver. S es responsabilidad única. Esto no dice nada sobre la interfaz, pero podría decir algo sobre sus clases concretas. Se podría argumentar que manejar el acceso a los datos en sí mismo podría ser una responsabilidad de una o más clases, mientras que la responsabilidad de los repositorios es traducir de un contexto implícito (CustomerRepository es un repositorio implícito para las entidades del Cliente) en llamadas explícitas a la API de acceso a datos generalizada que especifica el tipo de entidad del Cliente.

O es abierto-cerrado. Esto se trata principalmente de herencia. Se aplicaría si intentara derivar sus repositorios de una base común que implementa una funcionalidad común, o si espera derivar más de los diferentes repositorios. Pero no lo eres, así que no.

L es la sustituibilidad de Liskov. Esto se aplica si tenía la intención de utilizar los repositorios a través de la interfaz de repositorio común. Establece restricciones en la interfaz y las implementaciones para garantizar la coherencia y evitar un manejo especial para diferentes impelementers. La razón de esto es que tal manejo especial socava el propósito de una interfaz. Puede ser útil considerar este principio, porque puede advertirle que no use la interfaz de repositorio común. Esto coincide con la guía de YAGNI.

I es la segregación de interfaz. Esto puede aplicarse si comienza a agregar diferentes operaciones de consulta a sus repositorios. La segregación de interfaz se aplica cuando puede dividir a los miembros de una clase en dos subconjuntos donde uno será utilizado por ciertos consumidores y el otro por otros, pero es probable que ningún consumidor use ambos subconjuntos. La guía es crear dos interfaces separadas, en lugar de una común. En su caso, es poco probable que la búsqueda y el almacenamiento de instancias individuales se consuman con el mismo código que haría consultas generales, por lo que podría ser útil separarlos en dos interfaces.

D es inyección de dependencia. Aquí volvemos al mismo punto que la S. Si separó su consumo de la API de acceso a datos en un objeto separado, este principio dice que, en lugar de simplemente actualizar una instancia de ese objeto, debe pasarlo cuando cree Un repositorio. Esto facilita el control de la vida útil del componente de acceso a datos, abriendo la posibilidad de compartir referencias a él entre sus repositorios, sin tener que seguir la ruta de convertirlo en un singleton.

Es importante tener en cuenta que la mayoría de los principios SOLID no se aplican necesariamente en esta etapa particular del desarrollo de su aplicación. Por ejemplo, si debe romper el acceso a los datos depende de lo complicado que sea, y si desea probar la lógica de su repositorio sin tocar la base de datos. Parece que esto es poco probable (desafortunadamente, en mi opinión), por lo que probablemente no sea necesario.

Entonces, después de toda esa consideración, descubrimos que YAGNI y SOLID realmente proporcionan una pieza común de consejos sólidos e inmediatamente relevantes: probablemente no sea necesario crear una interfaz de repositorio genérica común.

Todo este pensamiento cuidadoso es extremadamente útil como ejercicio de aprendizaje. A medida que aprende, lleva mucho tiempo, pero con el tiempo desarrolla la intuición y se vuelve muy rápido. Sabrá qué hacer, pero no necesita pensar todas estas palabras a menos que alguien le pida que explique por qué.

Chris Ammerman
fuente
Creo que la mayor parte de la discusión en esta página deja fuera a 2 grandes contendientes "bajo acoplamiento" y "alta cohesión" de los Principios GRAS. Las decisiones de diseño más costosas se derivan del principio de "bajo acoplamiento". Como cuándo "activar" SRP + ISP + DIP en aras del bajo acoplamiento. Ejemplo: una clase -> 3 clases en patrón MVC. O incluso más caro: dividido en módulos / ensamblados .dll / .so. Eso es muy costoso debido a las implicaciones de compilación, proyectos, makelists, servidor de compilación, adiciones de archivos con
versión de origen
6

Parece creer que "buen diseño" significa seguir algún tipo de idealología y un conjunto formal de reglas que siempre deben aplicarse, incluso cuando no sirven.

OMI que es un mal diseño. YAGNI es un componente del buen diseño, nunca una contradicción.

Vector
fuente
2

En su ejemplo, diría que YAGNI debería prevalecer. No le costará tanto si necesita agregar interfaces más adelante. Por cierto, ¿es realmente un buen diseño tener una interfaz por clase si no sirve para nada?

Un pensamiento más, quizás a veces lo que necesita no es un buen diseño sino un diseño suficiente. Aquí hay una secuencia muy interesante de publicaciones sobre el tema:

David
fuente
Tus enlaces primero y tercero van al mismo lugar.
David Thornley
2

Algunas personas argumentan que los nombres de las interfaces no deberían comenzar por I. Específicamente, una de las razones es que usted está filtrando la dependencia de si el tipo dado es una clase o una interfaz.

¿Qué le impide CustomerFactoryser una clase al principio y luego cambiarla a una interfaz, que será implementada por DefaultCustormerFactoryo UberMegaHappyCustomerPowerFactory3000? Lo único que debería tener que cambiar es el lugar, donde se implementa la implementación. Y si tiene un diseño menos bueno, este es un puñado de lugares como máximo.

La refactorización es parte del desarrollo. Es mejor tener un código pequeño, que sea fácil de refactorizar, que tener una interfaz y una clase declaradas para cada clase, lo que le obliga a cambiar cada nombre de método en al menos dos lugares al mismo tiempo.

El punto real en el uso de interfaces es lograr la modularidad, que es posiblemente el pilar más importante del buen diseño. Sin embargo, tenga en cuenta que un módulo no solo se define por su desacoplamiento del mundo exterior (aunque así es como lo percibimos desde la perspectiva exterior), sino también por su funcionamiento interno.
Lo que quiero decir es que las cosas desacopladas, que intrínsecamente van juntas, no tienen mucho sentido. En cierto modo, es como tener un armario con un estante individual por taza.

La importancia radica en idear un problema grande y complejo en subproblemas más pequeños y simples. Y debe detenerse en el punto, donde se vuelven lo suficientemente simples sin subdivisiones adicionales, de lo contrario, se volverán más complicados. Esto podría verse como un corolario de YAGNI. Y definitivamente significa buen diseño.

El objetivo no es resolver de alguna manera un problema local con un único repositorio y una sola fábrica. El objetivo es que esta decisión no tenga efecto en el resto de su aplicación. De eso se trata la modularidad.
Desea que sus compañeros de trabajo vean su módulo, vean una fachada con un puñado de llamadas autoexplicativas y se sientan seguros de que pueden usarlos, sin tener que preocuparse por todas las tuberías internas potencialmente sofisticadas.

back2dos
fuente
0

Estás creando una interfaceanticipación de múltiples implementaciones en el futuro. También podría tener un I<class>para cada clase en su código base. No lo hagas

Solo use la clase de concreto individual de acuerdo con YAGNI. Si encuentra que necesita tener un objeto "simulado" para fines de prueba, convierta la clase original en una clase abstracta con dos implementaciones, una con la clase concreta original y la otra con la implementación simulada.

Obviamente, tendrá que actualizar todas las instancias de la clase original para crear una instancia de la nueva clase concreta. Puede evitar eso utilizando un constructor estático en la delantera.

YAGNI dice que no escriba código antes de que deba escribirse.

Un buen diseño dice que use la abstracción.

Puedes tener ambos. Una clase es una abstracción.

Jesse
fuente
0

¿Por qué las interfaces del marcador? Me parece que no está haciendo nada más que "etiquetar". Con una "etiqueta" diferente para cada tipo de fábrica, ¿cuál es el punto?

El propósito de una interfaz es dar a una clase "actos como" comportamiento, para darles "capacidad de depósito", por así decirlo. Entonces, si todos sus tipos de repositorio concretos se comportan como el mismo IRepository (todos implementan IRepository), entonces todos pueden ser manejados de la misma manera por otro código, exactamente por el mismo código. En este punto, su diseño es extensible. Añadiendo más tipos de repositorios concretos, todos manejados como repositorios IR genéricos: el mismo código maneja todos los tipos concretos como repositorios "genéricos".

Las interfaces son para manejar cosas basadas en elementos comunes. Pero las interfaces de marcadores personalizados a) no agregan ningún comportamiento. yb) obligarlo a manejar su singularidad.

En la medida en que diseñe interfaces útiles, obtendrá la bondad OO de no tener que escribir código especializado para manejar clases especializadas, tipos o interfaces de marcador personalizadas que tengan una correlación 1: 1 con clases concretas. Eso es redundancia sin sentido.

De repente, puedo ver una interfaz de marcador si, por ejemplo, necesita una colección fuertemente tipada de muchas clases diferentes. En la colección, todos son "ImarkerInterface", pero a medida que los saca, tiene que convertirlos en sus tipos adecuados.

radarbob
fuente
0

¿Puede, ahora mismo, escribir un ICustomerRepository vagamente razonable? Por ejemplo, (¿realmente llegando aquí, probablemente un mal ejemplo) en el pasado sus clientes siempre han usado PayPal? ¿O todos en la compañía están entusiasmados con la conexión con Alibaba? Si es así, es posible que desee utilizar el diseño más complejo ahora y que parezca clarividente a sus jefes. :-)

De lo contrario, espera. Adivinar en una interfaz antes de tener una o dos implementaciones reales generalmente falla. En otras palabras, no generalice / resuma / use un patrón de diseño elegante hasta que tenga un par de ejemplos para generalizar.

user949300
fuente