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

时间:2019-10-05 04:40:55

标签: swift swift5

我正在尝试创建一个通用的Plugin类,然后创建一个诸如PluginOnePluginTwo之类的子类,这些子类将通过添加一个函数run()和{{ 1}}属性,以便每个插件都可以执行自定义命令并保存输出。

类似这样的东西:

output

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

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"
  }  
}

然后我将文件解码为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) 中正确创建子类PluginOnePluginTwo,以便每个Plugin

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

请告知

执行第一个答案的结果:

run()

3 个答案:

答案 0 :(得分:1)

最好的方法肯定是协议。但是,如果您想这样做,则可以利用Swift不错的可选转换功能:

for plugin in plugins {
    if let pluginOne = plugin as? PluginOne {
        pluginOne.foo = 0// If you need to set some subclass-specific variable
        pluginOne.run()
    }
    else if let pluginTwo = plugin as? PluginTwo {
        pluginTwo.bar = 0
        pluginTwo.run()
    }
}

如果您想改用协议:

protocol Runnable {//Our new protocol, only containing one method
    func run()
}

class Plugin: Decodable {
    name: String

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

class PluginOne: Plugin, Runnable { //implements Runnable protocol
    output: String?

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

        super.init(name: name)
    }

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

class PluginTwo: Plugin, Runnable { //Also implements runnable protocol
    output: String?

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

        super.init(name: name)
    }

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

//.....

let plugins: [Runnable] = load("plugins.json")//Now we have an array of Runnables!
for plugin in plugins {
    plugin.run()//We know that plugin will implement the Runnable protocol,
                //so we know it contains the run() method!
}

答案 1 :(得分:1)

我认为您需要在插件和插件管理之间进行区分,因为json包含要加载,创建或运行的插件列表,而不是实际的插件。因此,对于此解决方案,我创建了一个单独的PluginManager来容纳插件,以及管理器要使用的协议和枚举

protocol Plugin { //Protocol each plugin must conform to
    func run() -> ()
}

enum PluginType: String { // All supported plugins. To simplify the code but not necessary
    case pluginOne = "PluginOne"
    case pluginTwo = "PluginTwo"
}

管理器类本身,它具有用于从json数据添加插件的add方法,例如,用于运行所有插件的runAll方法

struct PluginManager {
    var plugins: [String: Plugin]

    init() {
        plugins = [:]
    }

    mutating func add(_ type: String, name: String) {
        var plugin: Plugin?
        switch PluginType.init(rawValue: type) {
        case .pluginOne:
            plugin = PluginOne()
        case .pluginTwo:
            plugin = PluginTwo()
        default:
            print("warning unknow plugin type: \(type)")
        }
        if let plugin = plugin {
            plugins[name] = plugin
        }
    }

    func runAll() {
        for (name, plugin) in plugins {
            print("Executing \(name)")
            plugin.run()
        }
    }
}

json解码已简化为解码成字典,然后使用该字典将插件添加到管理器

var pluginManager = PluginManager()
do {
    let plugins = try JSONDecoder().decode([[String: String]].self, from: json)
    for plugin in plugins {
        if let type = plugin["type"], let name = plugin["name"] {
            pluginManager.add(type, name: name)
        }
    }
    pluginManager.runAll()

} catch {
    print(error)
}

答案 2 :(得分:1)

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

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协议的对象数组。