2010-05-13 30 views
5

Per il mio progetto semestrale, il mio team e io dovremmo creare un file .jar (libreria, non eseguibile) che contenga un framework di sviluppo del gioco e mostri i concetti di OOP. Dovrebbe essere un FRAMEWORK e un altro team dovrebbe usare il nostro framework e viceversa. Quindi voglio sapere come dovremmo iniziare. Abbiamo pensato a diversi approcci:
1. Iniziare con una classe normaleClasse base o classe astratta?

public class Enemy { 
    public Enemy(int x, int y, int health, int attack, ...) { 
     ... 
    } 
    ... 
} 
public class UserDefinedClass extends Enemy { 
    ... 
} 

2. Iniziare con una classe astratta che i nemici definiti dall'utente devono ereditare membri astratti

public abstract class Enemy { 
    public Enemy(int x, int y, int health, int attack, ...) { 
     ... 
    } 
    public abstract void draw(); 
    public abstract void destroy(); 
    ... 
} 
public class UserDefinedClass extends Enemy { 
    ... 
    public void draw() { 
     ... 
    } 
    public void destroy() { 
     ... 
    } 
} 

3. Creare un super ABC (Abstract Base Class) che TUTTI ereditano da

public abstract class VectorEntity { 
    ... 
} 
public abstract class Enemy extends VectorEntity { 
    ... 
} 
public class Player extends VectorEntity { 
    ... 
} 
public class UserDefinedClass extends Enemy { 
    ... 
} 

Quale dovrei usare? O c'è un modo migliore?

risposta

6

Beh, è ​​un po 'difficile da dire con certezza senza conoscere a fondo quello che stai facendo, e anche allora è piuttosto soggettiva. Tuttavia, ci sono alcune cose da considerare che potrebbero dirti.

  1. Sta per creare un'istanza di un nemico, oppure tutti i nemici devono davvero essere di tipo derivato? Se non si sta per istanziare i Nemici piuttosto che i tipi derivati, allora probabilmente dovrebbe essere un'interfaccia o una classe astratta.

  2. Se stai cercando di fornire il comportamento effettivo nella classe base, poi, ovviamente, ha bisogno di essere una classe piuttosto che un interfaccia.

  3. I metodi che devono essere presenti nell'API ma che non hanno alcun senso per fornire eventuali implementazioni devono essere astratti.

  4. I metodi in cui è logico disporre di implementazioni per loro nella classe base devono avere implementazioni nella classe base. E se non ha senso per loro di essere sopraffatti, quindi renderli definitivi.

  5. Rendere le classi condivise una classe base comune ha davvero senso solo se condivide realmente il comportamento o è necessario essere in grado di trattarle tutte uguali da qualche parte nel codice. Se non sono poi così simili, probabilmente non dovrebbero condividere una classe base. Ad esempio, se sia il nemico che il giocatore dovrebbero essere visualizzabili, può avere senso avere una classe base comune che gestisce la loro funzionalità di visualizzazione. Ma se Enemy fosse qualcosa che era visualizzabile, e Player era un concetto più astratto - come il controller del gioco - e non era visualizzabile, allora probabilmente non avrebbe senso per loro condividere una classe base. In generale, è preferibile preferire la composizione piuttosto che l'ereditarietà quando si creano classi, quindi se le classi in questione non condivideranno realmente il comportamento e non avranno realmente una relazione "is-a" con una classe base comune, allora non dovrebbero condividere una classe base comune.

  6. Preferire che le classi di base condividano solo i metodi, non i dati. In altre parole, in un albero ereditario, è meglio che solo le foglie siano istantanee. Ci sono varie cose come equals() che si interrompono quando si hanno classi di base con dati effettivi al loro interno. Questo non vuol dire che non puoi farlo - le persone lo fanno sempre - ma può causare problemi ed è meglio evitare se non è necessario.

  7. Preferiscono sovrascrivere i metodi astratti. Altrimenti, nelle classi derivate, rischi di non chiamare il metodo della classe base o di cambiare completamente il metodo.

sono sicuro che avrei potuto venire con di più, ma senza veramente avere familiarità con il progetto, è destinato ad essere piuttosto generica. Tra le 3 opzioni che hai dato, probabilmente andrei con 2. 3 sembra che tu stia probabilmente creando una classe base per classi non correlate, e 1 risulterebbe in Enemy che è istanziabile, che probabilmente non hai bisogno e farebbe sicuramente in modo che più delle foglie nella gerarchia ereditaria siano istantanee. Probabilmente finirai con i dati nelle classi base con 2, ma è più probabile che tu stia solo scavalcando i metodi astratti e avrai meno problemi con il comportamento alterato nelle classi derivate.

0

La mia regola di condotta è che una lunga quanto vi sono più di una classe che condividono la stessa attività/dati/metodi/funzionalità, dovrebbero essere estensioni della stessa classe astratta.

Quindi, se fosse me facendo:

  • Se tutte le classi hanno qualcosa in comune, hanno un alto livello abstract class che raccoglie questa funzionalità/campi/dati in un unico luogo.
  • Se non, solo quelle classi che in realtà hanno qualcosa in comune dovrebbero estendere un livello inferiore abstract class.

Se solo i metodi sono quelli che le classi avranno in comune, è possibile utilizzare anche interface s. Tuttavia, trovo sempre che prima o poi vedo che le classi che implementano lo interface hanno gli stessi campi private. A questo punto, trasformo il interface a un abstract class che contiene questi campi privati ​​(di risparmiare sulle righe di codice se non altro).

1

Una quarta opzione sarebbe utilizzare le interfacce.

interface Enemy { 

    public void draw(); 

    . . . 

} 

Se sei appena agli inizi, eviterei la tua terza opzione. Lascia che la struttura si sviluppi un po 'e vedi se ce n'è bisogno.

0

Solo una piccola risposta dal libro "c Più efficace ++" Pagina 271:

"Make classi di base astratta che non sono alla fine di una gerarchia". Sono troppo pigro per darti l'intero chaper, ma l'autrice elenca alcune buone ragioni per questo.

+0

C'è anche "Java efficace", che è più applicabile in questo caso. Dice qualcosa di simile, ma con cose più specifiche di Java, come il fatto che 'equals()' non funzioni correttamente quando si mescolano tipi derivati ​​e tipi base se le classi base non sono tutte astratte. –

Problemi correlati