Swift 5中如何正确使用子类
How to properly use subclasses in Swift 5
我正在尝试使用通用的 Plugin
class 然后是 class 子 PluginOne
和 PluginTwo
来扩展 class 通过添加函数 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
正确创建子classes PluginOne
和 PluginTwo
以便 run()
每个?
我也知道我做错了什么,也许应该立即解码成一个 subclass(如何?)and/or 使用协议而不是 subclasses。
请指教
执行第一个答案的结果:
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 的不错的可选转换功能:
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!
}
我认为您需要将插件和插件管理分开,因为 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"
}
管理器 class 本身,它有一个从 json 数据添加插件的添加方法,例如 运行All 方法 运行 所有插件
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)
}
在回答“如何正确使用子类”这个问题时,往往是“不要”。 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
协议的对象数组。
我正在尝试使用通用的 Plugin
class 然后是 class 子 PluginOne
和 PluginTwo
来扩展 class 通过添加函数 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
正确创建子classes PluginOne
和 PluginTwo
以便 run()
每个?
我也知道我做错了什么,也许应该立即解码成一个 subclass(如何?)and/or 使用协议而不是 subclasses。
请指教
执行第一个答案的结果:
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 的不错的可选转换功能:
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!
}
我认为您需要将插件和插件管理分开,因为 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"
}
管理器 class 本身,它有一个从 json 数据添加插件的添加方法,例如 运行All 方法 运行 所有插件
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)
}
在回答“如何正确使用子类”这个问题时,往往是“不要”。 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
协议的对象数组。