2012-05-22 10 views
10

Cercherò di spiegare al meglio.Come utilizzare una classe generica con oggetti specifici in un contesto statico?

Uso Play Framework 2 e eseguirò molte azioni CRUD. Alcuni di loro saranno identici, quindi mi piacerebbe KISS e ASCIUTO quindi all'inizio pensavo a una classe astratta contenente i metodi list, details, create, update e , con oggetto generico ed estendere questa classe specificando quale oggetto da utilizzare (modello & Form):

public abstract class CrudController extends Controller { 
    protected static Model.Finder<Long, Model> finder = null; 
    protected static Form<Model> form = null; 

    public static Result list() { 
     // some code here 
    } 

    public static Result details(Long id) { 
     // some code here 
    } 

    public static Result create() { 
     // some code here 
    } 

    public static Result update(Long id) { 
     // some code here 
    } 

    public static Result delete(Long id) { 
     // some code here 
    } 
} 

e una classe che utilizzerà CRUD:

public class Cities extends CrudController { 
    protected static Model.Finder<Long, City> finder = City.find; 
    protected static Form<City> form = form(City.class); 

    // I can override a method in order to change it's behavior : 
    public static Result list() { 
     // some different code here, like adding some where condition 
    } 
} 

questo funzionerebbe se non ero in un contesto statico.

Ma visto che è il caso, come posso fare?

+0

Perché non passare semplicemente ai metodi di istanza? L'ereditarietà non funziona sui metodi di classe in Java. – Vlad

+0

Un'altra idea potrebbe essere quella di sostituire l'ereditarietà con la composizione: basta disporre delle istanze di CrudController e delegare le operazioni all'oggetto contenuto specifico dell'istanza. (Tutto dipende dal tuo design, ovviamente.) – Vlad

+0

@Vlad: per la prima domanda, non posso, Play richiede un metodo statico per il controller. Ora per il secondo, non sono sicuro di capire, potresti elaborare (o creare una nuova risposta)? –

risposta

13

Ciò può essere ottenuto utilizzando la delega: definire una classe Java regolare che contiene la logica azioni CRUD:

public class Crud<T extends Model> { 

    private final Model.Finder<Long, T> find; 
    private final Form<T> form; 

    public Crud(Model.Finder<Long, T> find, Form<T> form) { 
     this.find = find; 
     this.form = form; 
    } 

    public Result list() { 
     return ok(Json.toJson(find.all())); 
    } 

    public Result create() { 
     Form<T> createForm = form.bindFromRequest(); 
     if (createForm.hasErrors()) { 
      return badRequest(); 
     } else { 
      createForm.get().save(); 
      return ok(); 
     } 
    } 

    public Result read(Long id) { 
     T t = find.byId(id); 
     if (t == null) { 
      return notFound(); 
     } 
     return ok(Json.toJson(t)); 
    } 

    // … same for update and delete 
} 

Quindi è possibile definire un controller di gioco con un campo statico che contiene un'istanza di Crud<City>:

public class Cities extends Controller { 
    public final static Crud<City> crud = new Crud<City>(City.find, form(City.class)); 
} 

ed il gioco è quasi fatto: basta necessario definire i percorsi per le azioni Crud:

GET /     controllers.Cities.crud.list() 
POST /     controllers.Cities.crud.create() 
GET  /:id     controllers.Cities.crud.read(id: Long) 

Nota: questo esempio produce risposte JSON per brevety ma è possibile rendere modelli HTML. Tuttavia, poiché i modelli di Play 2 sono tipizzati staticamente, è necessario passare tutti loro come parametri della classe Crud.

+0

Imo restituire una vista a seconda del controller è la parte difficile, come risolverebbe questo? –

+0

Questo è davvero un problema interessante. Questa domanda era più su come riutilizzare qualche logica nonostante la natura statica dei controller (che non consentono l'ereditarietà). Forse dovresti aprire un'altra domanda? –

+1

È vero, non è proprio nella portata di questa domanda. Ma mi stavo solo chiedendo quando leggevo la tua risposta, che mi piace! Forse aprirò un'altra domanda stasera. –

4

:

la seguente idea potrebbe aiutare (di responsabilità non ho alcuna esperienza con play framework.):

public interface IOpImplementation { 
    public static Result list(); 
    public static Result details(Long id); 
    public static Result create(); 
    public static Result update(Long id); 
    public static Result delete(Long id); 
} 

public abstract class CrudController extends Controller { 
    protected static Model.Finder<Long, Model> finder = null; 
    protected static Form<Model> form = null; 

    protected static IOpImplementation impl; 

    public static Result list() { 
     return impl.list(); 
    } 

    public static Result details(Long id) { 
     return impl.details(id); 
    } 
    // other operations defined the same way 
} 

public class Cities extends CrudController { 

    public static Cities() { 
     impl = new CitiesImpl(); 
    } 

} 

In questo modo è possibile creare una gerarchia di implementazioni.

(Questo deve essere un modello di progettazione fantasia-nome, ma non so il nome bancomat.)

+0

C'è una buona idea dietro la tua risposta, ma quello che sto cercando è avere una classe per modello da modificare nel database (città, ecc.). Perché quando definisco i percorsi, devo indirizzarli a una classe specifica (controller). Questo controller definirà quale modello usare. Quindi non è un problema se CrudController cambia, ho solo bisogno di scrivere il meno codice possibile per ogni controller Crud per il caso il codice è identico tra varie classi. –

+0

@ cx42net: non ho capito la parte "avere una classe per modello". Questo significa che per ogni possibile 'Modello 'ci dovrebbe essere una classe derivata da' CrudController' (o forse un'istanza di 'CrudController')? – Vlad

+0

Oh, questo non funzionerebbe, in quanto non ci sono istanze. Aspetta, lo aggiornerò. – Vlad

Problemi correlati