原文出处:http://www.artima.com/scalazine/articles/selfless_trait_pattern.html
This article describes a simple Scala design pattern that allows library
designers to provide services that their clients can access either
through mixins or imports. Giving users a choice between mixin
composition and importing makes a library easier to use.
When designing a Scala library, you can partition the services offered
by the library into traits. This gives your users more flexibility:
they can mix into each class only the services they need from your
library.
For example, in ScalaTest
you create a basic suite of tests
by mixing in any of several core Suite
traits:
class MySuite extends Suite // a basic suite of test methods
class MySuite extends FunSuite // a basic suite of test functions
class MySpec extends Spec // a BDD-style suite of tests
class MyeSpec extends FeatureSpec // a higher level BDD-style suite
class MySuite extends FixtureSuite // a suite of tests that take a fixture parameter
You can also mix in any of several other traits that add additional, or modify existing,
behavior of the core trait:
class MySuite extends FunSuite with ShouldMatchers with EasyMockSugar with PrivateMethodTester
Although splitting a library's behavior into composable parts with traits gives
users flexibility, one downside is that
users may end up repeatedly mixing together the same traits, resulting in code
duplication. User can easily eliminate this duplication, however, by creating a convenience
trait that mixes together the behavior they prefer, and then mixing in that convenience
trait into their classes instead. For example:
trait ProjectSpec extends WordSpec with ShouldMatchers with EasyMockSugar
class OneSpec extends ProjectSpec
class TwoSpec extends ProjectSpec
class RedSpec extends ProjectSpec with PrivateMethodTester
class BlueSpec extends ProjectSpec
Besides just mixing together the things they need, users can add value inside their convenience traits.
For example, projects often require many tests that share a common fixture, such as a connection to
a database containing a clean set of test data. This could be addressed simply by creating yet another trait that mixes in
ProjectSpec
and adds the needed pre- and post-test behavior:
trait DBProjectSpec extends ProjectSpec with BeforeAndAfterEach {
override def beforeEach() {
// initialize the database and open the connection
}
override def afterEach() {
// clean up the database and close the connection
}
}
Now test classes that need a database can simply mix in DBProjectSpec
:
class MySpec extends DBProjectSpec
Although the ease of behavior composition afforded by traits is very useful, it has some downsides.
One downside is that name conflicts are difficult to resolve. A user can't, for example, mix together two
traits that contain methods with signatures that cause an overload conflict. Still another downside is
that it is slightly awkward to experiment with the services offered by a trait in the Scala interpreter,
because before the trait's services can be accessed, it must be mixed into some class or object.
Both of these downsides can be addressed by making it easy to import the members of a trait as
an alternative to mixing them in.
Implementing the selfless trait pattern
Scala has two features that make it easy to offer users this choice between mixins and imports.
First, Scala allows users to import the members of any object. Second, Scala allows traits
to have a companion object
---a singleton object that has the same name as its
companion trait
. You implement the selfless trait pattern simply by providing a companion object
for a trait that itself mixes in the trait. Here's a simple example of the selfless trait pattern:
trait Friendly {
def greet() { println("hi there") }
}
object Friendly extends Friendly
Trait Friendly
in this example has one method, greet
. It
also has a companion object, named Friendly
, which mixes in trait
Friendly
. Given this friendly design, client programmers of this
library can access the services of Friendly
either via mix in composition, like this (imports and uses of Friendly
are in bold):
object MixinExample extends Application with Friendly
{
greet()
}
Or by importing the members of the Friendly
companion object, like this:
import Friendly._
object ImportExample extends Application {
greet()
}
Although the external behavior of MixinExample
is the same as ImportExample
,
when MixinExample
invokes greet
it is calling greet
on itself (i.e.
, on
this
), but when ImportExample
invokes greet
, it is calling
greet
on the Friendly
singleton object. This is why being able to fall back on an import
allows users resolve name conflicts. For example, a user would not be able to mix the following
Functional
trait into the same class as Friendly
:
trait Functional {
def greet: String = "hi there"
}
Because Friendly
and Functional
's greet
methods
have the same signature but different return types, they will not overload if mixed into the same class:
object Problem extends Application with Friendly with Functional // Won't compile
By contrast, the offending method can be renamed on import, like this:
import Friendly.{greet => sayHi}
object Solved extends Application with Functional {
sayHi()
println(greet)
}
A good real-world example is trait ShouldMatchers
from ScalaTest, which follows the selfless trait pattern.
I expect the most common way ShouldMatchers
will be used is by mixing it into test classes, often by
means of a convenience trait. Here's an example:
import org.scalatest.WordSpec
import scala.collection.mutable.Stack
import org.scalatest.matchers.ShouldMatchers
class StackSpec extends WordSpec with ShouldMatchers
{
"A Stack" should {
"pop values in last-in-first-out order" in {
val stack = new Stack[Int]
stack.push(1)
stack.push(2)
stack.pop() should be === 2
stack.pop() should be === 1
}
"throw NoSuchElementException if an empty stack is popped" in {
val emptyStack = new Stack[String]
evaluating { emptyStack.pop() } should produce [NoSuchElementException]
}
}
}
Occasionally, however, users may want to experiment with the ShouldMatchers
syntax in the
Scala interpreter, which they can do with an import:
scala> import org.scalatest.matchers.ShouldMatchers._
import org.scalatest.matchers.ShouldMatchers._
scala> Map("hi" -> "there") should (contain key ("hi") and not contain value ("dude"))
scala> List(1, 2, 3) should have length 2
org.scalatest.TestFailedException: List(1, 2, 3) did not have length 2
at org.scalatest.matchers.Matchers$class.newTestFailedException(Matchers.scala:148)
at org.scalatest.matchers.ShouldMatchers$.newTestFailedException(ShouldMatchers.scala:2318)
at org.scalatest.matchers.Matchers$ResultOfHaveWordForSeq.length(Matchers.scala:2891)
at .(:7)
at .()
...
The reason this is called the selfless trait pattern is that it generally only makes sense to do this
with traits that don't declare a self type. (A self type
is a more specific type for this
that restricts what the trait can be mixed into.) One other way in
which traits that follow this
pattern are "selfless" is that rather than forcing users to mix in
their services they instead give users
a choice: either mixing in the trait or importing the
members of its companion object. If you are designing a Scala library
that has a trait
that doesn't declare a self type, consider implementing the Selfless
Trait pattern by creating a companion object that mixes in the trait.
分享到:
相关推荐
"Selfless-crx插件"是一款专为英语环境设计的浏览器扩展程序,其主要功能是自动将用户在浏览网页过程中遇到的"selfie"一词替换为"self portrait"。这款插件旨在提升网络阅读体验,帮助用户在文化语境中更加准确地...
基于SUMO(STR-SUMO)的无私流量路由测试平台 该项目基于SUMO( )构建,该平台提供了交通模拟平台。 STR-SUMO的目标是提供一个测试平台,可以在以下约束条件下对路由策略的性能进行基准测试: ...
将“Selfie”一词替换为“Self Portrait” 扫描网页中是否出现“ selfie”一词,并将其替换为“ self portrait”。 支持语言:English
例如,将"good"替换为"kind, honest, generous, selfless, brave, warm-hearted, sympathetic, honorable, humorous, smart, gentle"等人格特质词,或者在描述事物时用"great, fantastic, splendid, marvelous, ...
此外,信件使用了诸如"concern"、"sacrifice"和"selfless"等词汇,增强了表达的深度和层次。 4. 庆祝节日的习俗:信件提到了母亲节,这是向母亲表示感谢和敬意的特殊日子。通过写信的方式来庆祝这个节日,体现了对...
I am most grateful for your selfless donation. 4. 我代表我的全家再次向你所做的一切表达感谢。祝你万事如意!I, (on behalf of) my whole family/class/school, would like to thank you again for what you ...
学习这些作文,学生可以了解到如何用英语描述情感,如使用诸如"tenderness"、"selfless"等词来描绘母爱的特质;如何构建有深度的比喻,比如"like a beautiful angel"和"a blue sky";以及如何用恰当的句式表达感激,...
- 结束时,可以使用"I must thank you again for your generous help."或"I am most grateful for your selfless donation."这样的句子,以再次确认你的感激之情。 以上是关于如何撰写英语感谢信的全面解析,包括...
这是一个基于B/S结构的通信原理仿真平台的源代码和技术报告还有Demo。它可以帮助你建立一个通信原理网上实验室,为学生提供在线实验。...her selfless help, I couldn’t finish the program in time
题目:—It is selfless the most beautiful teacher—Yang Xiangming to lose his life the child in the river. 答案:B. of; to save 解析:"It is + 形容词 + of sb. to do sth."结构强调的是某人的品质,这里...
1. **无私的合作者** (Selfless Collaborator): 这个特征强调在团队中能够积极合作、分享知识和资源,而不是只关注个人成就。展示这种特质的一个有效方式是在项目中与来自不同背景的同事合作,同时在描述成就时使用...
- 掌握形容词如selfless(无私的)、determined(坚定的)、generous(慷慨的)等描述伟人品质的词语。 2. **语法与句型**: - 使用了not only...but also...结构来描述人物品质,如"He is not only kind-hearted...
- **与either**:指两者之一,例如:"Either parent can attend the school meeting."(父母任何一方都可以参加学校会议。) - **与all**:通常不直接与"parent"的复数形式搭配,因为"all"表示三个或更多,但可以...
1. **词汇填空**:这部分考察了对Nelson Mandela个人特质的形容,如无私(selfless)、随和(easy-going)、慷慨(generous)以及他对帮助贫穷黑人学习的承诺(devoted)。他还作为一名律师(lawyer),为贫困的黑人...
在初中阶段,英语是中考的重要科目之一,而模拟考试是学生们备考的关键环节。这份“初三英语中考模拟考试题和答案OK.doc”文件显然是为帮助学生熟悉考试格式、提升英语水平而设计的。下面我们将对其中的部分题目进行...
5. 英语词汇学习:如"homeless"(无家可归的),"extreme weather"(极端天气),"shelter"(避难所),"selfless act"(无私的行为)等,这些都是与社会问题和人道主义相关的词汇。 6. 预备高考:这部分内容可能是...
The guilt of his deceit weighed heavily upon him, and the image of Nabor's selfless act began to erode his cunning facade. Nabor continued, "My horse is more than just an animal; it represents trust ...
【知识点详解】 在高中英语学习中,撰写感谢信是一项重要的写作技能。感谢信不仅能够体现个人的礼貌与修养,还能有效提升沟通...在实际应用中,这些技巧将帮助他们更好地表达对他人的感激之情,展现良好的社交礼仪。
7. 形容词selfless(无私的)表明品质,用of连接主语,不定式to save表目的,答案为B. of; to save。 8. 句子中表示搭建帐篷过夜,使用put up,答案为B. put up。 9. 回答用一般现在时态表示将来,因此选A. is ...
4. **常用词缀组合**:列举一些常见词缀组合,如“self-”与“-less”构成“selfless”(无私的),让考生理解词缀的组合规律。 5. **记忆策略**:提供词根词缀的记忆方法,如联想记忆、分类记忆等,帮助考生提高...