Java8能够使用Cake模式吗?

31
我很好奇:使用Java 8,并且可以在接口中添加实现(有点像Scala traits),是否可以像在Scala中一样实现cake pattern
如果可以,能否提供一段代码片段?
5个回答

27

在参考其他答案的启发下,我想出了以下(粗略的)类层次结构,类似于Scala中的cake pattern:

class A {
    def methodA(): Unit = {}
}
trait B {
    def methodB(): Unit = {}
}
trait C {
    def methodC(): Unit = {}
}
class D extends A with B with C {
    def methodD(): Unit = {}
}
val instance = new D()
instance.methodA()
instance.methodB()
instance.methodC()
instance.methodD()
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 {}

以上内容适用于Java 8,截至2013年1月9日。


那么,Java 8能够像蛋糕一样使用模式吗?可以。

它是否像Scala那样简洁,或者像Java中其他模式(例如依赖注入)那样有效?可能不是,上面的示例需要大量文件,而且不像Scala那么简洁。

总之:

  • 我们可以通过扩展所需的基础接口来模拟自身类型(如蛋糕模式)。
  • 接口无法拥有内部类(如@Owen所指出的),因此我们可以使用匿名类。
  • 可以通过使用静态哈希表(和惰性初始化)或只是客户端在其端存储值(例如UserService所做的那样)来模拟valvar
  • 我们可以通过在默认接口方法中使用this.getClass()来发现我们的类型。
  • 正如@Owen所指出的,路径相关类型在接口中不可能实现,因此完整的蛋糕模式固有地是不可能的。但是,上述内容显示出,我们可以将其用于依赖注入。

在默认方法体中,您应该能够访问thisthis.getClass(),并且可以通过弱引用映射添加额外的状态。然而,在日志记录示例中,这不是Java的方式;使用实例字段final Logger logger=Logger.of(this);来实现混合效果的简单/旧解决方案没有任何问题。 - irreputable

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"

你的第一个示例在Java 8中编译并按预期运行。 - Alex DiCarlo

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接口则不能。许多模块需要状态。这可以通过创建通用状态组件来解决以保存此信息,但至少部分需要在类中实现。第二个问题是,接口中的嵌套类更类似于类中的静态嵌套类。因此,您无法直接从模块类访问接口方法。默认接口方法具有访问此作用域的权限,并可将其添加到模块类的构造函数中。

状态可以通过使用静态身份映射来处理(以非常不优雅的方式),为每个字段映射(实例->值),然后在每个字段的getter中进行惰性初始化。 - Alex DiCarlo

2
一些实验表明没有:
  • Nested classes are automatically static. This is inherently 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();
    
  • So, apparently, are nested interfaces, although it's harder to coax out the error messages:

    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.
    

因此,如果没有实例成员类,您就没有路径相关类型,这对于蛋糕模式基本上是必要的。所以至少可以说,不是以直接的方式实现。


2
忽略Java 8中的新功能,理论上你可以使用编译时AspectJ ITDs在Java 5及以上版本中实现Cake Pattern。使用AspectJ DTO's可以让你制作混合组件。唯一烦人的是你需要创建两个构件:一个是方面(ITD),另一个是接口。但是ITDs允许你做一些疯狂的事情,比如向实现接口的类添加注释。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接