2012-02-15 8 views
6

considere el siguiente código:elementos de lista padre pueblan basados ​​en niños valores

CLASE AuditProgressReport:

public class AuditProgressReport 
{ 
    private List<AuditProgressReport> audit_progress_reports = null; 

    private String name = null; 
    private String description = null; 

    private int compliant; 
    private int non_compliant; 
    private int not_completed ; 

    /** 
    * 
    */ 
    public AuditProgressReport() 
    { 
     super(); 
    } 

    public AuditProgressReport(
     String name_param, 
     int compliant_param, 
     int non_compliant_param, 
     int not_completed_param) 
    { 
     super(); 

     this.name = name_param; 
     this.compliant = compliant_param; 
     this.non_compliant = non_compliant_param; 
     this.not_completed = not_completed_param; 
    } 

    public void addToCompliant(int compl_to_add_param) 
    { 
     this.compliant += compl_to_add_param; 
    } 

    public void addToNonCompliant(int non_compl_to_add_param) 
    { 
     this.non_compliant += non_compl_to_add_param; 
    } 

    public void addToNotCompleted(int not_compl_param) 
    { 
     this.not_completed += not_compl_param; 
    } 

    public void setAuditProgressReports(List<AuditProgressReport> report_category_nodes_param) 
    { 
     this.audit_progress_reports = report_category_nodes_param; 
    } 

    public List<AuditProgressReport> getAuditProgressReports() 
    { 
     return this.audit_progress_reports; 
    } 

    public void setCompliant(int compliantParam) 
    { 
     this.compliant = compliantParam; 
    } 

    public int getCompliant() 
    { 
     return this.compliant; 
    } 

    public void setNonCompliant(int nonCompliantParam) 
    { 
     this.non_compliant = nonCompliantParam; 
    } 

    public int getNonCompliant() 
    { 
     return this.non_compliant; 
    } 

    public void setNotCompleted(int notCompletedParam) 
    { 
     this.not_completed = notCompletedParam; 
    } 

    public int getNotCompleted() 
    { 
     return this.not_completed; 
    } 

    public void setName(String name_param) 
    { 
     this.name = name_param; 
    } 

    public String getName() 
    { 
     return this.name; 
    } 

    public void setDescription(String description_param) 
    { 
     this.description = description_param; 
    } 

    public String getDescription() 
    { 
     return this.description; 
    } 

    @Override 
    public String toString() 
    { 
     return ("Compliant["+this.compliant+ 
      "] Non-Compliant["+this.non_compliant+ 
      "] Not-Completed["+this.not_completed+"]"); 
    } 
} 

y Clase probador:

public class Tester 
{ 
    public static void main(String[] args) 
    { 

     List<AuditProgressReport> main_level = new ArrayList<AuditProgressReport>(); 

     AuditProgressReport ar_1_1 = new AuditProgressReport("ar_1_1",0,0,0); 
     AuditProgressReport ar_1_2 = new AuditProgressReport("ar_1_2",0,0,0); 

     AuditProgressReport ar_1_1_1 = new AuditProgressReport("ar_1_1_1",0,0,0); 
     AuditProgressReport ar_1_1_2 = new AuditProgressReport("ar_1_1_2",15,65,20); 
     AuditProgressReport ar_1_1_3 = new AuditProgressReport("ar_1_1_3",20,30,50); 

     AuditProgressReport ar_1_1_1_1 = new AuditProgressReport("ar_1_1_1_1",5,5,90); 
     AuditProgressReport ar_1_1_1_2 = new AuditProgressReport("ar_1_1_1_2",55,5,40); 
     AuditProgressReport ar_1_1_1_3 = new AuditProgressReport("ar_1_1_1_3",35,35,30); 

     List<AuditProgressReport> arl_1_1_1 = new ArrayList<AuditProgressReport>(); 
     arl_1_1_1.add(ar_1_1_1_1); 
     arl_1_1_1.add(ar_1_1_1_2); 
     arl_1_1_1.add(ar_1_1_1_3); 

     ar_1_1_1.setAuditProgressReports(arl_1_1_1); 

     List<AuditProgressReport> arl_1_1 = new ArrayList<AuditProgressReport>(); 
     arl_1_1.add(ar_1_1_1); 
     arl_1_1.add(ar_1_1_2); 
     arl_1_1.add(ar_1_1_3); 

     AuditProgressReport ar_1_2_1 = new AuditProgressReport("ar_1_2_1",10,30,60); 
     AuditProgressReport ar_1_2_2 = new AuditProgressReport("ar_1_2_2",20,20,60); 



     List<AuditProgressReport> arl_1_2 = new ArrayList<AuditProgressReport>(); 
     arl_1_2.add(ar_1_2_1); 
     arl_1_2.add(ar_1_2_2); 

     ar_1_1.setAuditProgressReports(arl_1_1); 

     ar_1_2.setAuditProgressReports(arl_1_2); 

     main_level.add(ar_1_1); 
     main_level.add(ar_1_2); 


     Tester tester = new Tester(); 

     for(AuditProgressReport prog_rep : main_level) 
     { 
      tester.populateParents(prog_rep, null); 
     } 

     //TODO Now check the values... 
    } 

    private void populateParents(
     AuditProgressReport audit_progress_param, 
     AuditProgressReport parent_param) 
    { 
     List<AuditProgressReport> audit_progress = 
      audit_progress_param.getAuditProgressReports(); 

     System.out.println("name["+audit_progress_param.getName()+"]"); 

     if(parent_param != null) 
     { 
      int compl = audit_progress_param.getCompliant(); 
      int nonCompl = audit_progress_param.getNonCompliant(); 
      int notCompleted = audit_progress_param.getNotCompleted(); 

      parent_param.addToCompliant(compl); 
      parent_param.addToNonCompliant(nonCompl); 
      parent_param.addToNotCompleted(notCompleted); 
     } 

     if(audit_progress != null && ! audit_progress.isEmpty()) 
     { 
      for(AuditProgressReport prog_rep : audit_progress) 
      { 
       this.populateParents(prog_rep,audit_progress_param); 
      } 
     } 
    } 
} 

Al ejecutar esto, Notará que los valores de los elementos principales en la lista se actualizan con la suma del valores en la lista de niños.

El problema al que me enfrento es que quiero que se actualice todo el tiempo en el árbol en lugar de solo el padre inmediato.

¿Hay algún patrón que me ayude a lograrlo?

vea la ilustración siguiente:

enter image description here

+2

Para cada nodo, establezca su valor en la suma de los valores de sus hijos. Huele a recursividad;) –

+0

¿No podrías hacer que cada padre oyente sea sus hijos? También te ahorraría algunos problemas para actualizar manualmente. – bdecaf

Respuesta

4

Al igual que otros sugirieron que usaría el patrón Observer. Cada nodo padre escucha los cambios en los niños.

Pero mi solución difiere de la de @zmf porque si tienes un árbol grande con muchos nodos hijos y en cada actualización tienes que sumar cada valor, pasarías mucho tiempo de procesamiento.

¿Qué ocurre si envía solo la diferencia entre el valor anterior y el nuevo cada vez que actualiza un nodo secundario? Hagamos un ejemplo. Se empieza con este árbol:

[12]--+--[10]-----[10] 
     | 
     +--[ 2]--+--[ ] 
       | 
       +--[ 2] 

y actualizar niños como esto

[12]--+--[10]-----[10] 
     | 
     +--[ 2]--+--[ 3] 
       | 
       +--[ 2] 

el nodo que se actualiza con el valor "3" enviar su cambio a los padres con el padre llamada al método. updateNode (3). El padre solo tiene que sumar su valor actual (en este ejemplo "2") con el valor que recibe del nodo hijo. Por lo tanto, se actualizará con el valor "5"

[12]--+--[10]-----[10] 
     | 
     +--[ 5]--+--[ 3] 
       | 
       +--[ 2] 

el nodo con el nuevo valor "5" llamará a parent.updateNode (3) y la solución final será

[15]--+--[10]-----[10] 
     | 
     +--[ 5]--+--[ 3] 
       | 
       +--[ 2] 

mi humilde opinión esta solución es mejor porque cada método updateNode() solo tiene que sumar su propio valor actual con el cambio recibido de su nodo hijo y llamar a su elemento primario con el mismo valor recibido. No tiene que obtener el valor de cada uno de sus hijos y sumar todos los valores. Esto te ahorrará mucho tiempo si tienes un gran árbol. Por lo tanto, en este ejemplo, cuando cambie el valor de 0 a 3. Recibirá 2 llamadas a parent.updateNode (3) y cada padre se actualizará.

2
public void updateNode(int value) { 

    if (value != this.value) { 
     this.value = value; 

     if (getParent() != null) { 
      int sum = 0; 
      for (Node n : getParent().getChildren()) { 
       sum += n.getValue(); 
      } 
      getParent.updateNode(sum); 
     } 
    } 
} 
1

Otros carteles sugirieron el uso del Observer pattern. El patrón Observer es un subconjunto de Pub/Sub pattern. Recomiendo usar esto en un patrón Observer.

La principal diferencia entre un patrón Observer y un patrón Pub/Sub es que en un patrón Observer, un Observer es un editor de ChangeEvents y un despachador de mensajes. Básicamente, hace que cada Observable se convierta en un EventDispatcher. En un patrón Pub/Sub tradicional, los Observables son solo editores de ChangeEvents. ChangeEvents se publica en un EventDispatchingService independiente que maneja a qué suscriptores se deben enviar los eventos.

Es difícil hacer un seguimiento de los cambios globales con un patrón Observer. Por ejemplo, si desea contar la cantidad de veces que se llamó el método addToCompliant(), debería agregar el Observer en cada instancia de Observable. Con un evento Pub/Sub, su clase de observador puede suscribirse para escuchar en el tipo de ChangeEvent y recibirá todos ellos. La mejor (en mi humilde opinión) Event Pub/Sub Library que he usado es Google Guava's Event Bus. En tu caso particular, haría algo como lo siguiente.

public class EventBusSingleton { 
    public static final EventBus INSTANCE = new EventBus("My Event Bus"); 
} 

public class ComplianceChange { 
    private AuditProgressReport changedReport; 
    private int delta; 

    public ComplianceChange(AuditProgressReport changedReport, int delta) { 
     this.changedReport = changedReport; 
     this.delta = delta; 
    } 

    ... 
} 

public class AuditProgressReport { 

    ... 
    private AuditProgressReport parent; 

    public AuditProgressReport getParent() { 
     return parent; 
    } 

    public void addToCompliant(int delta) { 
     this.compliant += delta; 
     ComplianceChange change = new ComplianceChange(this, delta); 
     EventBusSingleton.INSTANCE.post(change); 
    } 
    ... 
} 

public class ComplianceChangeHandler { 

    @Subscribe 
    public void notifyParent(ComplianceChange event) { 
     AuditProgressReport parent = event.getChangedReport().getParent(); 
     int delta = event.getDelta(); 
     parent.addToCompliant(delta); 
    } 

    @Subscribe 
    public void somethingElse(ComplianceChange event) { 
     // Do Something Else 
    } 
} 

// Somewhere during initialization 
EventBusSingleton.INSTANCE.register(new ComplianceChangeHandler()); 
+0

Creo que el patrón Pub/Sub no es la mejor opción para este problema porque el problema es demasiado simple para usar dicho patrón. En este problema, solo el nodo padre directo desea conocer los cambios en los valores de sus hijos. Utilizaría el patrón Pub/Sub para problemas más complejos, es decir, para la comunicación asíncrona entre dos clases no correlacionadas en dos paquetes diferentes. De hecho, con este paquete, las dos partes de la comunicación no pueden conocerse entre sí en tiempo de compilación o en tiempo de ejecución. – PinoSan

0

Según su nombre de clase, supongo que desea ver la progresión de su auditoría en funcionamiento. Así que mi hipótesis:

  • la estructura de árbol no cambia demasiado, casi fija después de la creación
  • valores de nodos cambian a menudo, los contadores de los estados iniciales son 0

Aquí es una implementación eficiente:

  • cada nodo mantiene la lista completa de sus nodos principales
  • nodos se insertan con 0 valor
  • cuando se cambia un valor de nodo o simplemente aumentaron, los valores de los padres de la lista del nodo se actualizan mediante la aplicación del delta entre el valor del nodo anterior

Como consecuencia, la estructura es siempre al día , la inserción de nodos sigue siendo posible y no afecta a los nodos existentes.

Si muchos subprocesos de auditoría se ejecutan al mismo tiempo e informan valores en la estructura, debe tener en cuenta los problemas de simultaneidad y usar AtomicInteger como titulares de contadores.

Este es un diseño pragmático y sinceramente no he encontrado ningún patrón coincidente. Al igual que para los algoritmos de ordenamiento, tratar de usar patrones en dicho contexto puede ser contraproducente.

Cuestiones relacionadas