如何创建抽象函数以减少代码重复

时间:2019-04-03 14:50:45

标签: ios swift

除了它们引用的几个变量名称外,我还有一些基本相同的功能。我想对函数进行抽象,这样就不必重复复制代码。这是一个示例函数:

func listenToParticipantNumber() {
    guard let reference = participantNumberReference else {
        return
    }
    guard participantNumberListener == nil else {
        return
    }
    participantNumberListener = backendClient.listenToRtdbProperty(reference) { [weak self] (result: Result<Int, RequestError>) in
        guard let strongSelf = self else {
            return
        }
        switch result {
        case .success(let participantNumber):
            strongSelf.participantNumber = participantNumber
        case .failure:
            break
        }
    }
}

在另一个函数中,我将participantNumberReferenceparticipantNumberparticipantNumberListener切换为不同的变量(它们对我的班级都是私有的),并且块返回类型为{ {1}}。但是该功能的核心布局是相同的。

如何使此过程更干净以重用此代码,而不必重复代码?是否可以以某种方式使用Int来引用我班上的不同变量?

3 个答案:

答案 0 :(得分:0)

要达到此抽象级别,需要大量的准备工作,因此您必须提出更大的问题,以了解您真正在尝试做什么以及值得这样做,但这是一个概述。本质是使用具有属性的字典,而不是显式声明的变量。

现在,您可以将未来的案例添加到PersonType中并对其进行初始化(可能是自动化的),并且该函数将适用于所有案例。可以想象将PersonType声明在类之外以增加分隔。

这可能更适合代码审查,您可以在其中发布更多上下文。

enum PersonType { case Participant, case Leader, case Observer }
struct TrackedObject { var number: Int; var numberReference: ReferenceProtocol; var numberListener: ListenerProtocol; }
// Instead of 9 private variables, make a Dictionary of 3 objects each of
// which have 3 properties
private var persons: [ PersonType: TrackedObject ] = ...

func listenToPersonOfType(personType: PersonType) {
    guard let reference = persons[personType].numberReference else {
        return
    }
    guard persons[personType].numberListener == nil else {
        return
    }
    persons[personType].numberListener = backendClient.listenToRtdbProperty(reference) { [weak self] (result: Result<Int, RequestError>) in
        guard let strongSelf = self else {
            return
        }
        switch result {
        case .success(let number):
            strongSelf.persons[personType].number = number
        case .failure:
            break
        }
    }
}

答案 1 :(得分:0)

我认为您在使用KeyPath的道路上正确。我可能会对此进行一些重构,以便listen方法返回Listener?(无论是哪种类型),并在此之外提升“如果已经设置了侦听器,请不要这样做”。功能。

这将使您遵循以下原则:

func listen<Response>(reference: Int?,  // Or whatever type this is
                      updating keyPath: WritableKeyPath<C, Response>,
                      completion: (Result<Response, Error>) -> Void) -> Listener? {

    guard let reference = reference else { return nil }

    return backendClient.listenToRtdbProperty(reference) { [weak self] (result: Result<Response, Error>) in
        guard var strongSelf = self else {
            return
        }
        switch result {
        case .success(let result):
            strongSelf[keyPath: keyPath] = result
        case .failure:
            break
        }
}

或者您可以通过以下方式保留现有结构:

func listen<Response>(reference: Int?,
                      updating keyPath: WritableKeyPath<C, Response>,
                      forListener listener: WritableKeyPath<C, Listener?>,
                      completion: (Result<Response, Error>) -> Void) {

    guard let reference = reference else { return }

    guard self[keyPath: listener] == nil else {
        return
    }

    var mutableSelf = self
    mutableSelf[keyPath: listener] = backendClient.listenToRtdbProperty(reference) { [weak self] (result: Result<Response, Error>) in
        guard var strongSelf = self else {
            return
        }
        switch result {
        case .success(let result):
            strongSelf[keyPath: keyPath] = result
        case .failure:
            break
        }
    }
}

(所有这些代码中的C都是“此类”。我们还不能写Self来表示“当前类”。)

答案 2 :(得分:0)

所以我这样解决了:

stack script

我实际上是在Rob回答之前完成此操作的,但是我必须使用#!/usr/bin/env stack {- stack script --resolver lts-13.14 --package turtle -} main = print "hello" ,因为使用private func listen<T>( to property: ReferenceWritableKeyPath<LivestreamModel, T?>, withReference propertyReference: ReferenceWritableKeyPath<LivestreamModel, String?>, listener: ReferenceWritableKeyPath<LivestreamModel, DatabaseHandle?>) { guard let reference = self[keyPath: propertyReference] else { return } guard self[keyPath: listener] == nil else { return } self[keyPath: listener] = backendClient.listenToRtdbProperty(reference) { [weak self] (result: Result<T, RequestError>) in switch result { case .success(let value): self?[keyPath: property] = value case .failure: self?[keyPath: property] = nil } } } 时,它会警告ReferenceWritableKeyPath没有下标成员。