design patterns - ¿Patrones de diseño de barra de progreso?
design-patterns oop (5)
La aplicación que estoy escribiendo realiza un algoritmo de longitud que generalmente tarda unos minutos en finalizar. Durante este tiempo, me gustaría mostrar al usuario una barra de progreso que indica qué parte del algoritmo se realiza de la forma más precisa posible.
El algoritmo se divide en varios pasos, cada uno con su propio tiempo típico. Por ejemplo-
- inicialización (500 milli-sec)
- lectura de entradas (5 seg)
- paso 1 (30 seg)
- paso 2 (3 minutos)
- escritura de salidas (7 seg)
- apagando (10 milli-sec)
Cada paso puede informar su progreso con bastante facilidad configurando el rango en el que está trabajando, digamos [0 a 150] y luego informando el valor que completó en su bucle principal.
Lo que actualmente tengo configurado es un esquema de monitores de progreso anidados que forman una especie de árbol implícito de informes de progreso.
Todos los monitores de progreso heredan de una interfaz IProgressMonitor
:
class IProgressMonitor
{
public:
void setRange(int from, int to) = 0;
void setValue(int v) = 0;
};
La raíz del árbol es ProgressMonitor que está conectado a la interfaz GUI real:
class GUIBarProgressMonitor : public IProgressMonitor
{
GUIBarProgressMonitor(ProgressBarWidget *);
};
Cualquier otro nodo en el árbol son monitores que toman el control de una parte del progreso principal:
class SubProgressMonitor : public IProgressMonitor
{
SubProgressMonitor(IProgressMonitor *parent, int parentFrom, int parentLength)
...
};
Un SubProgressMonitor
toma el control del rango [parentFrom, parentFrom+parentLength]
de su principal.
Con este esquema, puedo dividir estáticamente el progreso de nivel superior de acuerdo con la porción relativa esperada de cada paso en el tiempo global. Cada paso puede subdividirse en piezas, etc.
La principal desventaja de esto es que la división es estática y es doloroso realizar cambios de acuerdo con las variables que se descubren en tiempo de ejecución.
Entonces, la pregunta: ¿hay algún patrón de diseño conocido para el monitoreo del progreso que resuelva este problema?
Este es un problema difícil, también hemos luchado con él en un proyecto anterior.
Lo mejor que se me ocurre es recopilar estadísticas de cuánto tiempo dura cada fase en la vida real, y ajustar las longitudes de intervalo relativas en consecuencia.
Sin embargo, no lo hemos implementado en ese proyecto (al menos mientras estuve allí), así que esta es solo una idea teórica :-)
Péter''s fue el enfoque que tomé en un gran proyecto; durante nuestro lanzamiento inicial y piloto, cada uno de nuestros miles de dispositivos móviles enviaron datos de tiempo y uso, y utilizamos las desviaciones promedio, mediana y estándar del tiempo necesario para ajustar la configuración de nuestras tareas (cuando el la tarea se permitió ejecutar, cuánto tiempo se permitió ejecutar, qué valores se usaron en la barra de progreso, etc.). Dado que nuestra solución fue construida como la suya, pero impulsada por los valores proporcionados en un archivo de configuración XML, pensamos en construir esto como un sistema automatizado (por ejemplo, el servidor verificaría estos valores en algún intervalo, notará que ciertas tareas tardaron más tiempo en días de lo que solían y actualizar el archivo de configuración para reprogramarlos o alargarlos), pero pensé que no valía la pena solo para evitar una revisión humana rápida cada pocas semanas.
Como no conozco una solución técnica para su problema, creo que lo que le muestra al usuario (y cuánto tiempo dedica a desarrollar una solución) debe basarse en preocupaciones funcionales: ¿quién está usando esto? ¿Cuán precisa debe ser la información? ¿Es este un proceso interactivo durante el cual no pueden hacer ningún otro trabajo, o pueden dejarlo correr en segundo plano y volver a él? ¿Es el proceso de trabajo durante el cual su función de larga duración ocurre una función sensible al tiempo o de misión crítica?
Lamento que realmente no pueda darte la respuesta que estás buscando, pero quizás pensar en lo que estás tratando de lograr a grandes rasgos es una buena idea. =)
Puede considerar reemplazar la barra de progreso con un círculo de progreso. Si la tarea tiene N pasos, entonces haga N cuñas en el pie y llene cada cuña como una barra de progreso, a medida que se ejecuta ese paso.
Como paso adicional, tal vez se muestre texto para cada paso, de modo que tengan algo que leer mientras avanza el paso.
Un enfoque muy interesante es la percepción del usuario.
Chris Harrison publicó un documento sobre cómo el usuario percibe el tiempo que pasa dependiendo del progreso informado por la barra de progreso (aunque la duración real fue obviamente idéntica en todos los experimentos)
Tenga en cuenta que la fórmula de visualización preferida es (x + (1-x) / 2) 8 donde x
es el progreso real en una escala de 0 a 1 :)
Por lo tanto, sugeriría:
- reúna algunas estadísticas sobre el porcentaje de tiempo que toma una tarea determinada
- mida la inicialización y úselo para escalar su progreso en la barra de progreso, siendo pesimista (prepare un buffer de 10-15% por ejemplo)
- justo antes de la última tarea (o pocas últimas tareas, siempre que tengan una duración determinística), haga todo lo posible para completar la barra de progreso a tiempo (con aceleración progresiva)
Lo sé, eso no es exacto, pero si los usuarios piensan que es más rápido, ¡me conformaré con eso!
Cree un AggregateProgressMonitor que calcule automáticamente las divisiones de progreso de los niños según la información proporcionada por los monitores de progreso de los niños. El monitor de progreso del niño debe, al menos, informar al padre del tiempo de ejecución "esperado". Los tiempos de ejecución estimados de los monitores secundarios se pueden actualizar luego según sus operaciones respectivas en función de los parámetros de tiempo de ejecución y el informe general de progreso se ajustará en consecuencia y automáticamente.
Algo como esto...
class IAggregateProgressMonitor : public IProgressMonitor
{
void setChildValue(IProgressMonitor *, int v);
void setChildEstimatedTime(IProgressMonitor *, int v);
}
class AggregateProgressMonitor : public IAggregateProgressMonitor
{
void setChildValue(IProgressMonitor * child, int v)
{
int aggregateValue = mapChildValueToAggregateValue(child, v);
setValue(aggregateValue);
}
void setChildEstimatedTime(IProgressMonitor * child, ulong ms)
{
children[child]->estimatedTime = ms;
updateChildProgressRatios();
}
}
class SubProgressMonitor : public IProgressMonitor
{
SubProgressMonitor(IAggregateProgressMonitor *parent, int parentFrom,
int parentLength) ... ;
void setValue(int v)
{
parent->setChildValue(this, v);
}
void setEstimatedRunningTime(ulong ms)
{
parent->setChildEstimatedTime(this, ms);
}
};
Incluso puede usar el tiempo observado del primer paso para reasignar los informes de progreso posteriores para que sean más precisos.
Deberá mantener un mapa ordenado de algún tipo en el AggregateProgressMonitor para poder rastrear y calcular toda la información de los niños.
Una vez completado, puede extender AggregateProgressMonitor (anulando los métodos de IProgressMonitor) para mostrar el progreso al usuario.