vendredi 27 novembre 2020

Should I use observables or have all logic in parent?

I have following code:

Container is an object that holds set of Parts. Some of parts are able to tell to container that they changed (ObservablePart notifies Container via someObservableMarketChanged method), and some just update their state (NonObservablePart). In case that ObservablePart changes, I need to do recalculation, and update all parts again (but this change is only applicable to NonObservableParts, ObservableParts have their values remain the same). The output of this program is:

Changing NONobservable id 4 from 1 -> 235
Changing NONobservable id 3 from 1 -> 235
Changing observable id 1 from 1 -> 235
Changing NONobservable id 4 from 235 -> 236
Changing NONobservable id 3 from 235 -> 236
Changing NONobservable id 4 from 236 -> 236
Changing NONobservable id 3 from 236 -> 236
Changing observable id 2 from 1 -> 235
Changing NONobservable id 4 from 236 -> 470
Changing NONobservable id 3 from 236 -> 470
Changing NONobservable id 4 from 470 -> 470
Changing NONobservable id 3 from 470 -> 470

and as you can see there is too much unneeded calls. What I wanted to have is this:

Changing observable id 1 from 1 -> 235
Changing observable id 2 from 1 -> 235
Changing NONobservable id 4 from 1 -> 470
Changing NONobservable id 3 from 1 -> 470

It is possible to have this case implemented as easy as:

void doit(){
        allParts.stream()
                .filter(x -> x instanceof ObservablePart)
                .forEach(x -> x.updateMe(235));
        Map<Integer, Part> changed = onlyNonObservableMarketsChange();
        for(Part m : allParts){
            m.updateMe(changed.get(m.getId()).getVal());
        }
    }

So, basically, instead of ObservablePart notifying Container that it's state changed, I have to put all the logic inside Container, and Container is not more dumb container that just forwards responsibility to Part objects. Now, it has to know which parts are observable and which are not, and that it first needs to update all observable parts, and only then it can do some calculations and update non-observable parts.

Is this use of observer pattern an overkill in this case? Should I just put this logic inside Container, since code and reasoning is much more straightforward?

import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

public class Differ {
    public static void main(String[] args) {
        Container m = new Container();
        m.allParts = Set.of(
                new ObservablePart(1,m),
                new ObservablePart(2,m),
                new NonObservablePart(3),
                new NonObservablePart(4)
        );
        m.doit();
    }
}

class Container {
    public Set<Part> allParts = new HashSet<>();

    void doit(){
        for(Part m : allParts){
            m.updateMe(235);
        }
    }

    //this would not be in Container, just needed for example
    int getSumOfObservableMarkets(){
        return allParts.stream()
                .filter(x -> x instanceof ObservablePart)
                .mapToInt(x -> ((ObservablePart) x).val)
                .sum();
    }

    //this would not be in container, but something else would calc. changes
    Map<Integer, Part> onlyNonObservableMarketsChange(){
        int obsSum = getSumOfObservableMarkets();
        return allParts.stream()
                .collect(Collectors.toMap(k -> k.getId(), m -> {
                    if(m.getId()==1 || m.getId()==2){
                        return m;
                    }
                    m.updateMe(obsSum);
                    return m;
                }));
    }

    void someObservableMarketChanged(){
        Map<Integer, Part> changed = onlyNonObservableMarketsChange();
        for(Part m : allParts){
            m.updateMe(changed.get(m.getId()).getVal());
        }
    }
}

interface Part {
    int getId();
    int getVal();
    void updateMe(int val);
}
class ObservablePart implements Part {
    Container container;
    ObservablePart(int id, Container m){this.id=id;
        container =m;}
    public int id;
    int val = 1;
    public int getId() {return id;}
    public int getVal() {return val;}
    public void updateMe(int val) {
        if(this.val != val) {
            System.out.printf("Changing observable id %s from %s -> %s%n", id, this.val, val);
            this.val = val;
            container.someObservableMarketChanged();
        }
    }
}
class NonObservablePart implements Part {
    public int id;
    int val = 1;
    public NonObservablePart(int id) {this.id = id;}
    public int getId() {return id;}
    public int getVal() {return val;}
    public void updateMe(int val) {
        System.out.printf("Changing NONobservable id %s from %s -> %s%n", id, this.val, val);
        this.val = val;
    }
}

Aucun commentaire:

Enregistrer un commentaire