Scala中Apply单例案例的使用

4 阅读3分钟

(一)apply的基本使用

不使用new关键字创建对象?

在 Scala 中,apply 方法放在类的伴生对象(object)里,主要起到 工厂方法的作用,能够让使用者在创建该类实例时省去显式的 new 关键字,并且可以通过重载提供多种构造方式。

当写ClassName(arg1,arg2)时,编译器会自动翻译ClassName.apply(arg1,arg2),这可以让实例化代码更简单。

1.定义一对伴生类和伴生对象

2.在伴生对象中还需要实现apply方法,返回伴生类的实例

object Main {
  /*
  * apply
  * apply方法写在伴生对象中,可以在创建对象的时候,省略new关键字
  *
  * val 对象 = 伴生类() <===>  伴生对象.apply()
  */

  class Person() {
  }

  object Person {
    def apply(): Person = {
      println("apply ……")
      new Person()
    }
  }

  def main(args: Array[String]): Unit = {
    // 创建一个类的对象
    // val p1 = new Person()
    val p1 = Person() // 自动调用apply方法
    val p2 = Person()

    println(p1)
    println(p2)
  }
}

apply 方法的主要用途:

  1. 简化对象创建:省略 new 关键字
  2. 工厂模式:控制对象的创建过程
  3. 集合初始化:如 List(1,2,3)Array("a","b")
  4. 案例类:Scala 自动为 case class 生成 apply 方法

apply 方法是 Scala 中非常重要的语法糖,它让代码更加简洁和函数式。

(二)apply实现单例模式

object Main {
  /*
  * apply
  * apply方法写在伴生对象中,可以在创建对象的时候,省略new关键字
  * 实现单例模式
  * 在伴生对象中 创建一个对象:在apply方法中返回它
  */

  class Person() {
    println("主构造器被调用......")
  }

  object Person {
    val p = new Person()
    def apply(): Person = {
      p
    }
  }

  def main(args: Array[String]): Unit = {
    // 创建一个类的对象
    // val p1 = new Person()
    val p1 = Person() // 自动调用apply方法
    val p2 = Person()

    println(p1)
    println(p2)
    println(p1 == p2) // true
  }
}

在代码中 isEmpty判断是否为空,Some()用来包装一个对象,如果这个对象为空,就得到None对象,否则就返回这个对象。

这个单例模式实现的关键点:

  1. 私有构造函数class Person private () - 防止外部直接 new Person()
  2. 单例实例private val instance = new Person() - 在伴生对象中创建唯一实例
  3. apply方法:返回同一个单例实例
  4. 验证p1 == p2 返回 true,证明是同一个对象
  5. 构造器只调用一次:证明确实实现了单例

(三)案例-日志类

object Main {
    /*
    实现一个日志类:Logger
    1. 把一些操作信息写入到文本文件中。
    2. 实现单例模式
    */
    
    class Logger(filename: String) {
        def log(content: String): Unit = {
            println(s"${content}")
        }
    }
    
    object Logger {
        // 定义一个对象
        var instance: Option[Logger] = None
        
        def apply(filename: String): Logger = {
            if (instance.isEmpty) {
                instance = Some(new Logger(filename))
            }
            instance.get
        }
    }

    def main(args: Array[String]): Unit = {
        val logger1 = Logger("test.log")
        val logger2 = Logger("test.log")
        
        println(logger2 == logger1)  // true
        logger1.log("2005-11-4 09:06:03 上scala课")
        logger2.log("2005-11-5 09:06:03 运动会")
    }
}

这个日志单例模式实现的关键点:

  1. 单例实例管理:使用 Option[Logger] 来管理单例实例
  2. 懒加载:只有在第一次调用时才创建实例
  3. apply方法:提供统一的实例获取接口
  4. 验证单例logger2 == logger1 返回 true
  5. 日志功能log 方法输出日志内容

即使多次调用 Logger("test.log"),也只会创建一个实例,实现了真正的单例模式。

(四)案例-改进日志类

具体实现文件的创建和写入功能。

接下来,我们去实现文件的创建和写入功能。这里需要用java.io.FileWriter类。我们通过这个类来创建一个writer对象,来将具体的日志内容写入指定的文件。

import java.io.FileWriter

object Main {
  /*
  实现一个日志类:Logger
  1. 把一些操作信息写入到文本文件中。导入使用 java.io.FileWriter
  2. 实现单例模式
  */

  class Logger(filename: String) {
    def log(content: String): Unit = {
      // 把内容写入一个文件中
      val writer = new FileWriter(filename, true)
      writer.write(content + "\n")
      writer.close()
      // println(s "${content} ")
    }
  }

  object Logger {
    // 定义一个对象
    var instance: Option[Logger] = None

    def apply(filename: String): Logger = {
      if (instance.isEmpty) {
        instance = Some(new Logger(filename))
      }
      instance.get
    }
  }

  def main(args: Array[String]): Unit = {
    val logger1 = Logger("test.log")
    // val logger2 = Logger("test.log")
    // println(logger2 == logger1)
    logger1.log("2005-11-4 09:06:03 上scala课")
    logger1.log("2005-11-5 09:06:03 运动会")
    logger1.log("2005-11-6 09:06:03 休息")
  }
}

这个文件日志单例模式实现的关键点:

  1. 导入 FileWriterimport java.io.FileWriter
  2. 文件写入:使用 FileWriter 将日志内容写入文件
  3. 追加模式true 参数表示追加内容而不是覆盖
  4. 单例模式:确保只有一个日志实例操作同一个文件
  5. 换行写入:每个日志内容后添加 \n 换行符

每次调用 log 方法都会将内容追加到指定的日志文件中。