Come progettare un pattern composito in Python?

5

Il concetto

Sto programmando un'interfaccia su pygame come un progetto personale, per semplificare la creazione di giochi.

Finora sono riuscito a progettare un'architettura che si comporta in questo modo:

  • Gli oggetti sono componenti visualizzabili che possono apparire e spostarsi sullo schermo
  • Gli oggetti possono avere oggetti figli
  • Quando un oggetto viene visualizzato, chiede a tutti i suoi figli di visualizzarsi sulla superficie del genitore.
  • Gli oggetti hanno tre elementi importanti: un sistema di callback, un sistema grafico e un sistema fisico per agire, visualizzare e spostare rispettivamente.

Quindi, quando voglio creare una "scena" di gioco, creo un oggetto "radice" che contiene altri oggetti come il giocatore, il mouse, il terreno, i mostri ...

Quindi devo solo chiedere alla root di visualizzarsi, e ogni oggetto appare in modo ricorsivo.

L'ho progettato senza conoscere il modello composito all'inizio, solo le basi di OOP.

Il mio problema principale era rendere la proprietà di sostituibilità degli oggetti che proviene dall'ereditarietà per funzionare bene con la composizione ricorsiva che ho creato.

Voglio dire che ho una classe "astratta" chiamata "Object" (ho messo l'abstract tra virgolette perché Python non ha davvero un tale concetto) che è ereditato da classi come "Image" (per essere in grado di visualizzare) o "MovingObject" (per essere in grado di spostarsi). Qui, l'ereditarietà ha lo scopo di estendere le mie abilità oggettuali.

Ma il mio pattern composito richiede che "i gruppi di oggetti debbano essere considerati uguali ai singoli oggetti".

Quindi quando chiamo ricorsivamente un metodo da un oggetto, chiama quel metodo da ogni figlio dell'oggetto, indipendentemente dal fatto che alcuni di essi potrebbero non avere quel metodo.

Esempio

Ad esempio, utilizziamo questo elemento root:

  • root (Immagine)
    • player (MovingObject)
    • cloud (MovingObject)
    • background (immagine)
    • sole (immagine)

Ora supponiamo di voler chiamare il metodo move() sull'elemento root, per far muovere ogni bambino: in primo luogo, non possiamo perché root è un'istanza Image, quindi non conosce il metodo move() . Ma anche se fosse il caso, i bambini "sfondo" e "sole" non lo saprebbero.

Quindi ho deciso di mettere un metodo vuoto move() nella mia classe Object "astratta", quindi ogni oggetto lo sa, anche se non fa nulla.

Il problema è che la mia classe Object ora contiene metodi vuoti che non capisce e non ha bisogno, solo per consentire il comportamento ricorsivo.

Possibile soluzione

Poi ho sentito di tutto il trambusto dell'ereditarietà e della composizione e una delle soluzioni che mi è venuta in mente è stata quella di smettere di usare l'ereditarietà per le abilità Oggetto e usare invece la composizione. Ciò significa che vorrei creare, ad esempio, una classe "Body", una classe "Image" e una classe "Callback" che rappresentino azioni diverse, e quindi inserirle in un'istanza Object per "dotarla" e dargli più potenza .

Ma poi ho pensato che questo cambierebbe appena qualcosa, perché dovrò chiamare move() , e poi l'oggetto controllerà se ha il plug-in Body e lo userà. Ma richiede ancora il metodo move() per essere presente nella classe Object.

Domande

Quindi mi rivolgo a voi ragazzi per darmi consigli sul mio schema:

  • Ho capito bene come funziona il pattern composito?
  • Il mio approccio è corretto?
  • L'uso delle classi "plug-in" mi aiuterà?
  • Il comportamento ricorsivo è una buona idea?
  • Esistono altri modelli più adatti alle mie esigenze?

Spero che tu possa darmi qualche suggerimento!

    
posta Jérôme 11.07.2013 - 12:44
fonte

1 risposta

2

In un oggetto di design pulito in un dato albero dovrebbe essere usato con un'interfaccia uniforme. I tuoi dubbi riflettono un odore di design.

L'obiettivo di avere alberi oggetto nelle scene è di essere in grado di applicare trasformazioni geometriche a un gruppo di oggetti. Quindi se hai oggetti che non possono muoversi, non appartengono all'albero. Non ha senso.

Ecco una possibile soluzione. È possibile avere una classe di rendering che può avere oggetti di sfondo e la radice degli oggetti mobili. Tutti gli oggetti mobili possono muoversi. Una trasformazione applicata a un oggetto viene propagata ai suoi figli.

Per rendere la scena, devi renderizzare sia gli oggetti di sfondo non smontabili, sia la scena reale chiamando una funzione di rendering nella radice.

    
risposta data 16.07.2013 - 10:09
fonte