本文由 Captain 发表在 ScalaCool 团队博客。java
在上篇 Java 到 Scala 系列中,我想你或多或少在语言特性上对object
有了必定的掌握,在了解完它酷酷的语言特性——让静态回归常态并能简单运用其衍生出的方法后,我今天就来谈谈在现实应用方面本身对它的理解,不知道是否是也会给你一种耳目一新的感受,毕竟「单例对象」做为一种自然的语言特性,华而不实并非咱们想看到的。git
咱们已经知道了object
是做为打破静态而存在的「单例对象」,在 Scala 中,「单例对象」使用频率之高能够和 Java 中的 new 关键词相比,又或是 Spring 中DI(Dependency Injection),因此咱们不得不考虑到一些场景——多线程和性能开销。如今就具体来看看它和 Java 实现的单例模式有什么不一样。github
先来看看 Java 对于单例模式的实现:编程
public class UniqueSingleton {
//类加载时就初始化
private static uniqueSingleton instance = new uniqueSingleton();
private UniqueSingleton() {
System.out.println(("UniqueSingleton is created"));
}
public static UniqueSingleton getInstance() {
return instance;
}
}
复制代码
单例模式就靠以上几行代码实现了,就是这么简单。可是饿汉模式有这么一个缺点,不管你有没有调用它,它在 JVM 加载类这个过程当中都会将单例加载好,因此它并不具有惰性传值(在 Java 中即延迟加载的概念)这个特性。设计模式
public class UniqueSingleton {
//类加载时并未初始化
private static uniqueSingleton instance;
private UniqueSingleton() {
System.out.println(("UniqueSingleton is created"));
}
public static UniqueSingleton getInstance() {
if (instance == null) {
instance = new UniqueSingleton();
}
return instance;
}
}
复制代码
为了解决这个问题,咱们天然得考虑到延迟加载,解决办法也很是简单,相信你也一目了然即在在建立以前加个判断条件。可是问题真的就所有解决了么,其实否则,在单线程环境下,这确实是一种比较完美的方案,可是在多线程状况下呢?试想多个实例同时被建立,咱们能想到的解决办法一般是在整个getInstance
方法前加个synchronize
关键词,可是这同时也带来了很大的性能开销,这并非咱们但愿的。这里不得不提到网上一个大神的问答,他提出一个解决办法——use an enum。安全
public enum EnumExample {
INSTANCE;
private final String[] favoriteComic =
{ "fate", "Dragon Ball" };
public void printFavorites() {
System.out.println(Arrays.toString(favoriteComic));
}
}
复制代码
以上方法除了很简单之外,enum
还提供了序列化机制,防止从新建立新的对象,这个回答也在 Stack Overflow 上得到了最高票的回答。多线程
object 关键字建立一个新的单例类型,就像一个 class 只有一个被命名的实例。若是你熟悉 Java ,在 Scala 中声明一个 object 有些像建立了一个匿名类的实例。 ——引自 Scala 函数式编程app
其实我在上面罗列了这么多 Java 对于单例模式的实现方法以及对于不一样场景所进行的不断的优化,就是为了引出object
,由于object
就没必要考虑这么多,不会像 Java 那样受到场景的约束。ide
举个例子:函数式编程
object Singleton {
def sum(l: List[Int]): Int = l.sum
}
复制代码
看起来代码是否是瞬间优雅了许多。若是感兴趣的话,你能够利用$ javap
反编译一下,能够看到全部方法前都带上了static
关键词,在这里我就不在详述。
由于线程安全不再用担忧是单线程仍是多线程,又或是考虑到延迟加载也只要加上lazy
关键词按需初始化便可,方不方便谁用谁知道。
众所周知,在敲代码中咱们作的最多的事情之一就是先去建立一个对象,这跟咱们建房子先建地基一个道理。咱们但愿有一种模式能让咱们更好去使用它,方便后期的维护,建立型模式也就应运而生,而工厂模式又是建立型模式中的主角之一,我想这设计模式对熟悉 Java 的各位来讲应该是小菜一碟吧。实际上,工厂模式被分红了三类——简单工厂模式、工厂模式以及抽象工厂模式。但我更但愿把它分为两类,在我看来,简单工厂模式更像是工厂模式的一个特例,不能算是严格意义上的模式,可它确确实实实现了建立实例的逻辑与客户端的解耦。
在这里,我会经过 Scala 和 Java 两种不一样的语言来实现简单工厂模式,从而加深你对object
的印象。假设如今有个电脑器材制造厂,同时生产鼠标和键盘,咱们用熟悉的简单工厂模式设计来描述它的业务逻辑。先用 Java 来定义:
//定义产品接口
public interface Product{
public void show();
}
//如下实现了具体产品类
public class Mouse implements Product {
@Override
public void show() {
System.out.println("A mouse has been built");
}
}
public class Keyboard implements Product {
@Override
public void show(){
System.out.println("A keyboard has been built");
}
}
public class SimpleFactory {
public Product produce(String name) {
switch (name) {
case "Mouse":
return new Mouse();
case "Keyboard":
return new Keyboard();
default:
throw new IllegalArgumentException();
}
}
}
//简单使用
public class Test {
public static void main(String[] args) {
SimpleFactory simpleFactory = new SimpleFactory();
Mouse mouse = simpleFactory.produce("Mouse");
mouse.show();
}
}
复制代码
上述代码经过调用SimpleFactory
中的 produce
方法来建立不一样的 Product 子类对象,从而实现建立实例的逻辑与客户端之间解耦,在这里我采用直接判断传入的 key 的方式来实现了简单工厂模式,固然还有其余方式——经过 newInstance 反射等等。那么有人会问,经过 Scala 该怎么实现呢?这时候就要请咱们的主角——单例对象出场了。
要知道 Scala 支持用object
来实现Java中的单例模式,因此咱们能够实现一个SimpleFactory
单例,而不是一个工厂类,具体代码以下:
trait Product {
def show()
}
case class Mouse() extends Product {
def show = println("A mouse has been built")
}
case class Keyboard() extends Product {
def show = println("A keyboard has been built")
}
object SimpleFactory {//object代替class
def produce(name: String): Product = name match {
case "Mouse" => Mouse()
case "Keyboard" => Keyboard()
}
}
object Test extends App {
val mouse: Mouse = SimpleFactory.produce("Mouse")
mouse.show()
}
复制代码
经过以上代码,咱们能够发现,一样是经过判断传值的方式,Scala 也能够垂手可得地实现。但这并非最重要的,值得让咱们注意到的是在测试以前不用再去建立SimpleFactory
对象了,这正是先前讲的object
静态属性在应用层次给咱们带来的便利,或许你会嗤笑这小小简化才有多大的好处。别急,Scala 还为咱们提供了一种语法糖 —— apply
,它本质上相似一个构造方法,这在上篇文章中也有讲到,其实它也能够应用于工厂模式,经过这种方式,咱们能够省略工厂类,只需增长产品类接口的伴生对象就能够实现。
咱们经过判断传入的 name 字符串来建立不一样的对象,因此这里的produce()
方法就显得有点冗余,如何让工厂模式的实现更加的完美呢?用伴生对象来建立刚好能够解决这个问题:
object Product {
def apply(name: String): Product = name match {
case "Mouse" => Mouse()
case "Keyboard" => Keyboard()
}
}
复制代码
而后,咱们就能够如此调用
val mouse: Product = Product("Mouse")
val keyboard: Product = Product("Keyboard")
mouse.show()
keyboard.show()
复制代码
这样之后,调用的体验是否是更好了呢?能够看到,利用object
的特性,咱们在必定程度上改进了 Java 中设计模式的实现,简单工厂模式仅仅也是冰山一角而已。
因为篇幅有限再次只列出简单工厂模式,至于方法工厂模式和抽象工厂模式,有兴趣的话能够看看源码。
最后,总结成一句话,object
做为一种打破静态回归常态、自然的语言特性,它对 Java 的部分特性进行了优化以便咱们能跟好地去理解、去使用,不知道你有没有对此和我产生一些共鸣呢?