¿Cuál es el patrón más utilizado para administrar una gran cantidad de parámetros interconectados?

7

Recientemente comencé a trabajar en una aplicación que controla diferentes dispositivos de medición.

Antes de que el usuario comience una medida, ella establece sus parámetros.

En realidad, considerando todos los tipos de mediciones, hay más de 50 parámetros.

La dificultad aquí es que cada configuración depende de otras para:

  • Estar disponible
  • Lista de valores disponibles
  • y así sucesivamente ..

Además, algunas configuraciones de medición dependen de los resultados y configuraciones de mediciones anteriores.

Para abreviar: tenemos muchas cosas que están interconectadas.

El patrón real es validar todo tan pronto como se cambie un valor. Cuesta mucho (a tiempo) y vamos a agregar muchos más parámetros: se romperá.

Intentamos implementar un patrón donde usamos ObservableValues y donde todos los parámetros se registran en todos los valores de los que dependen.

Se volvió difícil cuando los parámetros dependen de otra medida de referencia. Si la referencia cambia, debemos dejar de escuchar en la referencia anterior y comenzar a escuchar en la nueva referencia.

Etc ...

Otro problema es que cuando trabajamos en nuestro patrón y tenemos más capacidades (como la serialización), o cuando tenemos alguna clase de helper (como fábrica), construimos archivos grandes con más de 50 parámetros o funciones.

¿Hay algún otro buen patrón o biblioteca para hacerlo?

    
pregunta Orace 11.06.2015 - 09:33

6 respuestas

1

sugeriría:

  • divide y vencerás el problema
  • use componentes y mantenga cada componente enfocado en su propósito principal
  • desacopla su sistema mediante el uso de un agregador de eventos (EA)
  • usa interfaces donde un EA no tiene sentido

Intentaría dividir el gran problema en problemas más pequeños y luego tratar de resolverlo por sí mismo. Los problemas más pequeños se pueden resolver más fácilmente con componentes especializados, si son lo suficientemente simples. Cada componente debe mantener su enfoque en su propósito principal.

  • Para mantener un acoplamiento suelto alrededor de los componentes, usaría un agregador de eventos (EA) (propósito principal: notificar a los oyentes). (por ejemplo, Extensiones reactivas, Caliburn Micro o el Agregador de eventos Prism, si está en .NET)
  • Usaría clases de parámetros simples para mantener los valores (propósito principal: manejar valores).
  • Agruparía los parámetros en algún tipo de clase de árbol (propósito principal: proporcionar parámetros).
  • El árbol debe ser construido o actualizado. Para esto usaría un componente generador de árbol (propósito principal: construir / actualizar árbol).
  • El creador del árbol debe ser notificado cuando debe reaccionar - > Notificación de EA
  • Algunos consumidores del árbol deben ser notificados después de que el árbol haya cambiado - > Notificación de EA
  • ...

Si los componentes no están bien acoplados, entonces el sistema puede escalarse o cambiarse cuando sea necesario. Es decir: si el proceso de construcción es demasiado complejo, entonces es posible usar varias clases de constructores de árboles en lugar de una. Para lograr esto, se agregaría un componente adicional, una fábrica de constructores de árboles. Reaccionaría ante los eventos de construcción / actualización del árbol al proporcionar el generador de árbol adecuado.

    
respondido por el al-bex 16.06.2015 - 13:10
3

Cree una clase de Parámetro (suponiendo que C #): class Parameter<T> { T Value; string Name; }

Luego cree un gráfico con nodos que contengan valores de parámetros y dependencias como sus referencias en el mismo gráfico.

Y a continuación, realice todas las operaciones en el gráfico en su conjunto, no directamente en los parámetros: %código% o graph.SetValue(parameter, value)

    
respondido por el Roland 15.06.2015 - 11:38
1

Podría considerar usar un mediador para desacoplar las dependencias de eventos de parámetros. Esto a veces se conoce como un Agregador de eventos . Sus clases de parámetros individuales están conectadas para publicar y suscribirse a eventos directamente a través del EA.

El siguiente ejemplo utiliza Extensiones reactivas pero puede implementar una versión simple utilizando devoluciones de llamada para suscripciones y publicaciones.

public class EventAggregator
{
    private Subject<Event> _events;
    public IObservable<Event> Events { get { return _events; } }

    public void Publish(Event evt)
    {
        _events.OnNext(evt);
    }
}

public class Parameter<T> : IDisposable
{
    private readonly EventAggregator _ea;
    public Parameter(EventAggregator ea, string name, T initialValue)
    {
        this._ea = ea;
        this._name = name;
        this._value = initialValue;
        this._subscription = ea.Events.Subscribe(evt => HandleEvent(evt));
    }

    private readonly string _name;
    public string Name { get { return this._name; } }

    private T _value;
    public T Value
    {
        get { return this._value; }
        set
        {
            this._value = value;
            this._ea.Publish(new ValueChangedEvent(this._name, this._value);
        }
    }

    private void HandleEvent(Event evt)
    {
        // decide what to do with an event here - change availability of the parameter etc
    }

    public void Dispose()
    {
        this._subscription.Dispose();
    }
}

Crea un solo EventAggregator y lo pasa como una dependencia a sus objetos Parameter :

var eventAggregator = new EventAggregator();
var length = new Parameter<int>(eventAggregator, "Length", 50);
var width = new Parameter<int>(eventAggregator, "Width", 50);

// this will fire an event to the event aggregator which can be handled by other parameters
width.Value = 20; 

// this unsubscribes from the event aggregator
length.Dispose(); 
    
respondido por el AlexFoxGill 15.06.2015 - 14:42
0

Hay herramientas para ayudarte a administrar la complejidad; vea por ejemplo el enfoque del Feature Model .

Sin embargo, si tuviera que hacerlo, intentaría un enfoque de programación de restricciones (los árboles de características son básicamente una notación simplificada de restricciones). Dado que está utilizando C #, le indicaré Prolog.NET . Sin embargo, tenga en cuenta que, aunque ya utilicé Prolog, no conozco Prolog.Net específicamente.

Nota: Editaré esta respuesta más adelante con un ejemplo, si es posible (ahora no tengo mucho tiempo). Por cierto, prefiero usar uno de su ejemplo, si pudiera proporcionar algo.

    
respondido por el coredump 15.06.2015 - 11:06
0

Una idea más:

Mantenga todos sus parámetros en un mapa grande como:

class Params
{
   Map<String, Object> map;

   /* fancy helper methods... */
}

Agrega un conjunto de objetos de regla:

interface Rule
{
  /* returns "OK" or an error message
   * or use exceptions or whatever you fancy */
  void validate(Params params); 

  /* returns the list of params that will trigger the rule */
  String[] triggers();
}

class FancyRule implements Rule
{
  String validate(Params params) {
     if( params.get("foo") <= params.get("bar") )
        return "foo should be greater than bar";
     else
        return "OK";
  }
  String[] triggers() {
     return ["foo", "bar"];
  }
}

Como último paso, solo necesitas unir las cosas:

class Params
{
   Map<String, Object> map;

   void setParam(key,value) {
      map.put(key,value); // + keep a backup if desired

      // now validate everything depending on it
      for( rule in rules having key as trigger):
           if( rule.validate(this) != "OK" )
                // ...
   }
}
    
respondido por el dagnelies 16.06.2015 - 14:02
0

Hay muchas maneras diferentes de abordar la actualización de dichos parámetros interconectados; Tengo experiencia previa con dos: a los efectos de esta respuesta, los llamaré enfoque "centralizado" y enfoque "descentralizado".

En el enfoque centralizado, tiene una función única de "Actualizar todo" que debe asegurarse de invocar cada vez que haga algo en todo su sistema que tenga la más mínima posibilidad de requerir una actualización. La ventaja del enfoque centralizado es que es simple: todo el código que implementa la actualización está en un solo lugar, por lo que puede leerlo para asegurarse de que sea correcto. Las desventajas del enfoque centralizado son que a) la función "Actualizar todo" por lo general termina siendo monstruosamente larga, b) realiza todas las actualizaciones cada vez que se invoca, aunque solo algunas cosas hayan cambiado y, por lo tanto, solo algunas es posible que las cosas necesiten ser actualizadas, yc) tienden a invocarse excesivamente, solo para reducir las posibilidades de perder una actualización, por lo que en general, representa una gran sobrecarga innecesaria.

El enfoque descentralizado es lo que ya ha dicho que ha comenzado a trabajar; es mucho más complicado y, en realidad, requiere la construcción de un marco para poder utilizarlo. En este enfoque, cada parámetro es capaz de desencadenar una notificación de "cambio", y cada parámetro tiene conocimiento de cada uno de los parámetros de los que depende, para que pueda registrarse con ellos para recibir una notificación cuando cambien, y así recompensar. su propio valor, posiblemente activando su propia notificación de "cambio" a su vez. El enfoque descentralizado resuelve todos los problemas del enfoque centralizado, pero es más elaborado y, por lo tanto, más costoso de implementar.

Algunos puntos a tener en cuenta:

  1. Deberá introducir y hacer uso principalmente de alguna interfaz "ReadonlyParameter", que tiene un "GetValue ()" y un "RegisterForChangedEvent ()", pero no "SetValue ()", porque no tiene tiene sentido establecer el valor de un parámetro que se construye para volver a calcular su propio valor basado en los valores de otros parámetros. "solo lectura" en este contexto no significa que su valor nunca cambie, solo significa que no puede cambiar su valor, aunque su valor todavía puede cambiar debido a otras razones, por lo que tiene sentido registrarse con él en orden para observar cualquier cambio de este tipo.

  2. Para aquellos parámetros que reciben valores del exterior, es posible que deba introducir una clase "StoringParameter" que "almacene" (contenga) su propio valor, implementando la interfaz "ReadonlyParameter" y también ofreciendo un "SetValue ( ) "método.

  3. Un parámetro que sabe cómo calcular su propio valor deberá almacenar ese valor en la memoria caché, de modo que a) pueda responder a "GetValue ()" sin recargos costosos, yb) solo emita un "Cambio" notificación cuando cambie su valor almacenado en caché, a fin de eliminar actualizaciones innecesarias.

  4. Un parámetro que sabe cómo calcular su propio valor generalmente tendrá un método privado RecomputeOwnValue() que se invoca a) cuando uno de los parámetros depende de los cambios, b) una vez al final de su propio constructor , a fin de calcular su valor inicial. Si existe la posibilidad de que un parámetro deba detenerse dependiendo de un parámetro en particular y, en su lugar, comenzar a depender de otro parámetro, también deberá invocar RecomputeOwnValue() cada vez que se agregue / elimine un parámetro dependiente.

  5. Puede crear un álgebra completa de parámetros para las operaciones de uso frecuente. Por ejemplo, puede introducir clases de parámetros lógicos AND, OR, XOR, etc., que saben cómo volver a calcular sus propios valores como resultado de una operación lógica en los valores de otros dos parámetros booleanos de los que dependen, puede introducir el parámetro de operación aritmética clases, haciendo suma, resta, multiplicación, división, etc., etc.

  6. Puede escribir funciones simples de ayuda que ejemplifican parámetros interconectados de una manera tan conveniente como escribir expresiones. Por lo tanto, es posible que tengas algo como esto:

    d = fob (a, violín (b, c)))

Donde fob() crea un objeto FobParameter , y fiddle() crea un objeto FiddleParameter , por lo que la expresión anterior significa que el parámetro d es un new FobParameter() que depende de dos instancias de ReadonlyParameter , para el cual pasamos el parámetro preexistente a y un new FiddleParameter() , que a su vez depende de otras dos instancias de ReadonlyParameter , para las cuales pasamos los parámetros preexistentes b y c .

    
respondido por el Mike Nakis 18.06.2015 - 14:03

Lea otras preguntas en las etiquetas