2013-01-10 13 views

risposta

23

Con ispirazione da altre risposte mi si avvicinò con la seguente (ruvida) gerarchia di classe che è simile al modello torta a Scala:

interface UserRepository { 
    String authenticate(String username, String password); 
} 

interface UserRepositoryComponent { 
    UserRepository getUserRepository(); 
} 

interface UserServiceComponent extends UserRepositoryComponent { 
    default UserService getUserService() { 
     return new UserService(getUserRepository()); 
    } 
} 

class UserService { 
    private final UserRepository repository; 

    UserService(UserRepository repository) { 
     this.repository = repository; 
    } 

    String authenticate(String username, String password) { 
     return repository.authenticate(username, password); 
    } 
} 

interface LocalUserRepositoryComponent extends UserRepositoryComponent { 
    default UserRepository getUserRepository() { 
     return new UserRepository() { 
      public String authenticate(String username, String password) { 
       return "LocalAuthed"; 
      } 
     }; 
    } 
} 

interface MongoUserRepositoryComponent extends UserRepositoryComponent { 
    default UserRepository getUserRepository() { 
     return new UserRepository() { 
      public String authenticate(String username, String password) { 
       return "MongoAuthed"; 
      } 
     }; 
    } 
} 

class LocalApp implements UserServiceComponent, LocalUserRepositoryComponent {} 
class MongoApp implements UserServiceComponent, MongoUserRepositoryComponent {} 

Le compilazioni di cui sopra su Java 8 come di Jan.9 2013.


Quindi, può Java 8 fare una torta- come modello? Sì.

È tetro come Scala, o efficace come altri modelli in Java (vale a dire l'iniezione di dipendenza)? Probabilmente no, lo schizzo sopra ha richiesto un sacco di file e non è così lineare come Scala.

In sintesi:

  • Self-tipi (come necessario per il modello di torta) può essere emulato estendendo l'interfaccia di base che ci aspettiamo.
  • Le interfacce non possono avere classi interne (come notato da @Owen), quindi possiamo usare le classi anonime.
  • val e var possono essere emulati utilizzando una hashmap statica (e l'inizializzazione pigra) o dal client della classe semplicemente memorizzando il valore sul lato (come fa UserService).
  • Possiamo scoprire il nostro tipo utilizzando this.getClass() in un metodo di interfaccia predefinito.
  • Come note @Owen, i tipi dipendenti dal percorso sono impossibili utilizzando le interfacce, quindi un modello di torta completo è intrinsecamente impossibile. Quanto sopra mostra, tuttavia, che si potrebbe usare per l'iniezione di dipendenza.
+0

Dovresti poter accedere a 'this' e' this.getClass() 'in un corpo del metodo predefinito ed è possibile aggiungere uno stato extra attraverso una mappa delle identità debole. Tuttavia, nell'esempio di registrazione, non è solo la via java; niente di sbagliato con la semplice/vecchia soluzione di aggiungere un campo di istanza 'Logger logger finale = Logger.of (this);' per ottenere l'effetto mixin. – irreputable

+0

Sei corretto, aggiornato il post per riflettere questo. –

+0

grazie, ottima risposta! –

2

Alcuni esperimenti suggeriscono no:

  • classi nidificate sono automaticamente statica. Questo è di per sé uncakelike:

    interface Car { 
        class Engine { } 
    } 
    
    // ... 
        Car car = new Car() { }; 
        Car.Engine e = car.new Engine(); 
    
    error: qualified new of static class 
        Car.Engine e = car.new Engine(); 
    
  • Così, le interfacce a quanto pare, sono annidati, anche se è più difficile di convincere i messaggi di errore:

    interface Car { 
        interface Engine { } 
    } 
    
    // ... 
        Car car = new Car() { }; 
        class Yo implements car.Engine { 
        } 
    
    error: package car does not exist 
         class Yo implements car.Engine { 
    
    // ... 
    
    class Yo implements Car.Engine { 
    }                          
    
    
    // compiles ok. 
    

Così, senza classi membri esempio, si fanno non ha tipi dipendenti dal percorso, che è fondamentalmente necessario per il modello di torta. Quindi, almeno, no, non in modo diretto, non è possibile.

2

forse si può fare qualcosa di simile in Java 8

interface DataSource 
{ 
    String lookup(long id); 
} 

interface RealDataSource extends DataSource 
{ 
    default String lookup(long id){ return "real#"+id; } 
} 

interface TestDataSource extends DataSource 
{ 
    default String lookup(long id){ return "test#"+id; } 
} 

abstract class App implements DataSource 
{ 
    void run(){ print("data is " + lookup(42)); } 
} 


class RealApp extends App implements RealDataSource {} 

new RealApp().run(); // prints "data is real#42" 


class TestApp extends App implements TestDataSource {} 

new TestApp().run(); // prints "data is test#42" 

Ma non è in alcun modo migliore di pianura/vecchio approccio

interface DataSource 
{ 
    String lookup(long id); 
} 

class RealDataSource implements DataSource 
{ 
    String lookup(long id){ return "real#"+id; } 
} 

class TestDataSource implements DataSource 
{ 
    String lookup(long id){ return "test#"+id; } 
} 

class App 
{ 
    final DataSource ds; 
    App(DataSource ds){ this.ds=ds; } 

    void run(){ print("data is " + ds.lookup(42)); } 
} 


new App(new RealDataSource()).run(); // prints "data is real#42" 


new App(new TestDataSource()).run(); // prints "data is test#42" 
+0

Il primo esempio viene compilato ed eseguito come previsto in Java 8. –

1

Ignorando la nuova funzionalità in Java 8 è possibile in teoria fare il Cake Pattern in Java 5 e successivi usando il tempo di compilazione AspectJ ITDs.

AspectJ DTO's allow you to make Mixins. L'unica cosa fastidiosa è che dovrai creare due artefatti: l'aspetto (ITD) e l'interfaccia. Tuttavia, gli ITD ti consentono di fare cose pazzesche come aggiungere annotazioni alle classi che implementano un'interfaccia.

3

Ho fatto un piccolo proof-on-concept su questo recentemente. Puoi vedere il post del blog qui: http://thoredge.blogspot.no/2013/01/cake-pattern-in-jdk8-evolve-beyond.html e il repository github qui: https://github.com/thoraage/cake-db-jdk8

Fondamentalmente puoi farlo, ma devi affrontare almeno due ostacoli che lo rendono meno fluido di Scala. Innanzitutto i tratti Scala possono avere lo stato e l'interfaccia Java non possono. Molti moduli hanno bisogno di stato. Questo può essere risolto creando un componente di stato generale per conservare queste informazioni, ma questo dovrà essere in una classe. Almeno in parte. Il secondo problema è che una classe nidificata in un'interfaccia è più simile a una classe nidificata statica in classe. Quindi non è possibile accedere ai metodi delle interfacce direttamente dalla classe del modulo. Il metodo di interfaccia predefinito ha accesso a questo ambito e può aggiungerlo al costruttore della classe del modulo.

+0

Lo stato può essere gestito (in modo molto inelegante) utilizzando le mappe di identità statiche, il mapping da (istanza -> valore) per ciascun campo e quindi inizializzandolo pigramente nel getter per ogni campo. –