5

I'm going to try to make this as concise and concrete as possible, but apologies since I can think of multiple ways to make it work. This question might also relate to handling production workflows or manufacturing, but I have no experience in these areas.

I have a problem where I need to move an Object, O, from different states to other states (like a Nondeterministic finite automata). The problem is the Object can be in different states at the same time, and the interaction sometimes matters.

For example say there are 10 states, O can be in up to 3 of those states at the same time. I'm a bit lost as to what type of code design I should use to model this. The two ideas I am thinking about are

  1. Pass O from handler to handler, and manage the transitions between states, and keep some sort of info in O saying which states they are in, so that each state can figure out what to do based on what other states O is in.

  2. Have a larger Superclass that handles which states O is in, and handles the interactions.

I'd normally say 1. but handling all the interactions between handlers in each handler sounds messy.

What would be the better option between these 2? Or is there another method that is preferred?

** Update and Clarification **

As a concrete example. The Object can be in one of many physical locations, while it can also be in one of several stages of processing (The object might move back and forth between locations, or back and forth between the stages of processing). As additional complexity each physical location might have sublocations, but I think I can ignore that for now. So if it's clearer, it can be considered to be two independent automata, but choosing the edge to move to the next state in either automata depends on it's state from both automata.

18
  • 6
    "The Object can be in different states at the same time" -- this is a very vague statement that suggests that your program is based on quantum physics. To get a clear answer you will need to clarify at least what you mean by that.
    – kampu
    May 20, 2013 at 6:31
  • 1
    So really your object has one state, but you classify that state along 3 different axes? If that's not it, I definitely don't understand.
    – kampu
    May 20, 2013 at 6:58
  • 1
    Yeah, a nondeterministic finite state machine seems simple enough, but it doesn't seem to have the concept of being in multiple states at once; rather, for any given input symbol, the resulting state chosen may be one of several. This may be due to my limited knowledge of nondeterministic FSA, but I just don't see how a single object 'being in more than one state / at more than one node' at once is even logically possible, excepting if that state is actually made up of sub-states. .. Thanks for the interesting link, BTW :)
    – kampu
    May 20, 2013 at 7:19
  • 1
    It would help if you posted an example of an object traversing through states and what would be different if you chose different answers. May 20, 2013 at 20:21
  • 3
    You could represent your state as a single state that is a combination of your constituents. This would put you into NFA of territory. While your actual conditions would be complex, the overall system isn't much more complex by adding more "states".
    – Guvante
    May 21, 2013 at 21:06

6 Answers 6

3
+50

Your different states sound more like activities. If they were states you would use Nouns to describe them (example; "full", "empty", "box", "container"). If they were activities then you would use Adjectives to describe them (example; "opening", "processing", "making").

Your object can be at a given location (i.e. a state), but it's doing many things at once (i.e. activities). Therefore, you would have an internal collection of activities that the object contains, but it may not be responsible for the logic of those activities. Each activity could implement that logic internally.

I'd create a new object to represent the factory, which contains the object at different locations (i.e. their state). The factory object would add/remove activities to objects when those activities report they are finished or not.

Here is a mockup

 class ActivityContext
 {
      // data that is needed by
      // other activities when
      // doing work.
 }

 interface Activity
 {
      public void DoWork(ActivityContext);
      public bool DoneWork(ActivityContext);
 }

 class MyObject
 {
      public string Location;
      public List<Activity> CurrentActivities;

      public void WorkOnActivites() {...}
      public void Add(Activity) {...}
      public void Remove(Activity) {...}
 }

 class Factory
 {
      public List<MyObject> objects;

      public void ChangeLocation(MyObject) {...}
      public void Add(MyObject) {...}
      public void Remove(MyObject) {...}
      // .. and other methods
 }

You would create new objects that implement the Activity interface, add them to MyObject when that object needs to perform that activity, or change it's location. To find objects and manage them you use the Factory object.

EDIT:

I added the class ActivityContext as a data object. These objects are created and passed to an Activity when it does work. Each Activity can update the context data so that other activites can receive that data when they do work. This allows activities to be aware of what other activities are doing without directly referencing those activities.

2
  • 1
    I like the general framework, it doesn't completely fit as the changes need to occur independently and at different times for the Objects. But, it looks like I can just swap the workOnActivities() from inside MyObject to workOnObjects inside Activities. Unless I'm missing something? May 22, 2013 at 3:57
  • @greedybuddha I updated my answer to include a context class that you pass to activities. This solves the problem of having activities be aware of what other activities are doing.
    – Reactgular
    May 22, 2013 at 15:09
1

2. Have a larger Superclass that handles which states O is in, and handles the interactions.

That sounds like an awful lot of logic from what you are describing. Do you want the same class defining that 30% complete parts in Atlanta have to be shipped to Dallas as the class that handles 99% complete parts in the East part of Dallas can be moved to finished in the same area?

  1. Pass O from handler to handler, and manage the transitions between states, and keep some sort of info in O saying which states they are in, so that each state can figure out what to do based on what other states O is in.

I think this would be manageable if you provide a way to ignore the states you don't care about. If step 2 of assembly is always followed by step 3, regardless of location, you want to be able to return "go to step 3" without worrying about what factory you are in.

For instance you could make a wrapper class that allows partial transitions. If you are going the immutable route, the functions could return the result of the partial transition function, i.e.

if state.Production = Step.Two then
    return state.AlterProduction(Step.Three)

Or you could go the mutable route and just transform the state with any updates.

2
  • One of the problems with going that route is that the states need to have particular knowledge about the other states. Which kind of breaks enapsulation? Anyway you can think of to get around this? May 21, 2013 at 21:39
  • @greedybuddha: Giving information you need doesn't break encapsulation. Encapsulation is about avoiding giving information you don't need. If I don't care about location a proper encapsulation will hide it from me, if I do care I should be able to see it.
    – Guvante
    May 21, 2013 at 22:05
1

If I understand your question correctly, you could design your code similar to the outline below:

  • A StateObjectContainer class that holds references to the current state, the object, and to the state manager. This class is responsible to the metadata (the references) associated with the contained object.
  • A State class with a collection of StateObjectContainers. Also has the ability to add objects to the current state.
  • A StateManager class that has references to all state instances and exposes functionality to query them for information.
  • A StateDecider class that determines the next logical states given an object and a snap shot of all states.
  • An event or callback that is called when an object is ready to change states and is given that object as an argument.

When an object is ready to change states:

  1. Get a snapshot of all current states
  2. Calculate the next logical states
  3. Add the object to each of the calculated states.
1

What I understand from your example is that your object can be represented by two parallel state-machines (location and stage), and you need to synchronize those automata.

You could take inspiration from Statecharts, for example, which allow hierarchical state-machines with communication across states, among other things, but this is likely to be overkill for your usage.

Personally, I would try to use something like Petri-nets. Basically, you can model fork/join ("rendez-vous") transitions easily between separate subgraphs. Read for example The application of petri-nets to workflow management or this paper (which is about another topic but has nice explanations).

Then, your would fork your initial state into different parallel paths (location, stage, ..) where each "token" would represent the current state of the object for the different variables. Some or all of those tokens will then be used jointly to trigger specific transitions.

1
  • Thanks for the reading material! I've never heard of Petri-nets. I'm going to have to look into them. May 22, 2013 at 14:52
1

Sounds like your object needs a location field (and maybe a location class or interface to help ensure valid values) and a second field for processing stage which could probably be represented by an enum of the possible stages.

enum ProcessingStage { STAGE_1, STAGE_2,... STAGE_N; }

class MyObject {
    String location;
    ProcessingStage stage;
    ...
}

Surely there is some limit to the number of processing-stage combinations. Even if there are 10 possible processing-stages, and it can be in 3 of them at once (though not the same 3 across the board), that's 10x9x8 = 720 possible processing-stage combinations. I'm guessing the number of valid combinations is much smaller, like maybe 30, in which case you could represent the combinations in an enum:

enum ProcessingStage {
    CHASSIS_ASSEMBLY,
    CHASSIS_VERIFICATION_AND_DASHBOARD_ASSEMBLY,
    DASHBOARD_ASSEMBLY,
    PAINTING,...
}

Otherwise instead of having a single processing-stage, you could store them in a list. Don't let the client modify the list directly. Instead, provide methods that check the validity of the processing-stage combinations.

private final List<ProcessingStage> procStages = new ArrayList<>();
private static final int MAX_STAGES = 3;

public synchronized void addStage(ProcessingStage ps) throws Exception {
    if (procStages.size() >= 3) {
        throw new Exception("Can't have more than 3 simultaneous stages");
    }
    if (procStages.size() < 1) {
        procStages.add(ps);
    } else {
        for (ProcessingStage testPs : procStages) {
            // Maybe you could start painting while still assembling,
            // but can't start assembling while you are painting.
            if ( !ps.isCompatibleWith(testPs) ) {
                throw new Exception("New stage " + ps +
                                    " is incompatible with an existing stage: " +
                                    testPs);
            }
        }
    }
}

public synchronized void removeStage(ProcessingStage ps) throws Exception {
    if (procStages.size() < 1) {
        throw new Exception("Can't remove a stage when there are none");
    }
    procStages.remove(ps);
}

/**
 In Java, the client cannot modify this list, but it still tracks
 the list used internally by this object (shares the underlying
 list) so that every time the client looks at an old list that they
 get from this method, it could be different.  Weird, but that's
 Java.
*/
public List<ProcessingStage> stageListView() {
    return Collections.unmodifiableList(procStages);
}

I synchronized the methods so that stage transitions would be atomic and made them throw exceptions so that the caller has to handle what happens if another caller changes the state underneath them.

It occurs to me that you might want to look at Josh Bloch's Item 33: "Use EnumMap instead of ordinal indexing" (from Effective Java p. 161). Regardless of the title and the language, he has a nice example of modeling states: SOLID, LIQUID, GAS and valid transitions: FREEZE(LIQUID, SOLID), MELT(SOLID, LIQUID)... Then he creates a map of valid transitions.

1
  • I'll definitely check that FREEZE(LIQUID,SOLID), etc out. That seems like a good way to handle the transitions. And you are right, the number of possible transitions is doable as an Enum. May 24, 2013 at 22:43
1

The Object can be in one of many physical locations, while it can also be in one of several stages of processing ...

This got me thinking about the Bridge Pattern. For structure.

(The object might move back and forth between locations, or back and forth between the stages of processing).

This got me thinking about the Visitor Pattern. For behavior.

State

In general an object's state is the sum-total of the current values of all its properties. So I'm assuming location, stage, and object can all have their own self-contained state. Further, the combination of location and stage state for a given object gives us the 10 possible states you refer to.

Code Sketch

  1. Look at the overall class, interface definitions. Don't get hung up on method names and details.
  2. abstract class vis-a-vis interface - use either or both as needed. This is not a discussion of "code to interfaces not implementation"

  3. If these 10 states can be described succinctly perhaps an enum defining them is in order.

  4. If state is as I describe above then object holding location and stage references is notionally the same as an enum, but very fungable.
  5. If concrete location and stage vary then each should have a base.
  6. If and object's location & stage can both vary independently, and object state should change when these do, thus we utilize bridge pattern.
  7. If a location (or stage) changes that new location can/must calculate its own state, and possibly directly affect object's other properties (and/or vice versa!), thus we have the visitor pattern. i.e. concrete location (or stage) driven state change.

  8. Not shown well here, but I'm thinking.. in general return new copies of objects rather than directly changing properties of the current object,location,stage. then you can utilize a "functional" programming style; and can keep the obsolete objects as state history.

    public class Object {
        public Location currentLocation;
        public Stage currentProcess;
        // other properties
    }

    public abstract class Location {
        public  DoYourThing();
        public Object myObject;  // this could be handy
        // other properties
    }

    public abstract class Stage {
        public Object myObject;  // this could be handy
        //other properties

        public  DoA();
        public  DoB();
    }

    // Smart enough to calculate Location state relative to concrete Object & Object.currentStage
    public abstract class LocationVisitor {
        public  CalcNewState(Object obj) {
            obj.Location.DoYourThing();
            . . .
        };
    }

    // Smart enough to calculate Stage state relative to concrete Object & Object.currentLocation
    public abstract class StageVisitor {
        // base implementation stuff
        // if certain things must be in a specific order, let's say:

        public  DoStageing(Object obj) {
            obj.currentStage.DoB();
            DoExtraStuffDefinedinVisitor();
            obj.currentStage.DoA();
            // and so on
        }
    }

    public abstract ObjectStateMachine {
        protected Object            theObject;
        protected LocationVisitor   locVisitor;
        protected StageVisitor      stageVisitor;

        // constructors to fix properties above, and/or
        // public properties to set each individually at will
        . . .

        public Object CalcNewState() {
            var locationState   = locVisitor.CalcNewState(theObject);
            var stageState      = stageVisitor.DoStaging(theObject);
            var allEncompassingState = CalcNewState (locationState, stageState);
        }

        public  CalcNewState (locationThing, stageThing) {
            // magic happens...
        }
    }

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.