Populate parent list items based on child values

Consider the following code:

CLASS 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+"]"); } } 

And CLASS Tester :

 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); } } } } 

When you run this, you will notice that the values ​​of the parent elements in the list are updated with the sum of the values ​​in the child list.

The problem I am facing is that I want it to be updated completely through the tree, and not just the parent itself.

Is there a template that will help me achieve this?

See the illustration below:

enter image description here

+6
source share
4 answers

Like others, I would like to use the Observer pattern. Each parent node element listens for changes for children.

But my solution is different from the @zmf solution, because if you have a large tree with a lot of node children, and with each update you have to sum each value, you would spend a lot of time processing.

What if you send only the difference between the old value and the new value each time the node child is updated. Let's make an example. You start with this tree:

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

and you update these children

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

node, which is updated with the value "3", sends its change to the parent method with a call to the parent.updateNode (3) method. The parent should only summarize its current value (in this example, "2") with the value that it receives from the child element node. Therefore, it will be updated to the value "5"

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

node with a new value of "5" will call parent.updateNode (3), and the final solution will be

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

IMHO, this solution is better because each updateNode () method should only sum its current value with the change received from its child node and call its parent with the same received value. You do not need to get a value from each of your children and summarize all the values. This will save you a lot of time if you have a large tree. So, in this example, when you change the value from 0 to 3. You will get 2 calls to parent.updateNode (3), and each parent will receive an update.

+4
source
 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); } } } 
+2
source

Another poster suggested using an observer pattern . The Observer pattern is a subset of the Pub / Sub pattern . I recommend using this with the Observer pattern.

The main difference between the Observer pattern and the Pub / Sub pattern is that in the Observer pattern, the Observer is both the ChangeEvents publisher and the message manager. This essentially makes everyone observable in EventDispatcher. In a traditional pub / Sub template, Observables are the only publisher of ChangeEvents. ChangeEvents are published in a separate EventDispatchingService that handles what needs to be sent to subscribers.

Cannot attempt to track global changes using the Observer pattern. For example, if you want to count the number of times the addToCompliant() method was called, you would need to add an Observer to each Observable instance. With Event Pub / Sub, your observer class can simply subscribe to a ChangeEvent type listener and it will receive all of them. The best (IMHO) Event Pub / Sub library I used was the Google Guava Event Bus . In your particular case, I would do something like the following.

 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()); 
+1
source

Based on your class name, I think you want to see your audit progression in real time. So my hypothesis is:

  • tree structure does not change too much, almost fixed after creation
  • node values ​​change frequently, the initial states of the counters are 0

Here is an effective implementation:

  • each node maintains a complete list of its parent nodes
  • nodes with 0 value are inserted
  • when the node value changes or simply increases, the values ​​of the parents in the node list are updated by applying a delta between the previous node value

As a result, the structure is always relevant; insertion of a node is still possible and does not affect existing nodes.

If many control threads execute at the same time and report the values ​​to the structure, you should take care of concurrency problems and use AtomicInteger as the counter holders.

This is a pragmatic design, and I sincerely did not find a suitable template. Like sorting algorithms, trying to use patterns in this context can be counterproductive.

0
source

Source: https://habr.com/ru/post/908494/


All Articles