Buscando algunos consejos de diseño OO

12

Estoy desarrollando una aplicación que se usará para abrir y cerrar válvulas en un entorno industrial, y estaba pensando en algo simple como esto:

public static void ValveController
{
    public static void OpenValve(string valveName)
    {
        // Implementation to open the valve
    }

    public static void CloseValve(string valveName)
    {
        // Implementation to close the valve
    }
}

(La implementación escribiría unos pocos bytes de datos en el puerto serie para controlar la válvula: una "dirección" derivada del nombre de la válvula y un "1" o "0" para abrir o cerrar la válvula).

Otro desarrollador preguntó si deberíamos crear una clase separada para cada válvula física, de las cuales hay docenas. Estoy de acuerdo en que sería mejor escribir código en PlasmaValve.Open()lugar de ValveController.OpenValve("plasma"), pero ¿es excesivo?

Además, me preguntaba cuál sería la mejor manera de abordar el diseño teniendo en cuenta un par de requisitos hipotéticos futuros:

  1. Se nos pide que admitamos un nuevo tipo de válvula que requiere diferentes valores para abrirla y cerrarla (no 0 y 1).
  2. Se nos pide que apoyemos una válvula que se puede configurar en cualquier posición de 0 a 100, en lugar de simplemente "abrir" o "cerrar".

Normalmente usaría la herencia para este tipo de cosas, pero recientemente he empezado a entender la "composición sobre la herencia" y me pregunto si hay una solución más ingeniosa con la composición.

Andrew Stephens
fuente
2
Crearía una clase de válvula genérica que tenga un identificador para la válvula específica (no una cadena, quizás una enumeración) y cualquier información necesaria para controlar el flujo dentro de los métodos OpenValve / CloseValve. Alternativamente, puede hacer que la clase valv sea abstracta y realizar implementaciones separadas para cada una, donde la válvula de apertura / cierre solo llama a la lógica dentro de la clase de válvula dada para el caso de que diferentes válvulas tengan diferentes mecanismos de apertura / cierre. El mecanismo común se definiría en la clase base.
Jimmy Hoffa
2
No se preocupe por los requisitos hipotéticos futuros. YAGNI
pdr
3
@pdr YAGNI es una cuchilla de doble filo, estoy de acuerdo en que vale la pena seguirla en general, pero llevado al extremo se podría decir que hacer cualquier cosa para ayudar a la mantenibilidad o legibilidad futuras está violando YAGNI, debido a esto encuentro que el alcance de YAGNI es demasiado ambiguo para muchos. Dicho esto, muchas personas reconocen dónde usar YAGNI y dónde tirarlo, porque tener en cuenta el futuro le ahorrará un gran dolor. Solo creo que uno debe tener cuidado al sugerir que la gente siga a YAGNI cuando no sabe dónde aterrizarán en ese espectro.
Jimmy Hoffa
2
Hombre, 'composición sobre inercia' está sobrevalorada. Haría una clase / interfaz abstracta de Valve y luego las subclase en PlasmaValve. Y luego me aseguraría de que mi ValveController funcione con Valve (s), sin importar qué subclase sean exactamente.
MrFox
2
@suslik: absolutamente. También he visto un excelente código llamado spaghetti por personas que no entienden los principios de SOLID. Podríamos continuar para siempre con esto. Mi punto es que he visto más problemas causados ​​por descartar los principios establecidos (nacidos de la experiencia de muchos años) de las que he visto causados ​​por el exceso de adherencia. Pero estoy de acuerdo en que ambos extremos son peligrosos.
pdr

Respuestas:

12

Si cada instancia del objeto de válvula ejecutara el mismo código que este ValveController, entonces parece que múltiples instancias de una sola clase serían el camino correcto. En este caso, simplemente configure qué válvula controla (y cómo) en el constructor del objeto de la válvula.

Sin embargo, si cada control de válvula necesita un código diferente para ejecutarse, y el ValveController actual está ejecutando una declaración de interruptor gigante que hace cosas diferentes según el tipo de válvula, entonces ha implementado mal el polimorfismo. En ese caso, vuelva a escribirlo en varias clases con una base común (si eso tiene sentido) y deje que el principio de responsabilidad única sea su guía de diseño.

metal de piedra
fuente
1
+1 por mencionar declaraciones de cambio basadas en tipo como olor de código. Frecuentemente veo este tipo de declaraciones de cambio donde el desarrollador afirma que solo estaba siguiendo a KISS. Ejemplo perfecto de cómo se pueden pervertir los principios de diseño je
Jimmy Hoffa
2
Varias instancias también podrían facilitar el enlace de las válvulas en una secuencia, permitiéndole modelar la tubería de la planta real como un gráfico dirigido en su código. También puede agregar lógica de negocios a las clases, en caso de que necesite hacer algo como abrir una válvula cuando se cierra otra para evitar la acumulación de presión, o cerrar todas las válvulas aguas abajo para que no obtenga un efecto de "golpe de ariete" cuando la válvula se abre nuevamente.
TMN
1

Mi mayor queja es usar cadenas para el parámetro que identifica la válvula.

Al menos, cree una Valveclase que tenga getAddressla forma que necesita la implementación subyacente y páselos al ValveControllery asegúrese de que no puede crear válvulas inexistentes. De esta manera, no tendrá que manejar cadenas incorrectas en cada uno de los métodos de apertura y cierre.

Si usted crea métodos convenientes que llaman a abrir y cerrar ValveControllerdepende de usted, pero para ser sincero, mantendría toda la comunicación con el puerto serie (incluida la codificación) en una sola clase a la que otras clases llamarán cuando sea necesario. Esto significa que cuando necesita migrar a un nuevo controlador, solo necesita modificar una clase.

Si le gusta la prueba, también debe hacer ValveControllerun singleton para poder burlarse de él (o crear una máquina de entrenamiento para los operadores).

monstruo de trinquete
fuente
Nunca he visto a nadie recomendar un singleton para probar antes, por lo general, va en sentido contrario.
Kazark
honestamente el singleton es más para evitar la estática y por lo que la comunicación puede ser sincronizado
trinquete monstruo