28 votos

¿Es posible el patrón Cake con Java8?

Sólo me pregunto: con Java 8, y la posibilidad de añadir implementación en interfaces (un poco como los traits de Scala), ¿será posible implementar el patrón de tarta ¿como podemos hacer en Scala?

Si es así, ¿puede alguien proporcionar un fragmento de código?

25voto

Alex DiCarlo Puntos 3276

Inspirándome en otras respuestas se me ocurrió la siguiente jerarquía de clases (aproximada) que es similar al patrón del pastel en 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 {}

Lo anterior compila en Java 8 a partir del 9 de enero de 2013.


Así que, ¿puede Java 8 hacer un pastel- como ¿Patrón? Sí.

¿Es tan conciso como Scala, o tan eficaz como otros patrones en Java (por ejemplo, la inyección de dependencia)? Probablemente no, el boceto anterior requiere un montón de archivos y no es tan conciso como Scala.

En resumen:

  • Los autotipos (necesarios para el patrón de la tarta) pueden emularse ampliando la interfaz base que esperamos.
  • Las interfaces no pueden tener clases internas (como señala @Owen), así que en su lugar podemos utilizar clases anónimas.
  • val y var puede emularse utilizando un hashmap estático (e inicialización perezosa), o bien el cliente de la clase simplemente almacenando el valor en su lado (como hace UserService).
  • Podemos descubrir nuestro tipo utilizando this.getClass() en un método de interfaz por defecto.
  • Como señala @Owen, los tipos dependientes de la ruta son imposibles utilizando interfaces, por lo que un patrón de pastel completo es inherentemente imposible. Lo anterior muestra, sin embargo, que uno podría utilizarlo para la inyección de dependencia.

3voto

thoredge Puntos 5829

Hace poco hice una pequeña prueba de concepto sobre esto. Puedes ver la entrada del blog aquí: http://thoredge.blogspot.no/2013/01/cake-pattern-in-jdk8-evolve-beyond.html y el repo de github aquí: https://github.com/thoraage/cake-db-jdk8

Básicamente puedes hacerlo, pero te enfrentas a al menos dos obstáculos que lo hacen menos hábil que Scala. En primer lugar, los rasgos de Scala pueden tener estado y la interfaz de Java no. Muchos módulos necesitan estado. Esto se puede arreglar creando un componente de estado general para mantener esta información, pero esto tendrá que estar en una clase. Al menos en parte. El segundo problema es que una clase anidada en una interfaz es más parecida a una clase estática anidada en una clase. Así que no se puede acceder a los métodos de las interfaces directamente desde la clase del módulo. El método de la interfaz por defecto tiene acceso a este ámbito y puede añadirlo al constructor de la clase del módulo.

2voto

Owen Puntos 14439

Algunos experimentos sugieren que no:

  • Las clases anidadas son automáticamente estáticas. Esto es intrínsecamente antiestático:

    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();
  • Al parecer, también lo son las interfaces anidadas, aunque es más difícil sacar los mensajes de error:

    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.

Por lo tanto, sin clases miembro de instancia, no tiene tipos dependientes de la ruta, lo cual es básicamente necesario para el patrón de la tarta. Así que al menos, no, no en la forma directa, no es posible.

2voto

irreputable Puntos 25577

Tal vez se pueda hacer algo así en 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"

Pero no es de ninguna manera mejor que el enfoque simple/viejo

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"

1voto

Adam Gent Puntos 15055

Ignorando la nueva funcionalidad de Java 8, en teoría se puede hacer el Cake Pattern en Java 5 y superiores utilizando el tiempo de compilación DTI de AspectJ .

Los DTO de AspectJ permiten hacer Mixins . Lo único molesto es que tendrás que hacer dos artefactos: el aspecto (ITD) y la interfaz. Sin embargo, los ITDs te permiten hacer algunas cosas locas como añadir anotaciones a las clases que implementan una interfaz.

Iteramos.com

Iteramos es una comunidad de desarrolladores que busca expandir el conocimiento de la programación mas allá del inglés.
Tenemos una gran cantidad de contenido, y también puedes hacer tus propias preguntas o resolver las de los demás.

Powered by:

X