在协议中使用通用Swift枚举

问题描述 投票:1回答:3

[我经常碰到头,比如说我在pod中有一个通用的Manager类可以处理权限,并且在应用程序中,我希望能够对其进行扩展以创建更有意义的方法名称,又名与enum一起用作参数,以使其使用更清晰,更不容易出错。

但是似乎在其他地方创建扩展时不能调用私有方法。

我敢肯定,Generic/AssociatedValue会有更简洁的方法,或者我的模式是错误的...

这是它的简化版:

外部Pod中的类:

public class FeatureDataManager {

    public static let shared = FeatureDataManager()

    private var permissionManager: PermissionManager!

    private init() {
        self.permissionManager = PermissionManager()
    }

    private getPermission(forFeature feature: String) -> Bool {
        return self.permissionManager.isEnable(feature)
    }
}

和应用程序中的扩展名:

extension FeatureDataManager {
    enum FeatureType: String {
        case ads = "ads"
        case showBanner = "banner"
        case showFullScreenPub = "showFullScreenPub"
    }

    public func isPermissionEnable(forFeature feature: FeatureType) {
        // Does not compile, visibility issue
        self.getPermission(forFeature: feature.rawValue)
    }
}

澄清:

FeatureDataManager是Pod中的一类,仅用于检查许多正在使用导入它的应用程序中的String值形式的权限。

我希望每个单独的应用程序都使用它,以定义扩展名,该扩展名将具有自己受支持的权限的有限枚举。假设App A支持广告,但不支持App B。因此,我希望有一个通用方法,当您调用featureManager.isPermissionEnable(.Ads)时,无论何时应用程序,自动完成功能都将提供该应用程序支持的权限列表。另外,将我的字符串权限值包装到枚举中的目的是更容易出错,并且如果名称更改,则只需要将其更改就可以更轻松地进行重构。

objective-c swift design-patterns enums protocols
3个回答
2
投票

您正在寻找的将是“受保护的”级别,它在Swift中不存在,并且如果不创建新的保护级别就不会存在,因为它将破坏编译器的优化。在您的示例中,由于承诺永远不要在此范围之外调用getPermission(forFeature:),因此编译器可以自由地内联它。因此,在您的扩展程序要调用此功能时,它甚至可能不存在。

Swift可能会添加“半公开”的“受保护”级别,但是Swift没有任何此类功能。您将需要重新设计FeatureDataManager才能实现。在您的示例中,这样做并不明显,因为您根本没有提供权限的公共接口,因此不清楚“我希望能够对其进行扩展以创建更有意义的方法名称”的含义。当前有no公共方法名称。如果有一个,那么像您描述的那样编写更方便的语法将很容易。

您能举例说明您希望此扩展改进的调用代码吗?

有关为何使用这种语言的更多信息,请参见Access Control and protected。这不是偶然的。

您注意,您可以在同一文件中执行此操作,这是正确的。 Swift出于风格原因允许这样做(出于代码组织的原因,许多人在单个文件中使用扩展名)。 Swift将同一文件中的所有扩展名都视为主定义中的扩展名。但这不会扩展到其他文件,当然也不会扩展到其他模块。


此通用解决方案如下:

public class FeatureDataManager<Feature>
where Feature: RawRepresentable, Feature.RawValue == String {

    private func getPermission(forFeature feature: String) -> Bool { ... }

    public func isPermissionEnable(forFeature feature: Feature) {
        self.getPermission(forFeature: feature.rawValue)
    }   
}

然后,应用程序将创建功能集并为该功能集创建管理器:

enum AppFeature: String {
    case ads = "ads"
    case showBanner = "banner"
    case showFullScreenPub = "showFullScreenPub"
}

let featureDataManager = FeatureDataManager<AppFeature>()
featureDataManager.isPermissionEnable(forFeature: .ads)

这确实妨碍了轻松创建.shared实例。这是好是坏是有争议的,但是在您想要它的前提下,您需要将其包装起来:

class AppFeatureDataManager {
    enum Feature: String {
        case ads = "ads"
        case showBanner = "banner"
        case showFullScreenPub = "showFullScreenPub"
    }

    static var shared = AppFeatureDataManager()

    let manager = FeatureDataManager<Feature>()

    public func isPermissionEnable(forFeature feature: Feature) {
        manager.isPermissionEnable(forFeature: feature)
    }
}

现在,对于应用程序而言,这有点样板(特别是如果方法多于isPermissionEnable,因此您可以通过这种方式删除样板(完整代码):

public class FeatureDataManager<Feature>
where Feature: RawRepresentable, Feature.RawValue == String {

    private var permissionManager: PermissionManager

    init() {
        self.permissionManager = PermissionManager()
    }

    private func getPermission(forFeature feature: String) -> Bool {
        self.permissionManager.isEnable(feature)
    }

    public func isPermissionEnable(forFeature feature: Feature) {
        self.getPermission(forFeature: feature.rawValue)
    }
}

protocol AppFeatureDataManager {
    associatedtype Feature: RawRepresentable where Feature.RawValue == String
    var manager: FeatureDataManager<Feature> { get }
}

// Here you can write any necessary pass-through functions so the app doesn't have to
extension AppFeatureDataManager {
    public func isPermissionEnable(forFeature feature: Feature) {
        manager.isPermissionEnable(forFeature: feature)
    }
}

//
// Application Developer writes this:
//
class MyGreatAppFeatureDataManager {
    enum Feature: String {
        case ads = "ads"
        case showBanner = "banner"
        case showFullScreenPub = "showFullScreenPub"
    }

    // This is the only thing that's really required
    let manager = FeatureDataManager<Feature>()

    // They're free make this a shared instance or not as they like.
    // That's not something the framework cares about.
    static var shared = MyGreatAppFeatureDataManager()
    private init() {}
}

所有这些,如果FeatureDataManager实际上只是PermissionManager的前端(如您在此描述的那样,我认为这层层太多了。 (也许您的示例已高度简化,因此以下内容不适用。)

如果PermissionManager是公共的,并且真正的目标只是拥有一个更好的前端,我会这样写:

protocol FeatureDataManager {
    associatedtype Feature: RawRepresentable where Feature.RawValue == String
    var permissionManager: PermissionManager { get }
}

extension FeatureDataManager {
    func isPermissionEnable(forFeature feature: Feature) {
        permissionManager.isEnable(feature.rawValue)
    }
}

//
// App developer writes this
//
class MyGreatAppFeatureDataManager: FeatureDataManager {
    enum Feature: String {
        case ads = "ads"
        case showBanner = "banner"
        case showFullScreenPub = "showFullScreenPub"
    }

    // This is the only required boilerplate; the protocol can't do this for you.
    let permissionManager = PermissionManager()

    // And the developer can decide to make it a shared instance if they like,
    // but it's not the business of the framework
    static let shared = MyGreatAppFeatureDataManager()
    private init() {}
}

2
投票

private访问控制限制对实体的使用:

私有访问将实体的使用限制为封闭的声明以及同一文件中该声明的扩展。

Access Control - The Swift Programming Language

如果您希望通过类扩展名访问实体(在另一个文件中,并且在在同一模块/包中:请使用internal访问控制。

如果要通过类扩展名访问实体(在另一个文件中,并且在[[另一个模块/包中]]:请使用public访问控制。


0
投票
您必须声明

fileprivate

© www.soinside.com 2019 - 2024. All rights reserved.