swift - 如何在 Swift 5 中正确使用子类

标签 swift swift5

我试图拥有一个通用的 Plugin 类,然后是诸如 PluginOnePluginTwo 之类的子类,它们可以通过添加函数 run()output 属性,以便每个插件可以执行自定义命令并保存输出。

类似这样的事情:

class Plugin: Decodable {
  var name: String

  init(name: String) {
    self.name = name
  }
}

class PluginOne: Plugin {
  var output: String?

  init(name: String, output: String) {
    self.output = output

    super.init(name: name)
  }

  func run() {
    // do something
    self.output = "Some output"
  }  
}

class PluginTwo: Plugin {
  var output: String?

  init(name: String, output: String) {
    self.output = output

    super.init(name: name)
  }

  func run() {
    // do something
    self.output = "Some other output"
  }  
}

现在我从 json 中获取可用插件的列表:

let json = """
[
  { "type": "PluginOne", "name": "First plugin of type one" },
  { "type": "PluginOne", "name": "Second plugin of type one" },
  { "type": "PluginTwo", "name": "abcd" }
]
"""

我正在将文件解码为[Plugin]:

let decoder = JSONDecoder()
let jsonData = Data(json.utf8)
let plugins = try decoder.decode([Plugin].self, from: jsonData)

现在的问题是如何从每个 Plugin 正确创建子类 PluginOnePluginTwo 以便 run() 他们每个人?

此外,我知道我做错了什么,也许应该立即解码为子类(如何?)和/或使用协议(protocol)而不是子类。

请指教

执行第一个答案的结果:

import Foundation

let json = """
[
  { "type": "PluginOne", "name": "First plugin of type one" },
  { "type": "PluginOne", "name": "Second plugin of type one" },
  { "type": "PluginTwo", "name": "abcd" }
]
"""


class Plugin: Decodable {
  var name: String

  init(name: String) {
    self.name = name
  }
}

class PluginOne: Plugin {
  var output: String?

  init(name: String, output: String) {
    self.output = output

    super.init(name: name)
  }

  required init(from decoder: Decoder) throws {
    fatalError("init(from:) has not been implemented")
  }

  func run() {
    // do something
    self.output = "Some output"
  }
}

class PluginTwo: Plugin {
  var output: String?

  init(name: String, output: String) {
    self.output = output

    super.init(name: name)
  }

  required init(from decoder: Decoder) throws {
      fatalError("init(from:) has not been implemented")
  }

  func run() {
    // do something
    self.output = "Some other output"
  }
}

let decoder = JSONDecoder()
let jsonData = Data(json.utf8)
let plugins = try decoder.decode([Plugin].self, from: jsonData)

for plugin in plugins {
    if let pluginOne = plugin as? PluginOne {
        pluginOne.run()
        print(pluginOne.output ?? "empty one")
    }
    else if let pluginTwo = plugin as? PluginTwo {
        pluginTwo.run()
        print(pluginTwo.output ?? "empty two")
    } else {
        print("error")
    }
}
// Result: error error error

最佳答案

对于“如何正确使用子类”这个问题的回答通常是“不要”。 Swift 提供了不同的范例:我们采用 WWDC 2016 视频 Protocol and Value Oriented Programming in UIKit Apps 中概述的面向协议(protocol)编程,而不是面向对象编程。 .

protocol Plugin: Decodable {
    var name: String { get }

    func run()
}

struct PluginOne: Plugin {
    let name: String

    func run() { ... }
}

struct PluginTwo: Plugin {
    let name: String

    func run() { ... }
}

接下来的问题是“如何解析 JSON”,我们将采用 Encoding and Decoding Custom Types 的“手动编码和解码”部分中概述的技术。文档:

struct Plugins: Decodable {
    let plugins: [Plugin]

    init(from decoder: Decoder) throws {
        enum AdditionalInfoKeys: String, CodingKey {
            case type
            case name
        }

        var plugins: [Plugin] = []

        var array = try decoder.unkeyedContainer()

        while !array.isAtEnd {
            let container = try array.nestedContainer(keyedBy: AdditionalInfoKeys.self)

            let type = try container.decode(PluginType.self, forKey: .type)
            let name = try container.decode(String.self, forKey: .name)

            switch type {
            case .pluginOne: plugins.append(PluginOne(name: name))
            case .pluginTwo: plugins.append(PluginTwo(name: name))
            }
        }

        self.plugins = plugins
    }
}

enum PluginType: String, Decodable {
    case pluginOne = "PluginOne"
    case pluginTwo = "PluginTwo"
}

然后您可以执行以下操作:

do {
    let plugins = try JSONDecoder().decode(Plugins.self, from: data)
    print(plugins.plugins)
} catch {
    print(error)
}

这将为您提供符合 Plugin 协议(protocol)的对象数组。

关于swift - 如何在 Swift 5 中正确使用子类,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58245399/

相关文章:

ios - 如何在特定行上查找和替换 UILabel 的值?

swift - 使用 CABasicAnimation 将半径从 0 调整为 100

swift - 如何刷新用SDWebImage缓存的tableView中的存储引用数据?

swift - [weak self] 情况下的快捷闭包语法

swift - 为什么我的 Swift Process() 启动路径会输出 `launch path not accessible` ?

avcapturesession - Swift 5 删除所有 AVCaptureSession 输入

iOS Swift - UITableView didselectrowatindexpath 崩溃

swift - Realm 中具有可选 Int 类型的空数据抛出异常

swift - 为什么使用 Swift 5 对枚举数组进行排序会给出不同的结果?

Swift 5 : String. UTF16View.Index(encodedOffset: l) 弃用