欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  资讯频道

Scala和设计模式

程序员文章站 2022-03-24 09:09:40
...

 

在当前软件设计中最流行的要算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

 

结论 
设计模式是软件设计中很有用的一种指导方式。但是,大部分模式只是解决语言本身的问题,而不是关于设计方面的。要是你有一个好的语言方式,那么大部分的模式将变得无足轻重。比如,一个结构化的语言,虚方法和虚类的概念也是一种设计模式。如果你使用一种足够强大的语言,你需要的设计方式也会更高级。随着发展,它们越将演变为一种更高层次的抽象。

 

相关标签: 设计模式 Scala