工厂方法
简单的说,工厂方法( factory method)提供了一个接口用来创建对象,并且用一个方法来封装对象实例的创建,可以通过子类来决定创建什么具体的对象。
使用工厂方法可以带来以下好处:
- 将复杂的对象创建代码简单化为一个方法调用。
- 可以创建不同类的实例。
- 缓存对象。
- 对共享资源提供一致性访问方式。.
我们可以考虑使用静态工厂方法(static factory method),这种方式和经典的模式稍有不同,它避免了子类,但是也没有办法来重载(override)这个方法。
在Java中,我们用 new 来调用类的构造函数来实例化一个类。为了实现这个模式,我们使用普通的方式,也就是使用类中的静态方法。
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")
工厂方法定义在一个伴随对象(一个和类型同名的特殊的单例对象)中。下面是这种方式的优劣:
- 重用基类的名字。
- 标准、简洁。
- 比较象构造函数的调用。
- 使用了静态方法。
延迟初始化
延迟初始化(Lazy initialization)是一种特殊的延迟求值的策略,它是一种在对象被初次访问时才初始化的技术。使用它可以推迟(或避免)一些耗时的计算。
Java中比较典型的实现是使用 null 来表示未初始化的状态,但是,如果 null 是一个有用的状态的化,那么就需要用一个单独的标记来说明初始化的过程是不是已经发生。
在多线程环境中需要使用synchronized 来避免不必要的竞争,甚至可以使用 double-checked locking 来保证更有效的同步,这样也会使代码变得更复杂。
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 value)
lazy val x = {
print("(computing x) ")
42
}
print("x = ")
println(x)
// x = (computing x) 42
懒值在scala中可以使用 null,对懒值的访问是线程安全(thread-safe)的。下面是这种模式的优劣:
- 代码简洁.
- 懒值可以使用null.
- 懒值是线程安全的。
- 对初始化过程缺少控制
单例
单例模式限制类只能创建一个对象,并且提供了一个唯一访问点。单例模式也许是Java中最知名的设计模式,下面是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()
在scala中,object可以从类或者接口中继承方法,object可以直接或者通过接口去访问,object在第一次使用时被初始化。
- 含义清晰。
- 语法简洁。
- 需要时初始化。
- 线程安全。
- 不容易通过初始化来控制。
这篇文章主要是翻译Design Patterns in Scala,但有所缩减和改动。