vendredi 13 mai 2022

Alternatives to cyclic references in java design

I am currently working on a design that is quite processing-intensive, and processing happens in various phases which are also required to interact with each other. I have developed various ProcessingManagers for each phase and to keep them loosely coupled with each other, a StateMachine class is also designed which will act as an interface for various phase managers to interact with each other.

Below is the dummy code which I just wrote by imitating the design.

I feel some problems with this design -

  1. Exessive use of method reference
  2. cyclic references between StateMachine and PhaseManagers

My Questions:

  1. Please suggest what could be the major disadvantages of such cycle references
  2. If you could suggest an altogether different design for the same solution or any minor tweak to avoid cyclic references
  3. Is there any local message queue API (within the same JVM) that we can use for PhasesManagers and StateMachine to send signals to each other and cyclic reference can be avoided.

Code:

StateMachine class:

package com.vg;

public class StateMachine {
    private volatile Phase state;
    private volatile boolean backgroundCompletedFirstPhase;
    private volatile boolean firstPhaseCompleted;

    private final BackgroundPhaseManager backgroundPhaseManager;
    private final FirstPhseManager firstPhaseManager;
    private final IntermediatePhaseManager intermediatePhaseManager;
    private final SecondPhaseManager secondPhaseManager;

    public StateMachine() {
        state = Phase.FIRST;
        backgroundCompletedFirstPhase = false;
        firstPhaseCompleted = false;

        this.backgroundPhaseManager = new BackgroundPhaseManager(this::process, this::stateUpdater,
                this::setBackgroundCompletedFirstPhase);
        this.firstPhaseManager = new FirstPhseManager(this::setFirstPhaseCompleted);
        this.intermediatePhaseManager = new IntermediatePhaseManager(this::startSecondPhase);
        this.secondPhaseManager = new SecondPhaseManager();
    }

    private void stateUpdater(String message) {
        System.out.println("perform udate based on many conditions");
    }

    private void setBackgroundCompletedFirstPhase() {
        this.backgroundCompletedFirstPhase = true;
        startIntermediateIfApplicable();
    }

    private void setFirstPhaseCompleted(){
        this.firstPhaseCompleted = true;
        startIntermediateIfApplicable();
    }

    private void startIntermediateIfApplicable(){
        if(backgroundCompletedFirstPhase && firstPhaseCompleted){
            this.state = Phase.INTERMEDIATE;
            intermediatePhaseManager.process("do it");
        }
    }

    private void startSecondPhase(){
        this.state = Phase.SECOND;
    }

    private void process(String message) {
        if (Phase.FIRST.equals(this.state)) {
            firstPhaseManager.process(message);
        } else {
            secondPhaseManager.process(message);
        }
    }

    public void start(){
        System.out.println("pass background processing manager to an external system which calls its methods");
    }

    public static void main(String[] args) {
        new StateMachine().start();
    }
}

Phases Enum:

package com.vg;

public enum Phase {
    FIRST, INTERMEDIATE, SECOND;
}

BackgroundPhaseManager class:

package com.vg;

import java.util.function.Consumer;

public class BackgroundPhaseManager {
    private final Consumer<String> consumer;
    private final Consumer<String> stateUpdater;
    private final Runnable finishFirstPhase;

    public BackgroundPhaseManager(Consumer<String> consumer, Consumer<String> stateUpdater,
                                  Runnable finishFirstPhase) {
        this.consumer = consumer;
        this.stateUpdater = stateUpdater;
        this.finishFirstPhase = finishFirstPhase;
    }

    public void supply(){
        consumer.accept("my-message");
    }

    public void changeStatus(){
        stateUpdater.accept("new-State");
    }

    public void finishFirstPhase(){
        finishFirstPhase.run();
    }
}

FirstPhseManager class:

package com.vg;

public class FirstPhseManager {
    Runnable phaseCompleteMessanger;

    public FirstPhseManager(Runnable phaseCompleteMessanger) {
        this.phaseCompleteMessanger = phaseCompleteMessanger;
    }

    public void process(String message){
        System.out.println("Extensive processing involving multiple threads, " +
                "hand-shakes, retries, failure recoveries, and takes-time " +
                "and lastly calls back to done function");
    }

    private void done(){
        phaseCompleteMessanger.run();
    }
}

IntermediatePhaseManager class:

package com.vg;

public class IntermediatePhaseManager {

    Runnable phaseCompleteMessanger;

    public IntermediatePhaseManager(Runnable phaseCompleteMessanger) {
        this.phaseCompleteMessanger = phaseCompleteMessanger;
    }

    public void process(String message){
        System.out.println("Extensive processing involving multiple threads, " +
                "hand-shakes, retries, failure recoveries, and takes-time " +
                "and lastly calls back to done function");
    }

    private void done(){
        phaseCompleteMessanger.run();
    }
}

SecondPhaseManager class:

package com.vg;

public class SecondPhaseManager {


    public void process(String message){
        System.out.println("perform extensive processing");
    }
}

Aucun commentaire:

Enregistrer un commentaire