30

我只是想知道:使用 Java 8,以及在接口中添加实现的可能性(有点像 Scala 特征),是否可以像在 Scala 中那样实现蛋糕模式

如果是,有人可以提供代码片段吗?

4

5 回答 5

27

在其他答案的启发下,我提出了以下(粗略的)类层次结构,类似于 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 {}

截至 2013 年 1 月 9 日,以上代码在 Java 8 上编译。


那么,Java 8 可以做蛋糕一样的模式吗?是的。

它是像 Scala 一样简洁,还是像 Java 中的其他模式一样有效(即依赖注入)?可能不是,上面的草图需要大量文件,而且不如 Scala 简洁。

总之:

  • 自我类型(根据蛋糕模式的需要)可以通过扩展我们期望的基本接口来模拟。
  • 接口不能有内部类(如@Owen 所述),因此我们可以使用匿名类。
  • val并且var可以通过使用静态哈希图(和延迟初始化)来模拟,或者由类的客户端简单地将值存储在他们身边(就像 UserService 一样)。
  • 我们可以通过使用this.getClass()默认接口方法来发现我们的类型。
  • 正如@Owen 所指出的,路径依赖类型是不可能使用接口的,因此完整的蛋糕模式本质上是不可能的。但是,上面显示了可以将其用于依赖注入。
于 2013-01-10T01:03:42.610 回答
3

也许你可以在 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"

但这绝不比普通/旧方法更好

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"
于 2013-01-10T01:42:07.637 回答
3

我最近对此做了一个小的概念验证。您可以在此处查看博客文章:http: //thoredge.blogspot.no/2013/01/cake-pattern-in-jdk8-evolve-beyond.html和 github 存储库:https ://github.com/thoraage /cake-db-jdk8

基本上你可以做到,但你至少面临两个障碍,使其不如 Scala 流畅。首先,Scala 特征可以有状态,而 Java 的接口不能。许多模块需要状态。这可以通过创建一个通用状态组件来保存此信息来解决,但这需要在一个类中。至少部分。第二个问题是接口中的嵌套类更类似于类中的静态嵌套类。所以你不能直接从模块类访问接口方法。默认接口方法可以访问这个范围,并且可以将它添加到模块类的构造函数中。

于 2013-01-10T07:34:09.570 回答
2

一些实验表明没有:

  • 嵌套类自动是静态的。这本质上不像蛋糕:

    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();
    
  • 因此,显然,是嵌套接口,尽管更难哄出错误消息:

    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.
    

因此,没有实例成员类,您就没有路径依赖类型,这对于蛋糕模式来说基本上是必需的。所以至少,不,不是直接的方式,这是不可能的。

于 2013-01-10T01:05:12.047 回答
2

忽略 Java 8 中的新功能,理论上您可以使用编译时AspectJ ITDs在 Java 5 及更高版本中执行 Cake Pattern 。

AspectJ DTO 允许您制作 Mixins。唯一烦人的事情是您必须制作两个工件:方面 (ITD) 和接口。但是,ITD 允许您做一些疯狂的事情,例如向实现接口的类添加注释。

于 2013-01-10T03:42:07.653 回答