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

基本的にはできますが、少なくとも 2 つの障害に直面するため、Scala よりもスムーズではありません。まず、Scala トレイトは状態を持つことができますが、Java のインターフェースはできません。多くのモジュールには状態が必要です。これは、この情報を保持する一般的な状態コンポーネントを作成することで修正できますが、これはクラス内にある必要があります。少なくとも部分的に。2 つ目の問題は、インターフェイス内のネストされたクラスが、クラス内の静的なネストされたクラスに似ていることです。したがって、モジュール クラスから直接インターフェイス メソッドにアクセスすることはできません。デフォルトのインターフェイス メソッドはこのスコープにアクセスでき、これをモジュール クラスのコンストラクターに追加できます。

于 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 の新機能を無視して、理論的には Java 5 以降でコンパイル時のAspectJ ITDを使用して Cake パターンを実行できます。

AspectJ DTO を使用すると、Mixin を作成できます。唯一厄介なのは、アスペクト (ITD) とインターフェースの 2 つの成果物を作成する必要があることです。ただし、ITD を使用すると、インターフェイスを実装するクラスに注釈を追加するなど、クレイジーなことを行うことができます。

于 2013-01-10T03:42:07.653 に答える