Как я могу сгенерировать вызов инициализации из строк в Swift - PullRequest
0 голосов
/ 01 февраля 2020
class A {
  let val : Int
  init(val: Int) {
     self.val = val
  }
}

У меня есть 3 строки:

let className = "A"
let argName = "val"
let argValue = "4"

Как мне позвонить A(val:4), используя эти 3 строки?

Ответы [ 2 ]

1 голос
/ 02 февраля 2020

Так как вы отметили в комментариях, что все типы будут подклассами некоторого супертипа, тогда этот супертип может обрабатывать всю диспетчеризацию. В Какао это довольно распространенный шаблон, известный как кластер классов.

class SuperA {
    enum SuperAError: Error {
        case cannotConstruct
    }

    static func create(className: String, argName: String, argValue: String) throws -> SuperA {
        switch className {
        case "A":
            guard argName == "val",
                let value = Int(argValue)
                else { throw SuperAError.cannotConstruct }
            return A(val: value)

        default:
            throw SuperAError.cannotConstruct
        }
    }
}

Теперь мне не особенно нравится этот подход. Этот вид подклассов, как правило, плохой Swift. Swift подходит для классов, когда вам требуется ссылочный тип, но он не поддерживает создание подклассов. Я бы сделал это с помощью протокола Buildable и Builder:

enum BuildableError: Error {
    case unknownType
    case badParameters
}

protocol Buildable {
    init(argName: String, argValue: String) throws
    // ... and the rest of the methods you require ...
}

struct A {
    var val: Int
}

extension A: Buildable {
    init(argName: String, argValue: String) throws {
        guard argName == "val", let value = Int(argValue) else {
            throw BuildableError.badParameters
        }

        self.init(val: value)
    }
}

final class Builder {
    var buildables: [String: Buildable.Type] = [:]

    func build(className: String, argName: String, argValue: String) throws -> Buildable {
        guard let buildable = buildables[className] else {
            throw BuildableError.unknownType
        }

        return try buildable.init(argName: argName, argValue: argValue)
    }
}

let builder = Builder()
builder.buildables["A"] = A.self
builder.build(className: "A", argName: "val", argValue: "4")

Если это приводит к дублированию кода, существуют простые способы решения этой проблемы с другими протоколами. Например, если многие из ваших типов имеют init(val: Int), они могут поделиться кодом с другим протоколом:

protocol ValIntBuildable: Buildable {
    init(val: Int)
}

extension ValIntBuildable {
    init(argName: String, argValue: String) throws {
        guard argName == "val", let value = Int(argValue) else {
            throw BuildableError.badParameters
        }

        self.init(val: value)
    }
}

extension A: ValIntBuildable {}
1 голос
/ 01 февраля 2020

Только на родном Свифте нельзя. Swift не является Dynami c таким образом, что вы можете создать экземпляр произвольного класса на основе строкового имени класса и так далее. Objective- C является Dynami c и имеет способы сделать это, поэтому, если для вас это важно, сделайте A подклассом NSObject и напишите эту часть кода в Objective- C (или используйте эквивалентный Cocoa / obj c - звонки в рабочее время).

...