原文出处:http://www.artima.com/scalazine/articles/stackable_trait_pattern.html
One way to use Scala's traits is as stackable modifications.
In this pattern, a trait (or class) can play one of three roles: the base
, a core
, or a stackable
.
The base trait (or abstract class) defines an abstract interface that all the cores and stackables extend, as shown in Figure 1.
The core traits (or classes) implement the abstract methods defined in the base trait, and provide basic, core functionality.
Each stackable overrides one or more of the abstract methods defined in the base trait, using Scala's abstract
override
modifiers, and provides some behavior and at some point invokes the super
implementation of the same method.
In this manner, the stackables modify the behavior of whatever core they are mixed into.
Figure 1. Roles in the stackable trait pattern.
|
This pattern is similar in structure to the decorator pattern, except
it involves decoration for the purpose of class composition
instead of object composition. Stackable traits decorate
the core traits at compile time, similar to the way
decorator objects modify core objects at run time in the decorator pattern.
As an example, consider stacking modifications to a queue of integers. (This example
is adapted from chapter 12 or Programming in Scala
, by Martin Odersky, Lex Spoon, and Bill Venners.)
The queue will have two operations: put
, which places integers in the queue, and get
, which takes them back out. Queues are
first-in, first-out, so get
should return the integers in the same
order they were put in the queue.
Given a class that implements such a queue, you could define traits to
perform modifications such as these:
-
Doubling
: double all integers that are put in the queue
-
Incrementing
: increment all integers that are put in the queue
-
Filtering
: filter out negative integers from a queue
These three traits represent modifications
, because they modify the
behavior of an underlying "core" queue class rather than defining a full
queue class themselves. The three are also stackable
. You
can select any of the three you like, mix them into a class, and obtain
a new class that has all of the modifications you chose.
An abstract IntQueue
class (the "base") is shown in Listing 1.
IntQueue
has a put
method that
adds new integers to the queue and a get
method that removes and returns them.
A basic implementation of IntQueue
(a "core" class), which uses an ArrayBuffer
, is
shown in Listing 2.
abstract class IntQueue {
def get(): Int
def put(x: Int)
}
Listing 1: Abstract class IntQueue
.
import scala.collection.mutable.ArrayBuffer
class BasicIntQueue extends IntQueue {
private val buf = new ArrayBuffer[Int]
def get() = buf.remove(0)
def put(x: Int) { buf += x }
}
Listing 2: A BasicIntQueue
implemented with an ArrayBuffer
.
Class BasicIntQueue
has a private field holding an array buffer. The
get
method removes an entry from one end of the buffer, while the
put
method adds elements to the other end. Here's how this
implementation looks when you use it:
scala> val queue = new BasicIntQueue
queue: BasicIntQueue = BasicIntQueue@24655f
scala> queue.put(10)
scala> queue.put(20)
scala> queue.get()
res9: Int = 10
scala> queue.get()
res10: Int = 20
So far so good. Now take a look at using traits to modify this
behavior. Listing 3 shows a trait that doubles integers as they are
put in the queue.
The Doubling
trait has two funny things going on. The first is that it
declares a superclass, IntQueue
. This declaration means that the
trait can only be mixed into a class that also extends IntQueue
.
trait Doubling extends IntQueue {
abstract override def put(x: Int) { super.put(2 * x) }
}
Listing 3: The Doubling
stackable modification trait.
The second funny thing is that the trait has a super
call on a
method declared abstract. Such calls are illegal for normal classes,
because they will certainly fail at run time. For a trait, however,
such a call can actually succeed. Since super
calls in a trait are
dynamically bound, the super
call in trait Doubling
will work so
long as the trait is mixed in after
another trait or class
that gives a concrete definition to the method.
This arrangement is frequently needed with traits that implement
stackable modifications. To tell the compiler you are doing this on
purpose, you must mark such methods as abstract
override
. This
combination of modifiers is only allowed for members of traits, not classes, and it
means that the trait must be mixed into some class that has a concrete
definition of the method in question.
Here's how it looks to use the trait:
scala> class MyQueue extends BasicIntQueue with Doubling
defined class MyQueue
scala> val queue = new MyQueue
queue: MyQueue = MyQueue@91f017
scala> queue.put(10)
scala> queue.get()
res12: Int = 20
In the first line in this interpreter session, we define class MyQueue
, which extends
BasicIntQueue
and mixes in Doubling
. We then put a 10 in the
queue, but because Doubling
has been mixed in, the 10 is doubled. When we get an integer from the queue, it is a
20.
Note that MyQueue
defines no new code. It simply identifies a
class and mixes in a trait. In this situation, you could
supply "BasicIntQueue
with
Doubling
" directly to new
instead of defining a named class. It would look as shown in Listing 4:
scala> val queue = new BasicIntQueue with Doubling
queue: BasicIntQueue with Doubling = \$anon\$1@5fa12d
scala> queue.put(10)
scala> queue.get()
res14: Int = 20
Listing 4: Mixing in a trait when instantiating with new
.
To see how to stack modifications, we need to
define the other two modification traits, Incrementing
and Filtering
. Implementations of these traits
are shown in Listing 5:
trait Incrementing extends IntQueue {
abstract override def put(x: Int) { super.put(x + 1) }
}
trait Filtering extends IntQueue {
abstract override def put(x: Int) {
if (x >= 0) super.put(x)
}
}
Listing 5: Stackable modification traits Incrementing
and Filtering
.
Given these modifications, you can now pick and choose which ones you
want for a particular queue. For example, here is a queue that
both filters negative numbers and adds one to all numbers that
it keeps:
scala> val queue = (new BasicIntQueue
| with Incrementing with Filtering)
queue: BasicIntQueue with Incrementing with Filtering...
scala> queue.put(-1); queue.put(0); queue.put(1)
scala> queue.get()
res15: Int = 1
scala> queue.get()
res16: Int = 2
The order of mixins is significant. (Once a trait is mixed into a class, you can alternatively
call it a mixin
.)
Roughly speaking, traits further to the
right take effect first. When you call a method on a class with
mixins, the method in the trait furthest to the right is called first.
If that method calls super
, it invokes the method in the next trait
to its left, and so on. In the previous example, Filtering
's
put
is invoked first, so it removes integers that were negative to
begin with. Incrementing
's put
is invoked second, so it adds
one to those integers that remain.
If you reverse the order, first integers will be incremented, and
then
the integers that are still negative will be discarded:
scala> val queue = (new BasicIntQueue
| with Filtering with Incrementing)
queue: BasicIntQueue with Filtering with Incrementing...
scala> queue.put(-1); queue.put(0); queue.put(1)
scala> queue.get()
res17: Int = 0
scala> queue.get()
res18: Int = 1
scala> queue.get()
res19: Int = 2
Overall, code written in this style gives you a great deal of
flexibility. You can define sixteen different classes by mixing in
these three traits in different combinations and orders. That's a
lot of flexibility for a small amount of code, so you should keep your
eyes open for opportunities to arrange code as stackable modifications.
分享到:
相关推荐
《PyPI 官网下载 | djangocms-stackable-plugins-0.2.4.tar.gz:深入了解Django CMS可堆叠插件与Python云原生生态》 在Python的生态系统中,PyPI(Python Package Index)是核心的软件包仓库,它提供了丰富的Python...
Tinker现在使用Scala令人敬畏的Stackable Trait Pattern,并与强大的Akka Stream紧密集成,以处理任务并行化和流水线操作。 它使您可以轻松读取多个格式化文件并进行合并/处理,并并行化常规文件操作(其中一些与...
Stackable modal Stackable modal是用于模态对话框的库,可以使用z-index位置将其堆叠。 该组件使用引导程序对样式进行样式设置,但默认情况下不包含它-您需要自己导入它。 当您打开第二个对话框时,第一个对话框将...
Stackable-Fetcher-Aws-Signer-v4 一个中间件,用于使用AWS Signature v4签名请求。 安装 npm install --save stackable-fetcher-aws-signer-v4 用法 // Create a new Fetcher instance var fetcher = new ...
网上能找到的版本都比较老了,这个是我在5.13内核上重新修改的。 ... wrapfs 优点: wrapfs是一种简单的堆栈式文件系统,类似于BSD的nullfs。 wrapfs算是比较简单的了,代码少于1800行, 相比eCryptfs和Unionfs...
可堆叠的组件托盘 用于小零件的存储系统,设计用于3D打印和最少的材料使用。 FreeCAD项目中的可自定义尺寸和网格。 此纸盒的灵感来自创作者的 托盘可以与M2 / 2mm开口销锁定在一起 变体 默认尺寸为160mm * 160mm *...
可堆叠空间用于stackable.space的仪表板加入我们的要求- [Node](https://nodejs.org) 4.x or better堆- [React](http://facebook.github.io/react)- [create-react-app]... [Apollo GraphQL]...
可堆叠的端到端集成和验收测试 设置 在上创建一个Local by Flywheel测试站点,该站点仅用于测试。 将此作为插件放置在e2etest.local中并激活它 执行npm install 怎么跑 确保在您的测试站点中激活了此插件。...
标题“Shield: A stackable secure storage system for file sharing in public storage”所涉及的知识点包括: 1. 公共存储环境中的文件共享安全问题:随着越来越多的个人数据存储在公共存储环境中,用户失去对其...
压缩包子文件的文件名称"stackable-controller-master"表明这是一个名为"stackable-controller"项目的主分支代码。在Git等版本控制系统中,"master"分支通常被视为默认的开发主线,包含最新且稳定的代码。 综合以上...
Stackable是古腾堡(Gutenberg)-新的WordPress编辑器的精美现成模块集合。 Stackable的块使您能够构建出色的首页和登录页面。 哲学 Stackable的目标很简单–为每个人提供有用且美观的块。 每个人都应该可以自由和...
一个UITableView子类,可以使用UIStackView为页眉和页脚设置视图数组 要求 iOS 11.0以上 安装 Swift软件包管理器(推荐) 设置Swift包后,将StackableTableView添加为依赖关系就像将其添加到Package.swift的依赖...
BQ79616芯片手册中还提到,该芯片提供了可stackable监控器和独立监控器两种形式的监控方式。可stackable监控器可以实现多个电池模块的监控,而独立监控器则可以单独监控一个电池模块。 四、内部 Cell 平衡功能 BQ...
* 交换机的Competitors包括:Switch4900 edge switch、Switch4400 stackable switch、Switch4200 stackable switch等 安全产品知识点: * 安全产品的类型包括:IPSSecurity gateway、SecBlade VPN module、Billing...
可以快速制作能量光束、光剑、激光、传送门、霓虹灯、电流等效果,包含Win/Mac版本和视频使用教程,支持AE CS5或者更高版本 Create Energy Beams, Lightsabers, Lasers, Portals, ...Stackable FX (可多个特效叠加使用)
Qualified for Automotive Applications n Measures Up to 12 Li-Ion Cells in Series (60V Max) n Stackable Architecture Enables Monitoring High Voltage Battery Stacks n Individually Addressable with 4-Bit...
1. 日期选择器(Datepicker):这是jQuery UI中最常用的组件之一,它提供了一个简洁的日期选择界面,允许用户以日历形式选择日期。开发者可以通过API设置日期格式、初始日期、禁用日期等功能。 2. 对话框(Dialog)...