我正在尝试创建一个通用的Plugin
类,然后创建一个诸如PluginOne
和PluginTwo
之类的子类,这些子类将通过添加一个函数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)
中正确创建子类PluginOne
和PluginTwo
,以便每个Plugin
?
我也理解我做错了什么,应该立即解码为子类(如何?)和/或使用协议而不是子类。
请告知
执行第一个答案的结果:
run()
答案 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
协议的对象数组。