Composizione di progettazione dello stato del sistema

3

Considera il seguente codice Java:

public class HumanStateImpl implements HumanState {
    private boolean alive;
    private Color skinColor;

    // getters and setters, other state fields

    @Override
    public boolean isAlive() {
        return alive;
    }
}

Ora immagina di voler avere una classe che rappresenti HandState e una corrispondente HandController . È meglio iniettare separatamente gli oggetti di stato di ogni livello di stato, o comporli in un unico oggetto di stato più specifico? Se dipende, da cosa dipende?

Per chiarire cosa intendo, ecco un'implementazione di entrambi i lati, rispettivamente:

public class HandState {
    private final Side handSide;
    private boolean closed;
}

public class HandController {
    private final HumanState humanState;
    private final HandState handState;

    public HandController(HumanState humanState, HandState handState) {
        this.humanState = humanState;
        this.handState = handState;
    }
}

Versus:

public class HandState implements HumanState {
    private final HumanState outerState;
    private final Side handSide;

    public HandState(HumanState outerState, Side handSide) {
        this.outerState = outerState;
        this.handSide = handSide;
    }

    @Override
    public boolean isAlive() {
        return outerState.isAlive();
    }
}

public class HandController {
    private final HandState handState;

    public HandController(HandState handState) {
        this.handState = handState;
    }
}
    
posta durron597 03.07.2014 - 21:39
fonte

2 risposte

3

tl; dr - Penso che vorrai iniettare oggetti di stato a ogni livello di stato, a seconda dei casi.

Essendo la ragione, mentre l'applicazione si ridimensiona e si aggiungono altri controller, si vorrà controllare quali controller dialogano con altri. E il primo approccio che hai suggerito ti consentirà di supportarlo.

Per aiutare a illustrare le cose, estrapola il tuo esempio un po 'più avanti.

Abbiamo un Human che ha deciso di andare a fare shopping (ovvero "andato al mercato"). Per rendere le cose divertenti, andremo in uno di quei super-pazzi bazar del mercato aperto in cui persone esperte possono partecipare sia ad acquistare che vendendo mentre passeggiano.

Mentre sono al mercato, Eyes sta prendendo in tutte le voci disponibili per l'acquisto o il consumo. Sono costantemente alla ricerca di offerte - qualsiasi situazione in cui potresti essere in grado di acquistare o vendere e fare soldi.

Il Brain tiene traccia dei prezzi che vede così come tutti i beni che sono stati acquistati finora.

Il Brain è abbastanza intelligente da sapere che deve scaricare parte del suo lavoro. Quindi fa affidamento sul Wrists per determinare se un particolare accordo è buono o no.

Ma anche Wrists sa di avere troppo da fare, quindi si affidano a Fingers per determinare quanto deve essere raccolto o offerto in vendita. Nel linguaggio del mercato azionario, questo sarebbe simile a determinare lo spread.

Infine, avremo bisogno di una sorta di Basket per portare l'inventario.

Un flusso di dati di esempio potrebbe essere:

Eyes --> Brain <--> Wrists <--> Fingers       Basket

In altri domini, potremmo volere un grafico non lineare, ma penso che in questo caso non lo fai. Con solo 5 elementi, abbiamo un enorme numero di percorsi di comunicazione se ogni componente può parlare con ogni altro componente.

Parte di ciò che rende questo modello gestibile è quando si riducono quei numeri di interazioni. Ad esempio, Eyes non dovrebbe sapere veramente cosa c'è in Basket e assolutamente non dovrebbe essere responsabile per prendere decisioni di acquisto o vendita. È lo stesso ragionamento dietro al colloquiale "i tuoi occhi erano più grandi del tuo stomaco" quando qualcuno è super affamato e ha un pasto.

E nota che Basket è disattivato da solo. Il Brain potrebbe dover chiedere a Basket di vedere se un elemento è presente o di vedere quanti soldi sono disponibili 1 . Allo stesso modo, Fingers potrebbe dover accedere a Basket per posizionare o rimuovere elementi nell'inventario di Basket .

1 Se Basket non dovrebbe sapere quanti soldi sono disponibili, quindi crea solo Wallet o oggetto simile.

Tornare allo stato di iniezione ...

Il Brain dovrà inserire una misura di stato in Wrists . Il Brain deve dire quello che pensa sia il prezzo corrente di mercato e potenzialmente quello che è già nel carrello. Il Wrists può assumere che se Brain li ha chiamati che il mercato è aperto.

Ma Brain non dovrebbe parlare direttamente con Fingers dato che in realtà non parlano delle stesse cose. Il Fingers è lì per determinare quanto prelevare o posizionare in base a ciò che il Wrists ha detto loro. Se il Fingers ha bisogno di un'ulteriore approvazione prima di finalizzare la decisione, allora l'approvazione dovrebbe essere indirizzata a Wrists e quindi a Brain .

Controllando quali oggetti parlano agli altri, limiti la quantità di stato che deve essere iniettata nel tuo modello.

Se osservi l'alternativa che hai suggerito, avere un singolo oggetto di stato potrebbe facilmente sfuggire di mano abbastanza rapidamente. E confonde le acque da quando stai iniettando o esponendo gli stati a controllori che semplicemente non si preoccupano di quei particolari stati. Ad esempio, il Fingers non interessa se il mercato è aperto, si limita a calcolare quanto prelevare.

Allo stesso modo, estendiamo davvero il tuo esempio e diciamo che stai modellando un essere umano con più armi in modo da poter prendere decisioni multiple (acquistare | vendere) allo stesso tempo. Pensa a quanto dovrebbe essere esteso un controller a stato singolo tenendo traccia di 10 insiemi di Wrists e di 10 insiemi di Fingers .

Quindi riassumendo tutto quanto sopra: vuoi iniettare oggetti di stato a ogni livello di stato come appropriato, il che significa che limiti anche quali controller possono parlare con quali altri controller.

    
risposta data 04.07.2014 - 16:49
fonte
1

Raccomando di utilizzare la composizione anziché l'ereditarietà in questo scenario. Inoltre, avrei invertito il modello per adattarlo al flusso logico di controllo.

Un HumanState può contenere variabili membro per HandState s. In questo modo, hai una composizione logica di elementi.

HumanState ->
--> contains HandState x2
--> contains HeadState x1
    --> contains MouthState x1

e così via.

Ora i controller possono anche seguire lo stesso schema. Il HumanController contiene istanze di HandController s. Questo schema combacia anche con le ipotesi intuitive: un umano controlla le proprie mani, ecc.

HumanController ->
--> contains HandController x2
--> contains HeadController x1
    --> contains MouthController x1
    
risposta data 04.07.2014 - 01:42
fonte

Leggi altre domande sui tag