Passare un oggetto tra diversi gestori o una Superclasse che contiene gran parte della logica

5

Cercherò di renderlo il più conciso e concreto possibile, ma mi scuso perché posso pensare a più modi per farlo funzionare. Questa domanda potrebbe riguardare anche la gestione dei flussi di lavoro di produzione o di produzione, ma non ho esperienza in questi settori.

Ho un problema in cui ho bisogno di spostare un oggetto, O, da stati diversi ad altri stati (come un automa finito non deterministico). Il problema è che l'oggetto può essere in diversi stati allo stesso tempo e l'interazione a volte conta.

Ad esempio, diciamo che ci sono 10 stati, O può essere in un massimo di 3 di questi stati allo stesso tempo. Sono un po 'perso su quale tipo di progettazione del codice dovrei usare per modellarlo. Le due idee a cui sto pensando sono

  1. Passa O dal gestore al gestore e gestisci le transizioni tra gli stati e mantieni una sorta di informazioni in O dicendo in che stato sono, in modo che ogni stato possa capire cosa fare in base a quali altri stati O è in.

  2. Avere una Superclasse più grande che gestisce gli stati in cui si trova O e gestisce le interazioni.

Normalmente direi 1. ma gestire tutte le interazioni tra i gestori di ciascun gestore suona disordinato.

Quale sarebbe l'opzione migliore tra questi 2? O c'è un altro metodo che è preferito?

** Aggiornamento e chiarimento **

Come esempio concreto. L'oggetto può trovarsi in una delle molte posizioni fisiche, mentre può anche essere in una delle diverse fasi di elaborazione (l'oggetto potrebbe spostarsi avanti e indietro tra le posizioni o avanti e indietro tra le fasi di elaborazione). Come complessità aggiuntiva ogni posizione fisica potrebbe avere delle sublocazioni, ma penso di poterlo ignorare per ora. Quindi, se è più chiaro, può essere considerato come due automi indipendenti, ma la scelta del bordo per passare allo stato successivo in entrambi gli automi dipende dallo stato di entrambi gli automi.

    
posta greedybuddha 19.05.2013 - 05:44
fonte

6 risposte

3

I tuoi stati diversi suonano più come attività. Se fossero stati si userebbe Nouns per descriverli (esempio: "completo", "vuoto", "scatola", "contenitore"). Se fossero attività, dovresti usare Adjectives per descriverle (esempio: "apertura", "elaborazione", "creazione").

Il tuo oggetto può trovarsi in una determinata posizione (ad esempio uno stato), ma sta facendo molte cose contemporaneamente (ad esempio attività). Pertanto, si dispone di una raccolta interna di attività che l'oggetto contiene, ma potrebbe non essere responsabile della logica di tali attività. Ogni attività potrebbe implementare quella logica internamente.

Creo un nuovo oggetto per rappresentare la fabbrica, che contiene l'oggetto in luoghi diversi (ad esempio il loro stato). L'oggetto factory aggiungere / rimuovere attività agli oggetti quando quelle attività segnalano che sono finiti o meno.

Ecco un motto

 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
 }

Creerai nuovi oggetti che implementano l'interfaccia Activity , li aggiungerai a MyObject quando quell'oggetto ha bisogno di eseguire quell'attività, o cambia la sua posizione. Per trovare oggetti e gestirli usa l'oggetto Factory .

Modifica

Ho aggiunto la classe ActivityContext come oggetto dati. Questi oggetti vengono creati e passati a Activity quando funziona. Ogni Activity può aggiornare i dati di contesto in modo che altre attività possano ricevere quei dati quando funzionano. Ciò consente alle attività di essere consapevoli di ciò che le altre attività stanno facendo senza fare riferimento diretto a tali attività.

    
risposta data 22.05.2013 - 05:20
fonte
1

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

Sembra un'enorme logica da ciò che stai descrivendo. Vuoi che la stessa classe definisca che il 30% delle parti complete ad Atlanta devono essere spedite a Dallas, poiché la classe che gestisce il 99% delle parti complete nella parte est di Dallas può essere spostata a finire nella stessa 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.

Penso che sarebbe gestibile se offri un modo per ignorare gli stati che non ti interessano. Se il passaggio 2 dell'assembly è sempre seguito dal passaggio 3, indipendentemente dalla posizione, si desidera poter tornare "vai al passaggio 3" senza preoccuparsi della fabbrica in cui ci si trova.

Ad esempio potresti creare una classe wrapper che consenta transizioni parziali. Se stai andando sulla rotta immutabile, le funzioni potrebbero restituire il risultato della funzione di transizione parziale, cioè

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

Oppure potresti seguire la rotta mutabile e trasformare lo stato con qualsiasi aggiornamento.

    
risposta data 21.05.2013 - 23:23
fonte
1

Se comprendo correttamente la tua domanda, potresti progettare il tuo codice in modo simile allo schema seguente:

  • Una classe StateObjectContainer che contiene riferimenti allo stato corrente, all'oggetto e allo stato manager. Questa classe è responsabile dei metadati (i riferimenti) associati all'oggetto contenuto.
  • Una classe State con una raccolta di StateObjectContainers. Ha anche la possibilità di aggiungere oggetti allo stato corrente.
  • Una classe di StateManager che ha riferimenti a tutte le istanze di stato ed espone funzionalità per interrogarle per ottenere informazioni.
  • Una classe StateDecider che determina i successivi stati logici dati un oggetto e un istantaneo di tutti gli stati.
  • Un evento o callback chiamato quando un oggetto è pronto a cambiare stato e gli viene assegnato un argomento come argomento.

Quando un oggetto è pronto a cambiare stato:

  1. Ottieni un'istantanea di tutti gli stati correnti
  2. Calcola gli stati logici successivi
  3. Aggiungi l'oggetto a ciascuno degli stati calcolati.
risposta data 22.05.2013 - 04:44
fonte
1

Quello che ho capito dal tuo esempio è che il tuo oggetto può essere rappresentato da due macchine a stati paralleli (posizione e stage), e devi sincronizzare quegli automi.

Potresti trarre ispirazione da Statecharts, ad esempio, che consente a macchine di stato gerarchiche con comunicazione tra gli stati, tra le altre cose, ma probabilmente è eccessivo per il tuo utilizzo.

Personalmente, proverei a usare qualcosa come le reti di Petri. Fondamentalmente, puoi modellare facilmente transizioni fork / join ("rendez-vous") tra sottografi separati. Leggi ad esempio L'applicazione delle reti di Petri alla gestione del flusso di lavoro o questo articolo (che tratta di un altro argomento ma ha delle belle spiegazioni).

Quindi, il tuo stato iniziale verrà biforcato in percorsi paralleli diversi (posizione, stadio, ..) in cui ogni "token" rappresenterebbe lo stato corrente dell'oggetto per le diverse variabili. Alcuni o tutti quei token verranno quindi utilizzati congiuntamente per attivare transizioni specifiche.

    
risposta data 22.05.2013 - 14:53
fonte
1

Sembra che il tuo oggetto abbia bisogno di un campo di posizione (e forse di una classe di posizione o di un'interfaccia per garantire valori validi) e un secondo campo per la fase di elaborazione che potrebbe probabilmente essere rappresentato da una enumerazione delle fasi possibili.

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

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

Sicuramente c'è un limite al numero di combinazioni di fasi di elaborazione. Anche se ci sono 10 fasi di elaborazione possibili, e può essere in 3 di esse contemporaneamente (sebbene non siano le stesse 3 su tutta la linea), ciò è 10x9x8 = 720 possibili combinazioni di fasi di elaborazione. Immagino che il numero di combinazioni valide sia molto più piccolo, come forse 30, nel qual caso potresti rappresentare le combinazioni in un enum:

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

Altrimenti, invece di avere una singola fase di elaborazione, è possibile memorizzarli in una lista. Non lasciare che il client modifichi la lista direttamente. Invece, fornire metodi che controllano la validità delle combinazioni dello stage-stage.

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

Ho sincronizzato i metodi in modo che le transizioni di stage fossero atomiche e le avessero generate eccezioni in modo che il chiamante debba gestire ciò che accade se un altro chiamante cambia lo stato sottostante.

Mi viene in mente che potresti voler dare un'occhiata all'articolo 33 di Josh Bloch: "Usa EnumMap invece dell'indicizzazione ordinale" (da Effective Java p.116). A prescindere dal titolo e dalla lingua, ha un bell'esempio di stati di modellazione: SOLID, LIQUID, GAS e transizioni valide: FREEZE (LIQUID, SOLID), MELT (SOLID, LIQUID) ... Quindi crea una mappa di transizioni valide .

    
risposta data 22.05.2013 - 04:52
fonte
1

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

Questo mi ha fatto pensare al Pattern del ponte . Per struttura.

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

Questo mi ha fatto pensare al Pattern visitatori . Per comportamento.

Stato

In generale lo stato di un oggetto è la somma totale dei valori correnti di tutte le sue proprietà. Quindi presumo che location , stage e object possano avere tutti il loro stato autonomo. Inoltre, la combinazione di location e stage state per un dato object ci fornisce i 10 possibili stati a cui ti riferisci.

Schema di codice

  1. Guarda la classe generale, le definizioni dell'interfaccia. Non rimanere impigliato nei nomi e nei dettagli dei metodi.
  2. abstract class vis-a-vis interface - utilizza uno o entrambi come necessario. Questa non è una discussione di " codice alle interfacce non implementazione "

  3. Se questi 10 stati possono essere descritti in modo succinto, forse un enum che li definisce è in ordine.

  4. Se state è come descritto sopra allora object holding location e stage riferimenti è nozionalmente uguale a enum , ma molto fungibile .
  5. Se il calcestruzzo location e stage variano, allora ognuno dovrebbe avere un base .
  6. If e object 's location & stage può entrambi variare in modo indipendente, e lo stato di object dovrebbe cambiare quando lo fanno, quindi utilizziamo bridge pattern .
  7. Se una location (o stage ) cambia quella nuova posizione può / deve calcolare il proprio stato, e possibilmente influenzare direttamente le altre proprietà di object (e / o viceversa!), quindi abbiamo il visitor pattern . Ovvero cambiamento di stato guidato concreto location (o stage ).

  8. Non mostrato bene qui, ma sto pensando .. in generale restituisco nuove copie di oggetti invece di cambiare direttamente le proprietà del corrente object , location , stage . allora puoi utilizzare uno stile di programmazione "funzionale"; e può mantenere gli oggetti obsoleti come cronologia dello stato.

    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...
        }
    }
    
risposta data 22.05.2013 - 22:22
fonte

Leggi altre domande sui tag