2013-01-10 19 views

Odpowiedz

23

Z inspiracji od innych odpowiedzi wymyśliłem następujący (szorstki) klasowej hierarchii, który jest podobny do wzoru ciasto 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 {} 

Powyższe kompiluje się na Javie 8 od Jan.9 2013.


Tak, można zrobić Java 8 cake- jak wzór? Tak.

Czy jest tak zwięzły jak Scala, czy tak samo skuteczny, jak inne wzorce w Javie (to jest zastrzyk zależności)? Prawdopodobnie nie, powyższy szkic wymagał dużej ilości plików i nie jest tak zwięzły jak Scala.

Podsumowując:

  • samoczynnej rodzaje (ile potrzeba do wzorca ciasto) może być naśladowane przez rozszerzenie interfejsu bazową spodziewać.
  • Interfejsy nie mogą mieć klas wewnętrznych (jak zauważył @Owen), więc zamiast tego możemy używać klas anonimowych.
  • val i var mogą być emulowane za pomocą statycznej hashmap (i leniwej inicjalizacji) lub przez klienta klasy po prostu przechowującej wartość po swojej stronie (jak robi UserService).
  • Możemy odkryć nasz typ za pomocą this.getClass() w domyślnej metodzie interfejsu.
  • Jak zauważa @Owen, typy zależne od ścieżki są niemożliwe przy użyciu interfejsów, więc pełny wzór ciasta jest z natury niemożliwy. Powyższe pokazuje jednak, że można go użyć do zastrzyku zależności.
+0

Powinieneś mieć dostęp do 'this' i' this.getClass() 'w domyślnej treści metody i możesz dodać dodatkowy stan przez słabą mapę tożsamości. Jednak w przykładzie rejestrowania nie jest to po prostu sposób java; nic złego w prostym/starym rozwiązaniu dodawania pola instancji 'końcowy Logger Logger = Logger.of (this);' w celu uzyskania efektu mixin. – irreputable

+0

Masz rację, zaktualizowałeś wpis, aby to odzwierciedlić. –

+0

dzięki, świetna odpowiedź! –

2

Kilka eksperymentów nie sugerować:

  • Klasy zagnieżdżone są automatycznie statyczne. Jest z natury 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(); 
    
  • Więc, jak widać, są zagnieżdżone interfejsy, choć trudniej namówić się komunikaty o błędach:

    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. 
    

Więc bez klas członkowskich instancja, robisz nie mają typów zależnych od ścieżki, co jest niezbędne w przypadku wzoru tortu. Przynajmniej nie, nie w prosty sposób, nie jest to możliwe.

2

Być może można zrobić coś takiego w 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" 

Ale to nie jest w żaden sposób lepiej niż zwykły/starego podejścia

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" 
+0

Twój pierwszy przykład kompiluje i uruchamia się zgodnie z oczekiwaniami w Java 8. –

1

ignorując nową funkcjonalność w Java 8 można w teorii zrobić wzór ciasta w Javie 5 i powyżej, korzystając z czasu kompilacji AspectJ ITDs.

AspectJ DTO's allow you to make Mixins. Jedyną irytującą rzeczą jest to, że będziesz musiał stworzyć dwa artefakty: aspekt (ITD) i interfejs. Jednak ITD pozwalają robić szalone rzeczy, takie jak dodawanie adnotacji do klas implementujących interfejs.

3

Zrobiłem mały dowód na to ostatnio. Możesz zobaczyć wpis na blogu tutaj: http://thoredge.blogspot.no/2013/01/cake-pattern-in-jdk8-evolve-beyond.html i repozytorium github tutaj: https://github.com/thoraage/cake-db-jdk8

Zasadniczo możesz to zrobić, ale napotkasz co najmniej dwie przeszkody, które sprawiają, że jest mniej zręczny niż Scala. Po pierwsze cechy Scala mogą mieć stan, a interfejs Java nie. Wiele modułów wymaga stanu. Można to naprawić, tworząc ogólny komponent stanu do przechowywania tych informacji, ale musi to być klasa. Przynajmniej po części. Drugi problem polega na tym, że zagnieżdżona klasa w interfejsie jest bardziej zbliżona do statycznej klasy zagnieżdżonej w klasie. Nie można uzyskać dostępu do metod interfejsów bezpośrednio z klasy modułu. Domyślna metoda interfejsu ma dostęp do tego zakresu i może dodać to do konstruktora klasy modułu.

+0

Stan można obsłużyć (w bardzo nieelegancki sposób) za pomocą statycznych map tożsamości, mapowania z (instancja -> wartość) dla każdego pola, a następnie leniwie inicjując go w pobierające dla każdego pola. –

Powiązane problemy