Combinazione del metodo del modello con la strategia


14

Un compito nella mia classe di ingegneria del software è quello di progettare un'applicazione che può giocare in diverse forme in un determinato gioco. Il gioco in questione è Mancala, alcuni di questi giochi sono chiamati Wari o Kalah. Questi giochi differiscono per alcuni aspetti, ma per la mia domanda è importante solo sapere che i giochi potrebbero differire nei seguenti:

  • Il modo in cui viene gestito il risultato di una mossa
  • Il modo in cui viene determinata la fine del gioco
  • Il modo in cui viene determinato il vincitore

La prima cosa che mi è venuta in mente per progettare questo è stato usare il modello di strategia, ho una variazione negli algoritmi (le regole reali del gioco). Il design potrebbe apparire così: inserisci qui la descrizione dell'immagine

Ho quindi pensato a me stesso che nel gioco di Mancala e Wari il modo in cui il vincitore è determinato è esattamente lo stesso e il codice sarebbe duplicato. Non penso che questa sia, per definizione, una violazione dell '"una regola, un posto" o del principio DRY visto che un cambiamento nelle regole per Mancala non significherebbe automaticamente che anche quella regola dovrebbe essere cambiata in Wari. Tuttavia dal feedback che ho ricevuto dal mio professore ho avuto l'impressione di trovare un design diverso.

Poi ho pensato a questo: inserisci qui la descrizione dell'immagine

Ogni gioco (Mancala, Wari, Kalah, ...) avrebbe solo un attributo del tipo di interfaccia di ogni regola, vale a dire WinnerDeterminerse c'è una versione Mancala 2.0 che è la stessa di Mancala 1.0, tranne per il modo in cui il vincitore è determinato, può solo usa le versioni di Mancala.

Penso che l'implementazione di queste regole come modello strategico sia certamente valida. Ma il vero problema arriva quando voglio progettarlo ulteriormente.

Nel leggere il modello di metodo del modello ho immediatamente pensato che potesse essere applicato a questo problema. Le azioni eseguite quando un utente effettua una mossa sono sempre le stesse e nello stesso ordine, vale a dire:

  • depositare pietre nei buchi (questo è lo stesso per tutti i giochi, quindi sarebbe implementato nel metodo modello stesso)
  • determinare il risultato della mossa
  • determinare se il gioco è terminato a causa della mossa precedente
  • se il gioco è finito, determinare chi ha vinto

Questi tre ultimi passaggi sono tutti nel mio modello di strategia descritto sopra. Ho molti problemi a combinare questi due. Una possibile soluzione che ho trovato sarebbe quella di abbandonare il modello di strategia e fare quanto segue: inserisci qui la descrizione dell'immagine

Non vedo davvero la differenza progettuale tra il modello di strategia e questo? Ma sono certo di dover utilizzare un metodo modello (anche se ero altrettanto sicuro di dover utilizzare un modello di strategia).

Inoltre, non riesco a determinare chi sarebbe responsabile della creazione TurnTemplatedell'oggetto, mentre con il modello di strategia sento di avere famiglie di oggetti (le tre regole) che potrei facilmente creare usando un modello di fabbrica astratto. Avrei quindi un MancalaRuleFactory, WariRuleFactoryecc. E avrebbero creato le istanze corrette delle regole e mi avrebbero restituito un RuleSetoggetto.

Diciamo che uso la strategia + modello astratto di fabbrica e ho un RuleSetoggetto che ha algoritmi per le tre regole in esso. L'unico modo in cui sento di poter ancora utilizzare il modello di metodo del modello con questo è passare questo RuleSetoggetto al mio TurnTemplate. Il "problema" che poi emerge è che non avrei mai avuto bisogno delle mie concrete implementazioni di TurnTemplatequeste classi diventerebbero obsolete. Nei miei metodi protetti nel TurnTemplatepotrei solo chiamare ruleSet.determineWinner(). Di conseguenza, la TurnTemplateclasse non sarebbe più astratta ma dovrebbe diventare concreta, è ancora un modello di modello?

Per riassumere, sto pensando nel modo giusto o mi sto perdendo qualcosa di facile? Se sono sulla buona strada, come posso combinare un modello di strategia e un modello di metodo modello?


1
Direi che il tuo pensiero è praticamente perfetto. Se il feedback che ricevi dal tuo professore non lo convalida, chiedi a lui / lei di evidenziare i difetti o di dare un suggerimento su ciò / i che sta cercando nella risposta. Smetti di provare a leggere le menti. Supporre cose su ciò che vogliono il tuo professore (e in seguito i tuoi utenti) è forse il peggio che puoi fare.
Marjan Venema,

" ... nel gioco di Mancala e Wari il modo in cui viene determinato il vincitore è esattamente lo stesso e il codice sarebbe duplicato. " Perché questo implica che il codice è duplicato? Lascia che entrambe le classi chiamino la stessa funzione.
D Drmmr,

Risposte:


6

Dopo aver esaminato i tuoi progetti, sia la prima che la terza iterazione sembrano essere disegni più eleganti. Tuttavia, dici che sei uno studente e il tuo professore ti ha dato un feedback. Senza sapere esattamente quale sia il tuo compito o lo scopo della lezione o maggiori informazioni su ciò che il tuo professore ha suggerito, prenderei qualsiasi cosa dico sotto con un granello di sale.

Nel tuo primo progetto, dichiari RuleInterfacedi essere un'interfaccia che definisce come gestire il turno di ogni giocatore, come determinare se la partita è finita e come determinare un vincitore al termine della partita. Sembra che sia un'interfaccia valida per una famiglia di giochi che subisce variazioni. Tuttavia, a seconda dei giochi, potresti avere un codice duplicato. Concordo sul fatto che la flessibilità di modificare le regole di un gioco sia una buona cosa, ma direi anche che la duplicazione del codice è terribile per i difetti. Se copi / incolli il codice difettoso tra le implementazioni e uno contiene un bug, ora hai più bug che devono essere corretti in posizioni diverse. Se si riscrivono le implementazioni in momenti diversi, è possibile introdurre difetti in posizioni diverse. Nessuno di questi è desiderabile.

Il tuo secondo disegno sembra piuttosto complesso, con un albero ereditario profondo. Almeno, è più profondo di quanto mi aspetterei per risolvere questo tipo di problema. Stai anche iniziando a suddividere i dettagli di implementazione in altre classi. In definitiva, stai modellando e implementando un gioco. Questo potrebbe essere un approccio interessante se ti fosse richiesto di mescolare e abbinare le tue regole per determinare i risultati di una mossa, la fine del gioco e un vincitore, che non sembra essere nei requisiti che hai menzionato . I tuoi giochi sono insiemi di regole ben definiti e proverei a incapsulare i giochi il più possibile in entità separate.

Il tuo terzo design è quello che mi piace di più. La mia unica preoccupazione è che non sia al giusto livello di astrazione. In questo momento, sembra che stia modellando una svolta. Consiglierei di prendere in considerazione l'idea di progettare il gioco. Considera che hai giocatori che stanno facendo delle mosse su una specie di tavola, usando le pietre. Il tuo gioco richiede la presenza di questi attori. Da lì, il tuo algoritmo non è doTurn()ma playGame(), che passa dalla mossa iniziale alla mossa finale, dopo di che termina. Dopo ogni mossa di ogni giocatore, regola lo stato del gioco, determina se il gioco è in uno stato finale e, se lo è, determina il vincitore.

Consiglierei di dare un'occhiata più da vicino al tuo primo e terzo progetto e di lavorare con loro. Potrebbe anche aiutare a pensare in termini di prototipi. Come sarebbero i client che usano queste interfacce? Un approccio progettuale ha più senso per l'implementazione di un client che sta per creare un'istanza di un gioco e giocare? Devi capire con cosa interagisce. Nel tuo caso particolare, è la Gameclasse e tutti gli altri elementi associati: non puoi progettare isolatamente.


Dato che dici che sei uno studente, mi piacerebbe condividere alcune cose di un tempo in cui ero il TA per un corso di progettazione software:

  • I motivi sono semplicemente un modo per catturare cose che hanno funzionato in passato, ma astrattandole al punto da poterle utilizzare in altri disegni. Ogni catalogo di modelli di design dà un nome a un modello, spiega le sue intenzioni e dove può essere utilizzato e le situazioni in cui alla fine vincolerebbe il tuo design.
  • Il design arriva con esperienza. Il modo migliore per diventare bravi nella progettazione non è semplicemente quello di concentrarsi sugli aspetti della modellazione, ma realizzare ciò che va nella realizzazione di quel modello. Il design più elegante è utile se non può essere facilmente implementato o non si adatta al design più ampio del sistema o con altri sistemi.
  • Pochissimi disegni sono "giusti" o "sbagliati". Finché il design soddisfa i requisiti del sistema, non può essere sbagliato. Una volta che c'è una mappatura da ciascun requisito in una rappresentazione di come il sistema soddisferà tale requisito, la progettazione non può essere sbagliata. È solo qualitativo a questo punto su concetti come flessibilità o riusabilità o testabilità o manutenibilità.

Grazie per l'ottimo consiglio, in particolare il tuo punto sul fatto che era al livello sbagliato di astrazione. L'ho cambiato ora in uno GameTemplateche sembra molto meglio. Mi permette anche di combinare un metodo di fabbrica per inizializzare i giocatori, il tabellone, ecc.
Mekswoll,

3

La tua confusione è giustificata. Il fatto è che i modelli non si escludono a vicenda.

Il metodo modello è la base per una serie di altri modelli, come strategia e stato. In sostanza, l'interfaccia della strategia contiene uno o più metodi modello, ognuno dei quali richiede che tutti gli oggetti che implementano una strategia abbiano (almeno) qualcosa come un metodo doAction (). Ciò consente di sostituire le strategie l'una con l'altra.

In Java, un'interfaccia non è altro che un insieme di metodi modello. Allo stesso modo, qualsiasi metodo astratto è essenzialmente un metodo modello. Questo modello (tra gli altri) era ben noto ai progettisti del linguaggio, quindi lo hanno incorporato.

@ThomasOwens offre ottimi consigli su come affrontare il tuo problema specifico.


0

Se vieni distratto da schemi di design, il mio consiglio per te sarebbe di prototipare prima il gioco, quindi gli schemi dovrebbero saltarti fuori. Non penso che sia davvero possibile o consigliabile provare a progettare un sistema perfettamente prima e poi implementarlo (in modo simile lo trovo sconcertante quando le persone provano prima a scrivere interi programmi e poi a compilarli, piuttosto che farlo a poco a poco .) Il problema è che è improbabile che tu pensi a ogni scenario che la tua logica dovrà affrontare, e durante la fase di implementazione perderai ogni speranza o proverai a rimanere fedele al tuo progetto originale imperfetto e introdurre hack, o anche peggio non consegnare nulla.


2
Mentre in generale sono d'accordo con te , questa non è davvero una risposta che risolva il problema del PO. "Non farlo" è una risposta, ma piuttosto scadente se non fornisce un'alternativa praticabile.
yannis,

@YannisRizos E anche se sono d'accordo con te , penso ancora che abbia dato un buon consiglio (o un'intuizione se consiglio non è la parola giusta). Per questo motivo, +1. Anche se, sì, tecnicamente non è una risposta molto utile.
Ha acquistato il

-1

Passiamo alle puntine di ottone. Non c'è assolutamente bisogno di alcuna interfaccia di gioco, di schemi di progettazione, di classi astratte e di UML.

Se hai un numero ragionevole di classi di supporto, come l'interfaccia utente, la simulazione e quant'altro, in pratica tutto il tuo codice specifico per la logica non di gioco viene riutilizzato comunque. Inoltre, il tuo utente non cambia il suo gioco in modo dinamico. Non capovolgi a 30Hz tra i giochi. Giochi una partita per circa mezz'ora. Quindi il tuo polimorfismo "dinamico" non è in realtà affatto dinamico. È piuttosto statico.

Quindi il modo sensato per andare qui è usare un'astrazione funzionale generica, come C # Actiono C ++ std::function, creare una classe Mancala, una Wari e una Kalah e andare da lì.

std::unordered_map<std::string, std::function<void()>> games = {
    { "Kalah", [] { return Kalah(); } },
    { "Mancala", [] { return Mancala(); } },
    { "Wari", [] { return Wari(); } }
};
void play() {
    std::function<void()> f;
    f = games[GetChoiceFromUI()];
    f();
    if (PlayAgain()) return play();
}
int main() {
    play();
}

Fatto.

Non chiami Giochi. I giochi ti chiamano.


3
Non vedo come sia utile. La persona che pone questa domanda è uno studente. Chiede esplicitamente i principi di progettazione e le interazioni tra due modelli di progettazione. Dire che non c'è bisogno di modelli di progettazione o UML potrebbe essere vero in alcuni casi nel settore, ma pensare a modelli di progettazione, modelli di progettazione e UML potrebbe essere il punto dell'esercizio richiesto.
Thomas Owens

3
Non c'è niente da dire su di loro, perché non c'è nessun posto in cui si applicano. Non ha senso passare il tempo a pensare a come progettare completamente usando i modelli di progettazione, a meno che tu non intenda essere una lezione su come non usarli.
DeadMG

4
C'è una tendenza preoccupante / fastidiosa in SoftDev in cui i modelli di progettazione utilizzati sono considerati più importanti della semplice risoluzione del problema. C'è qualcosa come l'ingegneria eccessiva.
James,

2
@DeadMG: mentre entrambi avete sostanzialmente ragione, nel caso di OP il problema sta superando l'esame e la soluzione a questo problema è quella di utilizzare modelli di progettazione e UML. Non importa quanto abbiamo ragione, se il suo professore non è d'accordo con le sue soluzioni, non passerà.
Goran Jovic,

2
Per tutta la vita ho sempre pensato che fosse una "tassa sull'ottone" ... Wow, "puntine d'ottone" ha molto più senso. lol
acquistato777
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.