views:

273

answers:

7

I have a simple problem try to stay DRY using Appengine.

The 2 functions below are identical except for the object sent as parameter. In reality I have 15 functions like this. I am try to find a way to create a super class or a generic to achieve this.

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();
 }
}

and

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();
 }
}

Unfortunately it seems that I cannot use generics since generics don't support T.class.

Any good suggestion how to do this w/o duplicating?

Thank you. Daniel

+4  A: 

You don't need generics for this; use thing.getClass():

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

(you should add a null-check in there just in case the parameter passed in was null)

[Note: I changed my answer just after I posted it when I realized you don't need generics here...]

Scott Stanchfield
Except object won't supply the getId method so you'll need to declare thing as <? extends SuperType> where SuperType implements or declares (abstract) the getId method.
pstanton
You need a common superclass (or interface) having the getId() method.
Buhb
@pstanton: you don't need generics to manage this.
Buhb
Or pass the result of s.getId() into the method as a parameter instead?
Grundlefleck
@Grundlefleck: Actually, that's a good idea.
R. Bemrose
Is the runtime class of thing guaranteed to be the class expected by the persistence manager? Couldn't it be a subclass thereof? If you persist with Hibernate, for instance, thing could be a lazy loading proxy, that merely implements the class the persistence manager expects.
meriton
+2  A: 

The easiest way would be to introduce the type as a parameter:

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();
        }
}

If your Persons, Products etc are only simple classes and don't have subclasses, you could use getClass() instead of specifying an explicit parameter, as suggested by Scott.

Jorn
Do you mean to use `Product.class` in your example? Do you not mean something from the parameter `type` instead?
Grundlefleck
I think that instead of `Product.class` you wanted to write `type`... am I right?
True Soft
yeah, that's right. Edited.
Jorn
Could you wrap this method by another method that takes only the single `s` arg?
Loadmaster
Only if the type parameter is equal to the exact class of `s`. But if that's the case, you don't need the generic type at all.
Jorn
+1  A: 

If you're using this in a DAO, I tend to initialize it with the class. So maybe

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();     
    }
  }
}
Mike
+4  A: 

This may not be the easiest way to do things, but I can't help but think it would make the most sense in the long run.

Create an interface

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

And in each of your classes, implement the interface and its method

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

and finally, your delete method:

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: I haven't completely tested this... Specifically, I haven't tested whether pm.deletePersistent(p) works with PersistenceManager.

R. Bemrose
i was halfway thru writing the same thing.
pstanton
@pstanton: I actually wrote this before seeing Buhb's reply. However, it turns out that Grundlefleck probably has a better idea.
R. Bemrose
no, i personally prefer your/our approach. compile time safety and proper use of inheritance = good.
pstanton
+2  A: 

DRY is a good principle. So is KISS ;-)

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();
    }
}

This would be called with, for example:

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

Since this is a void method, and PersistenceManager does not appear to work with generic types, I would advise avoiding using generics. This method, if applicable, has the added bonus that you won't need to modify the type hierarchy of Product, Person, etc.

The downside is that if this method is called from several places, there may be many places to change the signature - but it's easy to let the compiler find out how long it would take.

Grundlefleck
only problem is your code doesn't compile time restrict someone trying to delete an invalid type ie theObject.deleteRecord(Integer.class, s.getId()) would compile, but fail at runtime.
pstanton
Even with that, I probably wouldn't go turn to generics, I'd most likely use the compile time constraints an enum type gives. So an enum would contain members which could provide the required 15 or so classes. You reckon generics is a better option than this?
Grundlefleck
"PersistenceManager does not appear to work with generic types" I don't think any code by Apache (or Oracle) uses generics. With the possible exception of Apache's collections.
R. Bemrose
A: 

What is PMF? Where did you import it from? I'm trying to compile similar code from the book - Beginning Java Google App Engine, Apress - and there is no explanation of where this PMF comes from.

Thank you, Igor

PythonUser
A: 

You can use an enum to conceal this:

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");