Scala 特征/蛋糕模式与案例类

发布于 2024-09-26 07:04:45 字数 961 浏览 1 评论 0原文

在我的 Web 应用程序中,授权用户至少有 4 个“方面”:http 会话相关数据、持久数据、facebook 数据、运行时业务数据。

我决定使用案例类组合而不是特征,至少有两个原因:

  • 特征混合可能会导致名称冲突
  • 我想要免费的案例类好东西,例如模式匹配和复制方法

我想知道经验丰富的 scalaists 对这个主题的看法。看起来特质和/或蛋糕模式应该适合这样的任务,但正如我上面提到的,存在问题......很明显,我不仅想快速、简单地实现它,而且还想深入理解它以便使用未来。

那么我的决定到底有没有缺陷和误解,还是正确的呢? 相关代码如下:


case class FacebookUserInfo(name: String, friends: List[Long])
case class HttpUserInfo(sessionId: String, lastInteractionTime: Long, reconnect: Boolean)
case class RuntimeQuizUserInfo(recentScore: Int)
trait UserState {
  def db: User
  def http: HttpUserInfo
}

case class ConnectingUser(db: User, http: HttpUserInfo) extends UserState
case class DisconnectedUser(db: User, http: HttpUserInfo, facebook: Option[FacebookUserInfo]) extends UserState
case class AuthorizedUser(db: User, http: HttpUserInfo, facebook: FacebookUserInfo,
                          quiz: RuntimeQuizUserInfo) extends UserState

In my web application authorized user has at least 4 "facets": http session related data, persistent data, facebook data, runtime business data.

I've decided to go with case class composition instead of traits for at least two reasons:

  • traits mixing can cause name clashes
  • i want the free case class goodies like pattern matching and copy method

I'd like to know experienced scalaists opinions on this subject. It looks like traits and/or cake pattern should be suitable for such tasks but as i've mentioned above there are problems... Its obvious that not only i want to implement it fast and easy but also to understand it in depth for using in future.

So does my decision have any flaws and misunderstanding or is it right?
Related code looks like this:


case class FacebookUserInfo(name: String, friends: List[Long])
case class HttpUserInfo(sessionId: String, lastInteractionTime: Long, reconnect: Boolean)
case class RuntimeQuizUserInfo(recentScore: Int)
trait UserState {
  def db: User
  def http: HttpUserInfo
}

case class ConnectingUser(db: User, http: HttpUserInfo) extends UserState
case class DisconnectedUser(db: User, http: HttpUserInfo, facebook: Option[FacebookUserInfo]) extends UserState
case class AuthorizedUser(db: User, http: HttpUserInfo, facebook: FacebookUserInfo,
                          quiz: RuntimeQuizUserInfo) extends UserState

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

最丧也最甜 2024-10-03 07:04:45

我认为答案很简单:使用继承,只要所有东西都真正“属于”你的对象,只要所有东西都在同一个“问题域”中。

蛋糕模式的目的是分解出对象中需要的部分,但实际上并不是对象的一部分,例如策略、装饰、配置、上下文等。日志记录就是一个典型的例子。一般来说,我们讨论的是您不想“硬连线”事物的情况,例如您会考虑在 Java 中使用 DI 框架(如 Guice 或 Spring)的情况。请参阅http://jonasboner.com/2008/ 10/06/real-world-scala-dependency-injection-di.html 就是一个很好的例子。

通常有助于决定做什么的一个问题是:“我如何测试对象行为?”。如果你发现很难建立一个合适的测试环境,那么你很可能应该解耦事物,这意味着DI,这通常可以通过蛋糕模式方便地实现。

I think the answer is easy: Go with inheritance, as long as everything really "belongs" to your object, as long as everything is in the same "problem domain".

The intention of the cake pattern is to factor out parts of the object that are somehow required, but are not really part of it, e.g. a strategy, a decoration, a configuration, a context etc. Logging would be a typical example. Generally we're talking about situations you don't want to "hard-wire" things, e.g. cases you would consider to use a DI framework (like Guice or Spring) in Java. See http://jonasboner.com/2008/10/06/real-world-scala-dependency-injection-di.html for a good example.

A question that often helps to decide what to do is: "How could I test the object behavior?". If you find it difficult to set up a proper test environment, chances are that you should decouple things, and that means DI, which can be often realized conveniently with the cake pattern.

眼眸里的快感 2024-10-03 07:04:45

第三个选项是使用隐式转换器,又名“pimp my library”这可能不是必需的,因为您可以控制代码。

这完全取决于您希望对象的某些方面的不透明(或透明)程度。您可以向世界其他地方假装它是一个普通的旧案例类,但在内部通过使用隐式使其完成额外的工作。使用 case 类来保存数据是合适的,但我也觉得使用三个类(ConnectingUserDisconnectedUserAuthenticatedUser 来表示同一个对象很尴尬) code>) 取决于她的身份验证状态。

对于 UserState,您可以提供一个提取器,使其行为类似于案例类:

object UserState {
  def unapply(state: UserState) = Some(state.db, state.http)
}

这可以在匹配状态中使用,如下所示:

val user = ConnectingUser(User(), HttpUserInfo("foo", 0, false))
user match {
  case UserState(db, http) => println(http)
}

The third option is to use implicit converters aka "pimp my library," which probably is not necessary since you have the control of the code.

It all depends on how opaque (or transparent) you want to be about certain aspect of your object. You can pretend its a plain old case class to the rest of the world, but internally make it do extra work by using implicits. The use of case class to hold data is appropriate but I also feel that it's awkward to represent the same object using three classes (ConnectingUser, DisconnectedUser, AuthenticatedUser) depending on her state of authentication.

For the UserState, you could provide an extractor so it behaves like a case class:

object UserState {
  def unapply(state: UserState) = Some(state.db, state.http)
}

this can be used in a match state as follows:

val user = ConnectingUser(User(), HttpUserInfo("foo", 0, false))
user match {
  case UserState(db, http) => println(http)
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文