Questa implementazione dello schema di stato ha senso?

6

Ho il compito di implementare lo schema di stato in una delle mie classi. Il compito faceva riferimento a un'implementazione suggerita, e dopo aver esaminato tale implementazione ero confuso poiché ritenevo che fosse privo di senso per la maggior parte delle applicazioni e francamente semplicemente negativo dal punto di vista del design, dal momento che elimina completamente alcuni principi di base del codice software.

Questo è il diagramma di classe dell'implementazione suggerita:

Michiedo:acheservequestaimplementazione?Esso(imo)violachiaramenteidueprincipidiprogettazioneprincipalidell'ingegneriadelsoftware:Renderelecosefacilmentemanutenibiliefacilmenteestensibili.

Aimieiocchi,questaimplementazionenonhaun'estensionefacile(devimodificarel'originedialmenodueclassi,adesempio,perimplementareunnuovostatochetiseidimenticatoquandolohaiimplementatoperlaprimavolta).L'accoppiamentotraquestocodiceeilcodiceclientesembraessereenorme.Serimuoviqualcosainquestomodello,tuttoilcodiceclientsiinterromperà.Sevuoiaggiungerequalcosa,devimodificaretuttoilcodiceclienteperadattare,adesempio,unnuovostato,perchéinuovistatipossonoessereimmessisolotramiteilmetodoenter()dellostatoconcreto,oindirizzandoliainiettarliinilcontrollercheutilizzailmetodochangeState().

Comebonus,puoiaveresolounaportadelgarageintuttoilmondo,dalmomentocheglistatisonopraticamentesingletons.Ilmetodoenter()neglistaticoncretièstatodatoinquestomodo:

publicstaticStateenter(Controllerc){if(m_instance==null)m_instance=new<<concreteStateName>>(c);returnm_instance;}

Oppurepuoiaverepiùportedelgarage,chesiapronotuttecontemporaneamenteseneapriuna.

PerchédovrestimetteretuttiimetodinellaclasseStateastratta?Dov'èilpolimorfismo?Perchéglistaticoncretihannoversionidimetodidilanciovuoteodieccezionichenonhannonullaachefareconillorostato(adesempio,l'interfacciarisultantediOpenavràunmetodo"lock ()", anche se non è possibile bloccare direttamente il garage porta quando è aperta. Devi prima chiuderla). Perché non scegliere un singolo metodo e implementarlo in modo diverso negli Stati concreti, rendendo la cosa facilmente estendibile di nuovo?

È anche (di nuovo, per quanto posso vedere) non realmente mantenibile senza un sacco di problemi. E se tu, dopo un paio di mesi di utilizzo del garage, decidi che uno stato non è solo obsoleto ma sbagliato? Dovrai portarlo fuori, e il metodo secondo in State () con esso, rompendo l'intero codice base che utilizza questa implementazione. Questa è una porta del garage con quattro stati. Non vorrei lavorare su quella cosa se avesse altri stati.

La situazione diventa più confusa quando guardo su Internet: il pattern sembra essere implementato esattamente in questo modo un sacco di volte (per esempio è implementato in questo modo nella nostra Wikipedia nazionale (tedesco)).

La domanda attuale: mi manca qualcosa o è semplicemente brutto? Dato che è così diffuso, penso che potrei mancare qualcosa di ovvio qui.

    
posta heishe 27.04.2012 - 22:33
fonte

2 risposte

5

un modo più semplice sarebbe avere i metodi dello stato che restituiscono lo stato dopo l'operazione e combinare tutti i metodi nell'estratto State in un generico procesInput(data,controller): state

in questo modo gli stati non hanno bisogno di sapere a quale controller sono collegati (permettendo loro di essere veri singleton)

e ora ogni stato deve solo sapere quali sono i prossimi stati possibili in base a quale input

    
risposta data 27.04.2012 - 22:47
fonte
1

Cerca di non rimanere coinvolto nei dettagli di UML. È estensibile se si presuppone che enter(Controller) sia una sorta di implementazione dell'interfaccia e che i metodi concreti non siano necessari per implementare tutti i metodi della superclasse astratta. L'obiettivo del diagramma è di illustrare l'essenza del modello.

Il diagramma da solo non riesce a trasmettere questo, ma il payoff è il semplice fatto che l'istanza Controller non ha bisogno di tenere traccia dello stato per prendere decisioni corrette. Ad esempio, il controller può richiamare lock() in qualsiasi momento sull'oggetto m_state astratto senza timore di bloccare a doppio battito la porta o bloccare la porta in uno stato aperto, perché Closed è l'unico concreto State oggetto che implementa lock() (polymorphy).

Questo è il motivo per cui tutti i metodi definiti nella superclasse astratta vengono implementati selettivamente nelle classi concrete. Si risparmia al controller il problema di scrivere il codice di convalida per modifiche di stato non valide come il doppio blocco di una porta.

La logica di commutazione di stato è contenuta nelle implementazioni di stato concreta. Se lo stato corrente è un'istanza Locked e viene chiamato unlock() , l'oggetto stato Locked chiamerebbe changeState(Closed) sull'oggetto Controller (in aggiunta agli altri metodi Controller che potrebbero essere applicati, ad esempio un controller teorico metodo setIndicatorLight(Color) ).

La chiave qui è che le implementazioni concrete State sono progettate per interagire l'una con l'altra. Diciamo che creo un'altra implementazione concreta State come Broken . Se nessuno degli altri stati ha effettivamente impostato lo stato Broken sul controller, allora lo stato potrebbe anche non esistere. Inoltre, logicamente non creeresti implementazioni concrete che non hanno portato ad altri stati.

    
risposta data 18.04.2014 - 19:13
fonte

Leggi altre domande sui tag