Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Cake pattern with Java8 possible?

I just wonder: with Java 8, and the possibility to add implementation in interfaces (a bit like Scala traits), will it be possible to implement the cake pattern, like we can do in Scala?

If it is, can someone provide a code snippet?

like image 296
Sebastien Lorber Avatar asked Jan 10 '13 00:01

Sebastien Lorber


3 Answers

With inspiration from other answers I came up with the following (rough) class hierarchy that is similar to the cake pattern in 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 {}

The above compiles on Java 8 as of Jan.9 2013.


So, can Java 8 do a cake-like pattern? Yes.

Is it as terse as Scala, or as effective as other patterns in Java (i.e. dependency injection)? Probably not, the above sketch required a whole lot of files and is not as terse as Scala.

In summary:

  • Self-types (as needed for the cake pattern) can be emulated by extending the base interface we expect.
  • Interfaces cannot have inner classes (as noted by @Owen), so instead we can use anonymous classes.
  • val and var can be emulated by using a static hashmap (and lazy initialization), or by the client of the class simply storing the value on their side (like UserService does).
  • We can discover our type by using this.getClass() in a default interface method.
  • As @Owen notes, path dependent types are impossible using interfaces, so a full cake pattern is inherently impossible. The above shows, however, that one could use it for dependency injection.
like image 90
Alex DiCarlo Avatar answered Oct 23 '22 16:10

Alex DiCarlo


Maybe you can do something like this 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"

But it is in no way better than the plain/old approach

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"
like image 3
irreputable Avatar answered Oct 23 '22 14:10

irreputable


I did a small proof-on-concept on this recently. You can see the blog post here: http://thoredge.blogspot.no/2013/01/cake-pattern-in-jdk8-evolve-beyond.html and the github repo here: https://github.com/thoraage/cake-db-jdk8

Basically you can do it, but you face at least two obstacles that makes it less slick than Scala. Firstly the Scala traits can have state and Java's interface can't. Many modules need state. This can be fixed by creating a general state component to hold this information, but this will need to be in a class. At least in part. Second issue is that a nested class in an interface is more akin to a static nested class in class. So you can't access the interfaces methods directly from the module class. The default interface method have access to this scope and can add this to the constructor of the module class.

like image 3
thoredge Avatar answered Oct 23 '22 15:10

thoredge