2017-01-07 1 views
5

J'essaie d'en apprendre davantage sur le modèle de gâteau. Je lis le blog this à ce sujet.modèle de gâteau - pourquoi est-il si compliqué

Le code exemple de ce blog est:

case class User (name:String,email:String,supervisorId:Int,firstName:String,lastName:String) 

trait UserRepository { 
    def get(id: Int): User 
    def find(username: String): User 
} 
trait UserRepositoryComponent { 

    def userRepository: UserRepository 

    trait UserRepository { 
    def get(id: Int): User 
    def find(username: String): User 
    } 
} 
trait Users { 
    this: UserRepositoryComponent => 

    def getUser(id: Int): User = { 
    userRepository.get(id) 
    } 

    def findUser(username: String): User = { 
    userRepository.find(username) 
    } 
} 
trait UserInfo extends Users { 
    this: UserRepositoryComponent => 

    def userEmail(id: Int): String = { 
    getUser(id).email 
    } 

    def userInfo(username: String): Map[String, String] = { 
    val user = findUser(username) 
    val boss = getUser(user.supervisorId) 
    Map(
     "fullName" -> s"${user.firstName} ${user.lastName}", 
     "email" -> s"${user.email}", 
     "boss" -> s"${boss.firstName} ${boss.lastName}" 
    ) 
    } 
} 
trait UserRepositoryComponentImpl extends UserRepositoryComponent { 

    def userRepository = new UserRepositoryImpl 

    class UserRepositoryImpl extends UserRepository { 

    def get(id: Int) = { 
     ??? 
    } 

    def find(username: String) = { 
     ??? 
    } 
    } 
} 

object UserInfoImpl extends 
    UserInfo with 
    UserRepositoryComponentImpl 

je peux simplifier ce code en supprimant Users:

package simple { 

    case class User(name: String, email: String, supervisorId: Int, firstName: String, lastName: String) 

    trait UserRepository { 
    def get(id: Int): User 

    def find(username: String): User 
    } 

    trait UserRepositoryComponent { 

    def userRepository: UserRepository 

    trait UserRepository { 
     def get(id: Int): User 

     def find(username: String): User 
    } 

    } 

    trait UserInfo { 
    this: UserRepositoryComponent => 

    def userEmail(id: Int): String = { 
     userRepository.get(id).email 
    } 

    def userInfo(username: String): Map[String, String] = { 
     val user = userRepository.find(username) 
     val boss = userRepository.get(user.supervisorId) 
     Map(
     "fullName" -> s"${user.firstName} ${user.lastName}", 
     "email" -> s"${user.email}", 
     "boss" -> s"${boss.firstName} ${boss.lastName}" 
    ) 
    } 
    } 

    trait UserRepositoryComponentImpl extends UserRepositoryComponent { 

    def userRepository = new UserRepositoryImpl 

    class UserRepositoryImpl extends UserRepository { 

     def get(id: Int) = { 
     ??? 
     } 

     def find(username: String) = { 
     ??? 
     } 
    } 

    } 

    object UserInfoImpl extends 
    UserInfo with 
    UserRepositoryComponentImpl 

} 

et il compile très bien.

1) Pourquoi le code du blog est-il si compliqué?

2) Est-ce la façon idiomatique d'utiliser le modèle de gâteau?

3) Pourquoi la classe Users est-elle nécessaire dans cet exemple?

4) Est-ce que la façon dont le modèle de gâteau censé ressembler (avec cette classe Users apparemment inutile?

5) Ou est la version simplifiée très bien?

+1

le blog a une section de commentaire. pourquoi ne commencez-vous pas par demander à l'auteur? – pedrorijo91

+0

:) :) :) :) :) :) - vous êtes drôle – jhegedus

+0

désolé, n'a pas remarqué, juste vu le commentaire d'il y a 3 ans .. ** Je n'ai pas beaucoup creusé dans le code **, mais Je pense que c'est juste un autre niveau d'indirection.cela peut aider dans les tests si vous voulez mock/stub ce comportement – pedrorijo91

Répondre

8
  1. Au début, il pourrait ressembler à c'est compliqué, mais une fois que vous vous familiariser avec ce modèle, il est juste ... boilerplaty et encombrant. Pour chaque service, vous devez créer un composant d'accompagnement qui englobera ce service. Donc, dans l'exemple fourni, vous avez un UserRepository enveloppé par un UserRepositoryComponent. Et ceci est seulement l'abstraction, donc vous devez avoir une implémentation concrète pour le composant et le service (c'est-à-dire UserRepositoryComponentImpl enveloppant UserRepositoryImpl). Et jusqu'à présent vous n'avez qu'un seul service qui pourrait être utilisé dans vos modules, imaginez l'effort de créer des dizaines de services;)

  2. Oui, c'est la façon idiomatique d'utiliser ce modèle. Cependant, il existe également d'autres variantes de ce modèle, par ex. thin cake pattern ou parfait (un terme inventé par Dick Wall)

  3. Vous demandez au sujet User, mais votre simplification du code a été de supprimer Users, donc je vais décrire les deux. User est une classe de cas simple, qui devrait rendre cet exemple plus accessible/plus facile à saisir. Users cependant n'étaient pas nécessaires ici (c'est juste un autre niveau intermédiaire d'abstraction), et à mon avis ils apportent du bruit inutile à l'exemple.

  4. Je dirais que votre version simplifiée montre exactement comment le motif de gâteau devrait ressembler. Vous avez un résumé UserRepository enveloppé à l'intérieur de UserRepositoryComponent, vous avez une implémentation concrète de ces deux traits, et vous avez un service (UserInfo) qui nécessite le référentiel d'utilisateurs (qui est "injecté" en utilisant l'annotation de type auto).

  5. Vous avez déjà répondu.

+0

Merci pour l'explication détaillée, j'ai corrigé la question 3 et 4, en effet je voulais dire 'Users', donc l'écriture 'Utilisateur' il y avait une faute de frappe que j'ai faite. – jhegedus

+1

Malheureusement, je ne pouvais donner qu'un seul vote pour cette réponse détaillée. – ipoteka