Scala和设计模式
在当前软件设计中最流行的要算GoF这本书中提出的各种设计模式。很多人认为,设计模式对于程序语言(特别是c++/Java)本身的不足之处或多或少有一些弥补,不过如果语言足够强大,模式也许没有必要。
下面Peter Norvig的一个例子就非常有代表性。在有些语言中,使用设计模式在使代码变得自然和简洁,但是模式本身使用方便性也很重要。不妨让我们来看看Scala(一种类似Ruby/Lisp之类的语言,它一定程度上降低了模式实现的繁琐)中常用的一些模式。
1.Singleton(单体模式)
单体模式是一种非常频繁使用到的模式。尽管它是一种最简单的模式,而要在Java语言中实现的纯粹的单体模式却是异常困难。尤其是涉及到著名的double-checked locking 等问题。然而,对于Scala语言,一点也不难。可以说,你只是需要创建一个单体模式的Registry对象。按下面步骤:
object Registry {
def getEntry(): Entry {
}
//Other fields/methods
}
//Create and use the singleton
val entry = Registry.getEntry
这样就行了,唯一的区别是定义一个普通类时使用关键词‘object’而不是‘class’。如果你需要映射回到Java语言,只需要定义一个所有域/方法都是static的类。
2.Strategy(策略模式)
对于把函数功能用一组对象来实现或者带有截止功能的语言来说,策略模式是一个明显的选择。下面以‘征税’为例:
trait TaxPayer
case class Employee(sal: Long) extends TaxPayer
case class NonProfitOrg(funds: BigInt) extends TaxPayer
//Consider a generic tax calculation function. (It can be in TaxPayer also).
def calculateTax[T <: TaxPayer](victim: T, taxingStrategy: (T => long)) = {
taxingStrategy(victim)
}
val employee = new Employee(1000)
//A strategy to calculate tax for employees
def empStrategy(e: Employee) = Math.ceil(e.sal * .3) toLong
calculateTax(employee, empStrategy)
val npo = new NonProfitOrg(100000000)
//The tax calculation strategy for npo is trivial, so we can inline it
calculateTax(nonProfit, ((t: TaxPayer) => 0)
3.Factory(工厂模式)
工厂模式是针对那种对象构造函数不能返回仲裁对象的情况的。 如果你调用对象A的构造函数生成对象,
你得到的是一个对象A。如果你需要其它不同类型的对象,一般创建一个单体工厂。在Scala语言中你也要做这样的事,但是实现的方式更为优雅:
object Car {
def apply(String type) {
type match {
case "Race" => new RaceCar();
case "Normal" => new NormalCar();
case _ => throw new Exception;
}
}
}
//And you can create cars like:
val myCar = Car("Race")
//instead of more verbose
//Car myCar = CarFactory.getInstance().createCar("Ferrari");
基本上,这样你可以利用这个方式,看起来就像一个构造函数调用一个单体对象。
4.Vistor(访问者模式)
访问者模式被许多人认为是有害的,但是它有自己的价值。让我们来看看在Java中典型的使用访问者模式的例子:
abstract class Expression {
...
public void accept(Visitor v);
}
class Identifier extends Expression {
}
class Sum extends Expression {
}
..
class Visitor {
public void visit(Identifier i) { }
public void visit(Sum s) { }
}
典型的Java样本代码如你所熟悉的那样。在Scala语言中,利用模式匹配你可以达到同样的效果:
trait Expression {
...
}
case class Identifier(value: Int) extends Expression {
}
case class Sum extends Expression {
}
object EvalVisitor {
def visit(expr: Expression): Int = expr match {
case (Identifier(v)) => v
case (Sum(e1, e2)) => visit(e1) + visit(e2)
}
}
但是这种模式匹配的代码具有更大的灵活性。
5.Decorator(装饰模式)
最后来看一下装饰模式,既然Scala支持mixins和隐式转换,装饰模式也是简单和自然的。在概念上,有两个途径可以用来给一个对象加入新的功能以扩展现有功能。你可以使用隐式转换,也考虑使用Scala的标准库中的RichInt类,只需要下面的操作:
val range1to10 = 1 to 10
在Scala语言中是调用to把1转换为10。但是在Int类中没有to方法。它怎么处理呢? Scala公司有一个新的概念叫做implicits,来调用你的库。某种程度上,这其实是Ruby的公开类。这是个有趣的事,你可以在任意地方读取它。对我们来说,这里预先包含了一个隐式的转换。Mixins也可以很好的扩展你的类功能。下面考虑一个典型的阅读方面的接口:
trait Reader {
type T
def read: T
}
trait SynchronizedReader extends Reader {
abstract override def read: T = synchronized(super.next)
}
trait BufferedReader extends Reader {
abstract override def read: T = {
//buffering code
super.read
}
}
//A concrete implementation
class FileReader extends Reader {
type T = char
def read: char = ..
}
//Now we can mix in stuff that we need
//Create a FileReader
val f = new FileReader
//create a fileReader which is synchronized
val syncReader = new FileReader with SynchronizedReader
//create a fileReader which is synchronized and buffered
val bsReader = new FileReader with BufferedReader with SynchronizedReader
结论
设计模式是软件设计中很有用的一种指导方式。但是,大部分模式只是解决语言本身的问题,而不是关于设计方面的。要是你有一个好的语言方式,那么大部分的模式将变得无足轻重。比如,一个结构化的语言,虚方法和虚类的概念也是一种设计模式。如果你使用一种足够强大的语言,你需要的设计方式也会更高级。随着发展,它们越将演变为一种更高层次的抽象。
上一篇: 为什么我不建议在当前购买5G手机