Заполните родительские элементы списка на основе дочерних значений - PullRequest
6 голосов
/ 15 февраля 2012

Рассмотрим следующий код:

КЛАСС 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+"]");
    }
}

И КЛАСС Тестер :

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

Когда вы запустите это, вы заметите, что значения родительских элементов в списке обновляются суммой значений в дочернем списке.

Проблема, с которой я сталкиваюсь, заключается в том, что я хочу, чтобы она обновлялась по всему дереву, а не только по непосредственному родителю.

Есть ли образец, который помог бы мне достичь этого?

См. Иллюстрацию ниже:

enter image description here

Ответы [ 4 ]

4 голосов
/ 28 февраля 2012

Как и другие, я предложил использовать шаблон ObserverКаждый родительский узел прослушивает изменения на дочерних.

Но мое решение отличается от решения @zmf, потому что если у вас большое дерево с множеством дочерних узлов, и при каждом обновлении вы должны суммировать каждое значение, вы потратите много времени на обработку.

Что если вы отправляете только разницу между старым значением и новым значением каждый раз, когда обновляете дочерний узел.Давайте сделаем пример.Вы начинаете с этого дерева:

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

и обновляете дочерние элементы, подобные этому

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

узел, который обновляется со значением "3", отправляет свое изменение родительскому элементу с помощьювызов метода parent.updateNode (3).Родитель должен только суммировать свое текущее значение (в данном примере «2») со значением, которое он получает от дочернего узла.Таким образом, он будет обновлен до значения "5"

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

, узел с новым значением "5" вызовет parent.updateNode (3), и окончательное решение будет

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

IMHO, это решение лучше, потому что каждый метод updateNode () должен только суммировать свое собственное текущее значение с изменением, полученным от его дочернего узла, и вызвать своего родителя с таким же полученным значением.Вам не нужно получать значение от каждого из ваших детей и суммировать все значения.Это сэкономит вам много времени, если у вас большое дерево.Так что в этом примере при изменении значения с 0 на 3. Вы получите 2 вызова parent.updateNode (3), и каждый родитель будет обновлен.

2 голосов
/ 27 февраля 2012
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 голос
/ 01 марта 2012

Другие авторы предложили использовать шаблон Observer . Шаблон Observer является подмножеством шаблона Pub / Sub . Я рекомендую использовать это поверх шаблона Observer.

Основное различие между шаблоном Observer и шаблоном Pub / Sub заключается в том, что в шаблоне Observer Observer является как издателем ChangeEvents, так и диспетчером сообщений. По сути, это делает каждый Observable в EventDispatcher. В традиционном шаблоне Pub / Sub Observables являются только издателями ChangeEvents. ChangeEvents публикуются в отдельном EventDispatchingService, который обрабатывает то, на что подписчикам нужно отправлять события.

Попытка отследить глобальные изменения с помощью шаблона Observer затруднительна. Например, если вы хотите посчитать, сколько раз был вызван метод addToCompliant(), вам придется добавлять наблюдателя в каждый экземпляр наблюдаемого. С Event Pub / Sub ваш класс-наблюдатель может просто подписаться на прослушивание типа ChangeEvent, и он получит их все. Лучшая (IMHO) библиотека публикаций и публикаций о событиях, которую я использовал, - Шина событий Google Guava . В вашем конкретном случае я бы сделал что-то вроде следующего.

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 голосов
/ 03 марта 2012

На основании вашего имени класса, я думаю, вы хотите, чтобы ваш прогресс аудита во время работы. Итак, моя гипотеза:

  • древовидная структура не сильно меняется, почти исправлена ​​после создания
  • значения узлов часто меняются, начальные состояния счетчиков 0

Вот эффективная реализация:

  • каждый узел поддерживает полный список своих родительских узлов
  • узлы вставляются со значением 0
  • когда значение узла изменяется или просто увеличивается, значения родителей из списка узлов обновляются путем применения дельты между предыдущим значением узла

Как следствие, структура всегда актуальна, вставка узлов все еще возможна и не влияет на существующие узлы.

Если многие потоки аудита работают одновременно и сообщают значения в структуру, вы должны позаботиться о проблемах параллелизма и использовать AtomicInteger в качестве счетчиков.

Это прагматичный дизайн, и искренне я не нашел ни одного подходящего шаблона. Как и в случае алгоритмов сортировки, попытка использовать шаблоны в таком контексте может привести к обратным результатам.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...