Scala设计模式

         
banq
13-10-14 3 4332 4

Design Patterns in Scala一文列举了Java和Scala实现经典GoF设计模式的不同代码,有助于对比两种语言。

工厂模式:
Java代码:


public interface Animal {}

private class Dog implements Animal {}

private class Cat implements Animal {}

public class AnimalFactory {
public static Animal createAnimal(String kind) {
if ("cat".equals(kind)) return new Cat();
if ("dog".equals(kind)) return new Dog();
throw new IllegalArgumentException();
}
}

AnimalFactory.createAnimal("dog");


Scala的代码:


trait Animal
private class Dog extends Animal
private class Cat extends Animal

object Animal {
def apply(kind: String) = kind match {
case "dog" => new Dog()
case "cat" => new Cat()
}
}

Animal("dog")


注意Scala使用了trait替代Java接口,使用Object替代Java的Class。

Lazy initialization懒初始化
懒初始化是懒赋值lazy evaluation的一种特殊实现。

Java代码:


private volatile Component component;

public Component getComponent() {
Component result = component;
if (result == null) {
synchronized(this) {
result = component;
if (result == null) {
component = result = new Component();
}
}
}
return result;
}


Scala代码:


lazy val x = {
print("(computing x) ")
42
}

print("x = ")
println(x)


Scala引入了lazy语法。

单态Singleton

Java代码:


public class Cat implements Runnable {
private static final Cat instance = new Cat();

private Cat() {}

public void run() {
// do nothing
}

public static Cat getInstance() {
return instance;
}
}

Cat.getInstance().run()


Scala代码:


object Cat extends Runnable {
def run() {
// do nothing
}
}

Cat.run()


object语法其实是一个单例对象。

Adapter适配器

Java代码:


public interface Log {
void warning(String message);
void error(String message);
}

public final class Logger {
void log(Level level, String message) { /* ... */ }
}

public class LoggerToLogAdapter implements Log {
private final Logger logger;

public LoggerToLogAdapter(Logger logger) { this.logger = logger; }

public void warning(String message) {
logger.log(WARNING, message);
}

public void error(String message) {
logger.log(ERROR, message);
}
}

Log log = new LoggerToLogAdapter(new Logger());


Scala代码:

trait Log {
def warning(message: String)
def error(message: String)
}

final class Logger {
def log(level: Level, message: String) { /* ... */ }
}

implicit class LoggerToLogAdapter(logger: Logger) extends Log {
def warning(message: String) { logger.log(WARNING, message) }
def error(message: String) { logger.log(ERROR, message) }
}

val log: Log = new Logger()


Scala使用了语法implicit,隐式语法,它不是全局变量,也不是trait那样动态注入,隐藏魔法吧,在因果论中因有两种形式,显式因和隐式因,隐式因是无法显式看到的,所以new Logger还是之前原始类名,但是其实已经被调包。

Decorator适配器

public interface OutputStream {
void write(byte b);
void write(byte[] b);
}

public class FileOutputStream implements OutputStream { /* ... */ }

public abstract class OutputStreamDecorator implements OutputStream {
protected final OutputStream delegate;

protected OutputStreamDecorator(OutputStream delegate) {
this.delegate = delegate;
}

public void write(byte b) { delegate.write(b); }
public void write(byte[] b) { delegate.write(b); }
}

public class BufferedOutputStream extends OutputStreamDecorator {
public BufferedOutputStream(OutputStream delegate) {
super(delegate);
}

public void write(byte b) {
// ...
delegate.write(buffer)
}
}

new BufferedOutputStream(new FileOutputStream(
"foo.txt"))


Scala代码:


trait OutputStream {
def write(b: Byte)
def write(b: Array[Byte])
}

class FileOutputStream(path: String) extends OutputStream { /* ... */ }

trait Buffering extends OutputStream {
abstract override def write(b: Byte) {
// ...
super.write(buffer)
}
}

new FileOutputStream(
"foo.txt") with Buffering

trait语法很适合进行方法拦截或添加。

4
banq
2013-10-14 08:56

值对象valueObject

值对象代表不可变,不变性,一旦构成就再也不改变。值对象是作为DTO的自然实现。

Java代码需要使用final


public class Point {
private final int x, y;

public Point(int x, int y) { this.x = x; this.y = y; }

public int getX() { return x; }

public int getY() { return y; }

public boolean equals(Object o) {
// ...
return x == that.x && y == that.y;
}

public int hashCode() {
return 31 * x + y;
}

public String toString() {
return String.format(
"Point(%d, %d)", x, y);
}
}

Point point = new Point(1, 2)


Scala主要针对可变和不可变提供var和val两个声明:
val point = (1, 2)


type Point = (Int, Int) // Tuple2[Int, Int]
val point: Point = (1, 2)


case class Point(x: Int, y: Int)
val point = Point(1, 2)

Null Object空对象
空对象代表一个对象什么也不做。

Java代码:

public interface Sound {
void play();
}

public class Music implements Sound {
public void play() { /* ... */ }
}

public class NullSound implements Sound {
public void play() {}
}
public class SoundSource {
public static Sound getSound() {
return available ? music : new NullSound();
}
}
SoundSource.getSound().play();

使用空对象就不必检查getSound()是否为空,

scala代码:


trait Sound {
def play()
}

class Music extends Sound {
def play() { /* ... */ }
}

object SoundSource {
def getSound: Option[Sound] =
if (available) Some(music) else None
}

for (sound <- SoundSource.getSound) {
sound.play()
}

Scala使用Option。它可以用来作为一个可选值的占位符。


Strategy策略模式
Java代码:

public interface Strategy {
int compute(int a, int b);
}

public class Add implements Strategy {
public int compute(int a, int b) { return a + b; }
}

public class Multiply implements Strategy {
public int compute(int a, int b) { return a * b; }
}

public class Context {
private final Strategy strategy;

public Context(Strategy strategy) { this.strategy = strategy; }

public void use(int a, int b) { strategy.compute(a, b); }
}

new Context(new Multiply()).use(2, 3);


Scala利用其函数语言的魔力简化了这些方法:

type Strategy = (Int, Int) => Int

class Context(computer: Strategy) {
def use(a: Int, b: Int) { computer(a, b) }
}

val add: Strategy = _ + _
val multiply: Strategy = _ * _

new Context(multiply).use(2, 3)



Command命令模式

java代码:


public class PrintCommand implements Runnable {
private final String s;

PrintCommand(String s) { this.s = s; }

public void run() {
System.out.println(s);
}
}

public class Invoker {
private final List<Runnable> history = new ArrayList<>();

void invoke(Runnable command) {
command.run();
history.add(command);
}
}

Invoker invoker = new Invoker();
invoker.invoke(new PrintCommand("foo"));
invoker.invoke(new PrintCommand(
"bar"));


Scala使用by-name parameter 实现:


object Invoker {
private var history: Seq[() => Unit] = Seq.empty

def invoke(command: => Unit) { // by-name parameter
command
history :+= command _
}
}

Invoker.invoke(println(
"foo"))

Invoker.invoke {
println(
"bar 1")
println(
"bar 2")
}

个人感觉,虽然很简单,大道至简,但是简单得有点像魔术,属于玄了。


Chain of responsibility职责链
Scala代码:


rait EventHandler {
def handle(event: Event)
}

class DefaultHandler extends EventHandler {
def handle(event: Event) { /* ... */ }
}

trait KeyboardHandler extends EventHandler {
abstract override def handle(event: Event) {
if (event.source ==
"keyboard") /* ... */ else super.handle(event)
}
}

trait MouseHandler extends EventHandler {
abstract override def handle(event: Event) {
if (event.source ==
"mouse") /* ... */ else super.handle(event)
}
}

new DefaultHandler with KeyboardHandler with MouseHandler

使用trait 和with语法替代了Java的"."


banq
2013-10-14 09:02

Dependency injection依赖注入

Java代码:


interface Repository {
void save(User user);
}

class DatabaseRepository implements Repository { /* ... */ }

class UserService {
private final Repository repository;

UserService(Repository repository) {
this.repository = repository;
}

void create(User user) {
// ...
repository.save(user);
}
}

new UserService(new DatabaseRepository());


java实现注入需要依赖Spring或JEE之类框架容器,而Scala好像不像之前显得那么从容,这点在发明语言时怎么没有考虑呢?依赖注入大概是针对类的,而不是函数。


trait Repository {
def save(user: User)
}

trait DatabaseRepository extends Repository { /* ... */ }

trait UserService { self: Repository =>
// requires Repository
def create(user: User) {
// ...
save(user)
}
}

new UserService with DatabaseRepository


更多见 Cake pattern :Real-World Scala: Dependency Injection (DI)

lshoo
2013-10-17 08:21

Scala本身的语言特性比Java要强大很多,很多Java的设计模式用Scala就不存在了。
例如DI,Scala的cake pattern, function currying,implicit等实现起来都委优雅。
[该贴被lshoo于2013-10-17 08:58修改过]