2016-09-28 16 views
5

プロトコルで作業するときに、一部の機能を公開し、内部を一部にしたいときに、ベストプラクティスが何であるか疑問に思っています。

Swift 3に貼り付けています。AVPlayerをフレームワークとしています。

私はいくつかのメソッドを公開したいので、 AudioManagerを使用するViewControllerはいくつかのメソッドにアクセスできますが、フレームワーク
- つまりpublicの代わりにアクセス修飾子internalを持つメソッドの外に公開されないメソッドもあります。

私はプロトコル駆動型設計でフレームワークを書いていますが、ほとんどすべての部分にプロトコルが必要です。
プロトコルはフレームワーク内のプロトコルと話しています。
など。メインクラス - AudioManager - はAudioPlayerを持ち、internal関数を
と呼び出すことができるはずです。 pause(reason:)ですが、その方法はinternalであり、フレームワーク外には公開されません。

ここに例があります。内部機能とプロパティを持つ公開プロトコル公開

func pauseBecauseOfRouteChange() { 
    guard let internalPlayer = audioPlayer as? InternalAudioPlayerProtocol else { return } 
    internalPlayer.pause(reason: .routeChange) 
} 

しかし、よりエレガントな解決策がある場合、私は疑問に思って:

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を変更することにより、仕事を得ることができます知っていますか? AudioPlayerProtocolInternalAudioPlayerProtocol洗練ことマーキングなどの
何か...

それとも仲間のプログラマがそれをどのように行うのですか?
フレームワークは、内部使用を意図したメソッドや変数を公開しないと、より美しくなります。

ありがとうございます!

答えて

0

いいえ、そこにこのエレガントな解決策は、これ以上、少なくともプロトコルを検討する際に、ここに理由です:

があなたのフレームワークを使用して、誰かがAudioPlayerProtocolの拡張子を書きたいというシナリオを想像してみて、どのようにしてpause(reason:)メソッドが内部であれば実装できますか?

あなただけのサブクラス化することでそれを達成することができ、このコードは実際にコンパイルします:プロトコルで

public class AudioPlayer: AudioPlayerProtocol { 
    public func pause() { 
     pause(reason: .byUser) 
    } 

    internal func pause(reason: PauseReason) { 
    } 
} 

をパブリックアクセスレベルを持つ誰かがしたい場合、あなたは、単に内部関数の実装を保証することはできませんので、これは、そうではありません混合公開/内部プロトコルを使用してください。

0

プロトコルを社内と社外に分割して、パブリック実装クラスを内部実装に委譲する場合はどうですか?ように

internal protocol InternalAudioPlayerProtocol { 
    func pause(reason: PauseReason) 
} 

public protocol AudioPlayerProtocol { 
    func pause() 
} 

internal class InternalAudioPlayer: InternalAudioPlayerProtocol { 
    internal func pause(reason: PauseReason) { 
    } 
} 

public class AudioPlayer: AudioPlayerProtocol { 
    internal var base: InternalAudioPlayerProtocol 

    internal init(base: InternalAudioPlayerProtocol) { 
     self.base = base 
    } 

    public func pause() { 
     base.pause(reason: .byUser) 
    } 
} 

public protocol AudioManagerProtocol { 
    var audioPlayer: AudioPlayerProtocol { get } 
} 

public class AudioManager: AudioManagerProtocol { 
    internal let base = InternalAudioPlayer() 
    public let audioPlayer: AudioPlayerProtocol 

    public init() { 
     audioPlayer = AudioPlayer(base: base) 
    } 

    internal func handleSomeNotification() {    
     pauseBecauseOfRouteChange() //amongst other things 
    } 

    internal func pauseBecauseOfRouteChange() { 
     base.pause(reason: .routeChange) 
    } 
} 
関連する問題