给超类添加协议,强制继承自该超类的其他类实现该协议。

5

我是iOS开发的新手,正在实习期间为一款具有相对庞大的Objective-C代码库的应用程序进行微小更改。我从Treehouse学习Swift(哇,我爱他们!)并且刚刚学习了协议。目前,在某些情况下应该使用它们,而导师使用了以下示例。

假设你有一个公司,有两种不同类型的雇员:薪资和小时工(非常普遍)。现在,他们都将继承自名为Employee的超类,并且都必须调用一个名为“pay”的函数,该函数将支付雇员。如何强制执行这些类来实现该函数?当然,可以使用协议,但这将要求您记住将其添加到函数声明中。是否有一种方法可以将协议添加到超类“Employee”中,然后任何继承自该类的内容都必须遵循该协议,该协议是该超类的一部分。是否还有其他方法可以实现这一点?谢谢!

3个回答

6
你所需要的是一个“抽象类”。抽象类的目的是作为具体类继承的基类,但是不能直接实例化。
如果“Employee”是抽象类,那么试图实例化“Employee”的任何尝试都将被编译器报告为错误。你需要实例化“Employee”的具体子类,例如“SalariedEmployee”或“HourlyEmployee”。
“Employee”类的定义将包括“calculatePay”方法是必需的,如果具体子类没有实现该方法,则会产生编译时错误。
现在,坏消息是,Objective-C和Swift都不支持抽象类。
你可以提供一种类似的类,通过提供一个实现了抛出异常方法的方法,如果没有被子类覆盖,则会产生运行时错误,而不是编译时错误。
例如:
class Employee {
    var givenName: String
    var surname: String
    ...

    init(givenName: String, surname: String) {
        self.givenName = givenName
        self.surname = surname
    }

    func calculatePay() -> Float {
        fatalError("Subclasses must override calculatePay")
    }
}


class SalariedEmployee: Employee {

    var salary: Float

    init(givenName: String, surname: String, annualSalary: Float) {
        salary = annualSalary
        super.init(givenName: givenName, surname: surname)
    }

    override func calculatePay() -> Float {
        return salary/12     // Note: No call to super.calculatePay
    }

}

无论是 calculatePay 是作为基类的一部分还是通过扩展分配给基类并添加符合协议,结果都是相同的;
  • Employee 类将需要一个生成某种错误的函数的默认实现
  • 子类未实现该方法不会导致编译时错误

你可以为每个子类单独分配一个协议,例如 Payable,但是由于该协议不是基类的一部分,因此您无法说出这样的话:

 var employees[Employee]

 for e in employees {
     let pay = e.calculatePay()
 }

您需要使用略微复杂的方法:

 for e in employees {
     if e is Payable {
         let pay = e.calculatePay()
     }
 }

这将是我所指的最接近的事物,非常感谢!解释非常清晰,我会尝试你建议的方法。希望他们很快添加抽象类的功能。 - humbleCoder
有一个Swift进化提案的抽象类,但目前被推迟了。 - Paulw11
有趣。我知道它是开源的,这太棒了。我还没有学习过协议扩展,希望这也能对我有所帮助。 - humbleCoder

3

抱歉,目前不支持abstract函数。一个可能的解决方法是在子类没有覆盖此函数时,触发fatalError,如下所示:

protocol YourProtocol {
  func pay()
}

class Employee: YourProtocol {
  func pay() {
     fatalError("Must Override")
  }
}

class SubEmployee: Employee {
  func pay() {
     print("stuff here")
  }
}

这是另一个人提出的建议,谢谢! - humbleCoder

0

我的方法是在类初始化器中将委托作为参数包含进去。请参见下面的代码:

protocol ProtocolExample {
    func somethingNeedsToHappen()
}

// typical class example with delegate property for the required protocol
class ClassExampleA {
    
    var delegate: ProtocolExample!
    
    init() {
    }
    
    func aCriticalMethodWithUpdates() {
        delegate.somethingNeedsToHappen()
    }
}

// use class example in a view controller.  Can easily forget to invoke the delegate and protocol
class MySampleViewControllerA: UIViewController {
    
    var classExampleA : ClassExampleA!
    
    func loadMyData() {
        classExampleA = ClassExampleA()
    }
}

// an alternative approach for the class is to include the delegate parameter in the initializer.
class ClassExampleB {
    
    var delegate: ProtocolExample!
    
    init(delegateForUpdates: ProtocolExample) {
        delegate = delegateForUpdates
    }
    
    func doSomething() {
        delegate.somethingNeedsToHappen()
    }
}

// go to use it and you're reminded that the parameter is required...
class MySampleViewControllerB: UIViewController {
    
    var classExampleB: ClassExampleB!
    
    func loadMyData() {
        classExampleB = ClassExampleB() // error: Missing argument for parameter 'delegateForUpdates' in call
    }
}

// so to avoid error:
class MySampleViewControllerC: UIViewController {
    
    var classExampleB: ClassExampleB!
    
    func loadMyData() {
        classExampleB = ClassExampleB(delegateForUpdates: <#ProtocolExample#>)
    }
}


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接