2009-12-03 3 views
3

J'ai un problème simple essayer de rester au sec en utilisant Appengine.Aide pour créer une classe générique pour éviter la duplication de code

Les 2 fonctions ci-dessous sont identiques à l'exception de l'objet envoyé en paramètre. En réalité, j'ai 15 fonctions comme celle-ci. Je suis essayer de trouver un moyen de créer une super classe ou un générique pour y parvenir.

public void deleteRecord(Person s) { 

    PersistenceManager pm = PMF.get().getPersistenceManager(); 
    try { 
     Person p = pm.getObjectById(Person.class, s.getId()); 
     pm.deletePersistent(p); 
    } finally { 
     pm.close(); 
    } 
} 

et

public void deleteRecord(Product s) { 

    PersistenceManager pm = PMF.get().getPersistenceManager(); 
    try { 
     Product p = pm.getObjectById(Product.class, s.getId()); 
     pm.deletePersistent(p); 
    } finally { 
     pm.close(); 
    } 
} 

Malheureusement, il semble que je ne peux pas utiliser les médicaments génériques puisque les génériques ne prennent pas en charge T.class.

Toute bonne suggestion comment faire cela sans duplication?

Merci. Daniel

+0

ne pouvez pas modifier votre méthode pour accepter une classe plutôt qu'un objet? – JRL

+1

ces méthodes doivent-elles être dans la même classe, ou est-ce que chaque méthode est similaire à une classe? – meriton

Répondre

2

DRY est un bon principe. Alors est BAISER ;-)

public void deleteRecord(Class classOfProduct, Object id) { 

    PersistenceManager pm = PMF.get().getPersistenceManager(); 
    try { 
      Object p = pm.getObjectById(classOfProduct, id); 
      pm.deletePersistent(p); 
    } finally { 
      pm.close(); 
    } 
} 

Ce serait appelé, par exemple:

theObject.deleteRecord(Person.class, s.getId()); 
theObject.deleteRecord(Product.class, s.getId()); 

Comme il est une méthode vide, et PersistenceManager ne semble pas fonctionner avec des types génériques, je conseille éviter d'utiliser des génériques.Cette méthode, le cas échéant, a le bonus supplémentaire que vous n'aurez pas besoin de modifier la hiérarchie de type Product, Person, etc.

L'inconvénient est que si cette méthode est appelée à partir de plusieurs endroits, il peut y avoir beaucoup d'endroits pour changer la signature - mais il est facile de laisser le compilateur savoir combien de temps cela prendrait.

+0

seul problème est votre code ne compile pas le temps restreindre quelqu'un essayant de supprimer un type invalide à savoir le theObject.deleteRecord (Integer.class, s.getId()) compilerait, mais échoue à l'exécution. – pstanton

+0

Même avec cela, je ne serais probablement pas aller aux génériques, j'utiliserais probablement les contraintes de temps de compilation qu'un type enum donne. Donc, une énumération contiendrait des membres qui pourraient fournir les 15 classes requises. Vous pensez que les génériques sont une meilleure option que celle-ci? – Grundlefleck

+0

"PersistenceManager ne semble pas fonctionner avec les types génériques" Je ne pense pas que le code par Apache (ou Oracle) utilise des génériques. À l'exception possible des collections d'Apache. – Powerlord

4

Vous n'avez pas besoin de génériques pour cela; utiliser thing.getClass():

public void deleteRecord(Object thing) { 
    ... 
    Object o = pm.getObjectById(thing.getClass(), s.getId()); 
    ... 
} 

(vous devez ajouter un null-chèque là-bas juste au cas où le paramètre passé dans était nulle)

[Note: J'ai changé ma réponse juste après je l'ai posté quand je me suis aperçu que vous n'avez pas besoin ici génériques ...]

+4

Sauf que l'objet ne fournit pas la méthode getId, vous devez donc déclarer une chose comme où SuperType implémente ou déclare (abstrait) la méthode getId. – pstanton

+0

Vous avez besoin d'une super-classe commune (ou interface) ayant la méthode getId(). – Buhb

+0

@pstanton: vous n'avez pas besoin de génériques pour gérer cela. – Buhb

2

la meilleure façon serait d'introduire le type comme paramètre:

public <T> void deleteRecord(Class<T> type, T s) { 
     PersistenceManager pm = PMF.get().getPersistenceManager(); 
     try { 
       T p = pm.getObjectById(type, s.getId()); 
       pm.deletePersistent(p); 
     } finally { 
       pm.close(); 
     } 
} 

Si votre personnes, les produits etc ne sont que de simples classes un Si vous n'avez pas de sous-classes, vous pouvez utiliser getClass() au lieu de spécifier un paramètre explicite, comme suggéré par Scott.

+1

Voulez-vous utiliser 'Product.class' dans votre exemple? Ne voulez-vous pas dire quelque chose du paramètre 'type' à la place? – Grundlefleck

+0

Je pense qu'au lieu de 'Product.class' vous vouliez écrire' type' ... ai-je raison? –

+0

ouais, c'est vrai. Édité. – Jorn

1

Si vous utilisez ceci dans un DAO, j'ai tendance à l'initialiser avec la classe. Alors peut-être

public class DAO<T> { 

    private Class klass 

    public DAO(Class klass) { 
    this.klass = klass; 
    } 

    public void deleteRecord(T record) { 
    PersistenceManager pm = PMF.get().getPersistenceManager();  
    try {  
     T p = pm.getObjectById(this.klass, record.getId());  
     pm.deletePersistent(p);  
    } finally {  
     pm.close();  
    } 
    } 
} 
4

Cela ne peut être la meilleure façon de faire les choses, mais je ne peux pas empêcher de penser qu'il serait le plus logique à long terme.

Créer une interface

// This is a terrible name, I know 
public interface Identifier { 
    // Assumes ID was an int 
    public int getId(); 
    // Maybe have setId, too 
} 

Et dans chacune de vos classes, mettre en œuvre l'interface et sa méthode

public class Person implements Identifier { 
    public int getId() { 
     //Implementation details here 
    } 
} 

et enfin, votre méthode de suppression:

public void deleteRecord(Identifier s) { 

    PersistenceManager pm = PMF.get().getPersistenceManager(); 
    try { 
     Identifier p = pm.getObjectById(s.getClass(), s.getId()); 
     pm.deletePersistent(p); 
    } finally { 
     pm.close(); 
    } 
} 

Note: Je n'ai pas complètement testé cela ... Plus précisément, je n'ai pas testé si pm.deletePersistent (p) wor ks avec PersistenceManager.

+0

J'étais à mi-chemin à écrire la même chose. – pstanton

+0

@pstanton: J'ai effectivement écrit ceci avant de voir la réponse de Buhb. Cependant, il s'avère que Grundlefleck a probablement une meilleure idée. – Powerlord

+0

non, je préfère personnellement votre/notre approche. compiler la sécurité du temps et l'utilisation appropriée de l'héritage = bien. – pstanton

0

Vous pouvez utiliser un ENUM pour cacher ceci:

class FetchableThing {} 
class Person extends FetchableThing {} 
class Organisation extends FetchableThing {} 

enum DAO<T extends fetchableThing> { 
    Person(Person.class), Organisation(Organisation.class); 

    private Class<T> myClass; 
    private DAO(Class<T> myClass) { this.myClass=myClass;} 

    public delete(String id) { 
     PersistenceManager pm = PMF.get().getPersistenceManager(); 
     try { 
      T p = (T) pm.getObjectById(myClass, id); 
      pm.deletePersistent(p); 
     } finally { 
      pm.close(); 
     } 
    } 
} 

DAO.Person.delete("1234"); 
DAO.Organsation.delete("1234"); 
Questions connexes