Scala-如何避免对象工厂的if/else条件



我正在努力解决以下问题。问题

  • 我必须编写一个copy method,以便从一个文件系统复制到另一个。(即本地到hdfs,s3到s3,以及稍后的一些(
  • 这个文件系统(local、s3、hdfs(将来可能会增加,操作(复制、移动、删除(也会增加
  • 有些操作是跨文件系统的(即复制、移动(,有些则不是(删除、列出、查找(
  • 我有一个属性文件,其中包含源和目标位置,以及一些其他字段(即计数(,这些字段可以帮助我了解在哪里复制文件

我试图用以下方式使用Factory解决问题,但它仍然无法解决跨平台操作问题。代码看起来并不优雅。

实现

abstract class FileSystem(propFileURI: String) {
def moveFile(): Unit
}
object FileSystem {
private class HDFSystem(propFileURI: String) extends FileSystem(propFileURI) {
override def moveFile(): Unit = {
println(" HDFS  move file")
}
}
private class S3System(propFileURI: String) extends FileSystem(propFileURI) {
override def moveFile(): Unit = {
println("S3 Move File ")
}
}
def apply(propFileURI: String): Option[FileSystem] = {
val properties: Properties = new Properties()
val source = Source.fromFile( System.getProperty("user.dir")+"\src\main\resources\"+propFileURI).reader
properties.load(source)
val srcPath = properties.getProperty("srcPath")
val destPath = properties.getProperty("destPath")
if (destPath.contains("hdfs")){
Some(new HDFSystem(propFileURI))
}
if (srcPath.contains("s3") && destPath.contains("s3")){
Some(new S3System(propFileURI))
}else{
None
}
}
def main(args: Array[String]): Unit = {
val obj = FileSystem("test.properties")
obj match {
case Some(test) => test.moveFile()
case None => println("None returned")
}
}
}

问题:

  1. moveFile的当前实现仅处理s3->s3hdfs->hdfs。如何对local->hdfslocal->s3实现相同的方法

  2. 如何将HDFSystemS3System移动到单独的文件中?

  3. 如何避免apply方法中的if/else

您可以用模式匹配替换if-else。但是,这不仅仅是if-else的声明,对吧?。因此,它可以写成如下:


sealed abstract class FileSystem(propFileURI: String) {
def moveFile(): Unit
}
case class HDFSystem(propFileURI: String) extends FileSystem(propFileURI) {
override def moveFile(): Unit =
println(" HDFS  move file")
}
case class S3System(propFileURI: String) extends FileSystem(propFileURI) {
override def moveFile(): Unit =
println("S3 Move File ")
}
case class MoveFile(hdfs: Option[HDFSystem] = None, s3: Option[S3System] = None)
object FileSystem {
def apply(propFileURI: String): MoveFile = {
val properties: Properties = new Properties()
val source = Source.fromFile(System.getProperty("user.dir") + "\src\main\resources\" + propFileURI).reader
properties.load(source)
val srcPath = Option(properties.getProperty("srcPath")).fold(false)(_.contains("hdfs"))
val destPath = Option(properties.getProperty("destPath")).fold(false)(_.contains("s3"))
(destPath, srcPath) match {
case (true, true) =>
MoveFile(
hdfs = Option(HDFSystem(propFileURI)),
s3 = Option(S3System(propFileURI))
)
case (false, true) =>
MoveFile(s3 = Option(S3System(propFileURI)))
case (true, false) =>
MoveFile(hdfs = Option(HDFSystem(propFileURI)))
case _ =>
MoveFile()
}
}
}
object TestObj {
def main(args: Array[String]): Unit = {
val obj = FileSystem("test.properties")
(obj.hdfs, obj.s3) match {
case (Some(hdfs), _) => hdfs.moveFile()
case (_, Some(s3)) => s3.moveFile()
case (_, _) => println("None returned")
}
}
}

老实说,我不喜欢上面的实现,并为下面的用例做了一些修改。您可以在没有MoveFile包装的情况下将它们用作ADT:


def testMethod(fs: FileSystem): Unit = {
fs.moveFile()
}
def main(args: Array[String]): Unit = {
// You can have a logic here for which way to go
val obj = S3System("test.properties")
testMethod(obj)
val obj1 = HDFSystem("test.properties")
testMethod(obj1)
}

在这种情况下,您可以完全删除FileSystem对象。如果你想有一些路径检查器,你可以在每个sub-types中都有它们。HdfsSystemS3Sytem应该实现moveFile方法

我将分离FileSystem的创建,它可以在同一文件系统中内部移动文件,也可以在文件系统之间移动文件。

对于简单的文件系统实现,我将创建一个具有各种系统的sealed trait

sealed trait FileSystem {
def moveFile(path: String)
}
object FileSystem {
class HDFSSystem extends FileSystem {
override def moveFile(path: String): Unit = ???
}
class S3FileSystem extends FileSystem {
override def moveFile(path: String): Unit = ???
}
def apply(path: String): Either[Throwable, FileSystem] = {
val properties: Properties = new Properties()
properties.load(
Source
.fromFile(s"${System.getProperty("user.dir")}\src\main\resources\$path")
.reader
)
val srcPath = properties.getProperty("srcPath")
val destPath = properties.getProperty("destPath")
if (!srcPath.equalsIgnoreCase(destPath))
Left(new Exception("Source and dest paths should be equal"))
else {
path.toLowerCase() match {
case s3 if s3.startsWith("s3")       => Right(new S3FileSystem)
case hdfs if hdfs.startsWith("hdfs") => Right(new HDFSSystem)
case _                               => Left(new Exception(s"Received unknown file system prefix: $path"))
}
}
}
}

对于多文件系统传输,我将使用不同的抽象来封装FileSystem。这是一个草图:

abstract class MultiFileSystemTransfer[A <: FileSystem, B <: FileSystem](
val srcSystem: A,
val dstSystem: B
) {
def moveFile(srcPath: String, dstPath: String): Unit
}
object MultiFileSystemTransfer {
class S3ToS3FileSystemTransfer private
extends MultiFileSystemTransfer[FileSystem.S3FileSystem, FileSystem.S3FileSystem] {
override def moveFile(srcPath: String, dstPath: String): Unit = ???
}
}

我们可以进一步改进路径,这些路径实际上源自使用类型成员提供的底层文件系统:

sealed trait FileSystem {
type Path
def moveFile(path: Path)
}
object FileSystem {
class HDFSSystem extends FileSystem {
type Path = String
override def moveFile(path: Path): Unit = ???
}
}
abstract class MultiFileSystemTransfer[A <: FileSystem, B <: FileSystem](
val srcSystem: A,
val dstSystem: B
) {
def moveFile(srcPath: srcSystem.Path, dstPath: dstSystem.Path): Unit
}
object MultiFileSystemTransfer {
class S3ToS3FileSystemTransfer(srcPath: FileSystem.S3FileSystem, dstPath: FileSystem.S3FileSystem)
extends MultiFileSystemTransfer[FileSystem.S3FileSystem, FileSystem.S3FileSystem](
srcPath,
dstPath
) {
override def moveFile(srcPath: srcSystem.Path, dstPath: dstSystem.Path): Unit = ???
}
}

最新更新