Pie template - why it is so complicated

I am trying to learn about a pie template.

I read this blog about it.

Sample code from this blog:

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 

I can simplify this code by removing 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 } 

and it just compiles.

1) Why is the blog code so complicated?

2) Is this an idiomatic way to use a cake template?

3) Why is the Users class needed in this example?

4) This is what the cake template should look like (with this seemingly unnecessary Users class?

5) Or just a simplified version?

+5
source share
1 answer
  • It may seem complicated at first, but as soon as you become familiar with this template, it is just ... collapsible and bulky. For each of your services, you must create an accompanying component that will wrap this service. Therefore, in the above example, you have a UserRepository wrapped by a UserRepositoryComponent . And this is just an abstraction, so you need to have a specific implementation for both the component and the service (i.e. UserRepositoryComponentImpl wrapping UserRepositoryImpl ). And while you have only one service that can be used in your modules, imagine that you need to create dozens of services;)

  • Yes, this is an idiomatic way to use this template. However, there are other variations of this template, for example. thin cake pattern or parfait (a term coined by Dick Wall)

  • You are asking about User , but the simplification of your code was to remove Users , so I will describe both of them. User is a simple case class that should make this example more accessible / understandable. Users , however, were not needed here (this is just another intermediate level of abstraction), and, in my opinion, they add an extra noise to the example.

  • I would say that your simplified version shows what the cake template looks like. You have an abstract UserRepository wrapped inside a UserRepositoryComponent , you have a concrete implementation of both of these signs, and you have some service ( UserInfo ) that requires a user repository (which is “injected” using self-type annotations).

  • Already answered.

+8
source

Source: https://habr.com/ru/post/1262455/


All Articles