我想知道什么是最好的做法是什么当我想要一些功能公开,一些在我内部使用协议时.我正在写一个
AudioManager在Swift 3中包装AVPlayer作为框架.我想要一些公开的方法,例如使用AudioManager的ViewController可以访问某些方法,但某些方法不会暴露在框架之外 – >即具有内部访问修饰符而不是公共修饰符.我正在使用协议驱动设计编写框架,几乎每个部分都应该有一个协议.所以协议正在与框架内的协议进行通信.例如.主类 – AudioManager – 有一个AudioPlayer,应该可以调用它上面的一些内部函数,例如暂停(原因:),但该方法应该是内部的,不会暴露在框架之外.这是一个例子.
internal enum PauseReason { case byUser case routeChange } // Compilation error: `Public protocol cannot refine an internal protocol` public protocol AudioPlayerProtocol: InternalAudioPlayerProtocol { func pause() // I want } internal protocol InternalAudioPlayerProtocol { func pause(reason: PauseReason) // Should only be accessible within the framework } public class AudioPlayer: AudioPlayerProtocol { public func pause() { pause(reason: .byUser) } // This would probably not compile because it is inside a public class... internal func pause(reason: PauseReason) { //I want this to be internal // save reason and to stuff with it later on } } public protocol AudioManagerProtocol { var audioPlayer: AudioPlayerProtocol { get } } public class AudioManager: AudioManagerProtocol { public let audioPlayer: AudioPlayerProtocol init() { audioPlayer = AudioPlayer() NotificationCenter.default.addObserver(self,selector: #selector(handleRouteChange(_:)),name: NSNotification.Name.AVAudioSessionRouteChange,object: nil) } func handleRouteChange(_ notification: Notification) { guard let userInfo = notification.userInfo,let reasonRaw = userInfo[AVAudioSessionRouteChangeReasonKey] as? NSNumber,let reason = AVAudioSessionRouteChangeReason(rawValue: reasonRaw.uintValue) else { print("what could not get route change") } switch reason { case .oldDeviceUnavailable: pauseBecauSEOfRouteChange() default: break } } } private extension AudioManager { func pauseBecauSEOfRouteChange() { audioPlayer.pause(reason: .routeChange) } } // Outside of Audio framework class PlayerViewController: UIViewController { fileprivate let audioManager: AudioManagerProtocol @IBAction didPressPauseButton(_ sender: UIButton) { // I want the `user of the Audio framwwork` (in this case a ViewController) // to only be able to `see` `pause()` and not `pause(reason:)` audioManager.audioPlayer.pause() } }
我知道我可以通过将pauseBecauSEOfRouteChange方法更改为如下所示来实现它:
func pauseBecauSEOfRouteChange() { guard let internalPlayer = audioPlayer as? InternalAudioPlayerProtocol else { return } internalPlayer.pause(reason: .routeChange) }
但我想知道是否有更优雅的解决方案?像标记AudioPlayerProtocol精炼InternalAudioPlayerProtocol …
或者你们的程序员如何做到这一点?如果框架没有公开供内部使用的方法和变量,那么框架会更漂亮!
谢谢!
解决方法
不,没有更优雅的解决方案,至少在考虑协议时,这就是原因:
想象一下,有人使用你的框架想要为AudioPlayerProtocol写一个扩展,如果它是内部的,那么如何实现pause(reason :)方法呢?
你可以通过子类化实现它,这个代码实际上将编译:
public class AudioPlayer: AudioPlayerProtocol { public func pause() { pause(reason: .byUser) } internal func pause(reason: PauseReason) { } }
对于协议,情况并非如此,因为如果具有公共访问级别的人想要使用混合公共/内部协议,则无法保证实现内部功能.