Class 层次结构 - class 只能由另一个 class 调用

Class hierarchy - class should only called by another class

我尝试实现 Security class 和 Secret class。在我的整个项目中 Secret class 应该只被 Security.getSecretInstance().doSomeSecretAction()

调用

所以 Secret.doSomeSecretAction() 应该抛出一个编译错误。

我需要 Security.getSecretInstance() 进行身份验证。

我正在寻找一个好的模式或其他东西,但我认为我的搜索关键字太糟糕或者我的要求是 stupid/or 不可能的。

目前我称 Security.getSecretInstance() 它 returns 是 Secret 的单例实例,但我也可以称其为 Secret.doSomeSecretAction()。没有区别。

你有一些模式、关键词或片段给我吗?

编辑 我对真棒的定义是我有这样一种方法:

Security.isAuthorized { secret in
   secret.doSomeSecretAction
 }, failure { 
   print("permission denied")
}

而且我只能用这个.isAuthorized-方法

来保密

我建议做的是声明 Secret 嵌套在 Security 中,使 Secret 私有并在 Security 中创建可以访问 [=12] 的非私有方法=].像这样:

class Security {

    class func doSomeSecretAction() {
        Secret.doSomeSecretAction()
    }

    private class Secret {
        class func doSomeSecretAction(){
            print("Private method called")
        }
    }
}

Security.doSomeSecretAction()

这里,Security.doSomeSecretAction()可以从Securityclass外部调用,但是Secret.doSomeSecretAction()只能在Security内部调用class ].

根据评论更新: 一个可行的解决方案是将 Security 的初始值设定项声明为私有,因此它只能从 Security class 内部调用并声明一个计算变量(现在我称之为 shared) 这是初始化程序的唯一访问点。此计算变量 returns nil 或基于 Security.isAuthorizedSecret class 的新实例。这样,每次调用Secret的函数时,都会检查授权状态,只有状态为授权才能调用该函数,否则shared变量returnsnil,因此不会调用该方法。

class Security {

    static var isAuthorized = false //change this when the authorisation status changes

    class Secret {

        static var shared: Secret? {
            if Security.isAuthorized {
                return Secret()
            } else {
                return nil
            }
        }

        private init(){} //a new instance of Secret can only be created using the `shared` computed variable, the initializer cannot be called directly from outside the Secret class

        func doSomeSecretAction(){
            print("Private method called")
        }
    }
}

Security.Secret.shared //nil
//Security.Secret.init() //if you uncomment this line, you'll get an error saying all initializers are inaccessible
Security.Secret.shared?.doSomeSecretAction() //nil

Security.isAuthorized = true
Security.Secret.shared?.doSomeSecretAction() //function is called

Security.isAuthorized = false
Security.Secret.shared?.doSomeSecretAction() //nil

当 Dávid 编辑他的答案时,我正在研究这个答案;我没有意识到他刚才发布了更新。我们的答案有很多重叠,所以这只是同一方法的另一种风格。

首先我要明确的是,你所描述的只能实现封装,不能"security."我的意思是你可以构建一个系统,让开发者很容易正确使用,而很难正确使用使用不当。这很简单。但是您无法阻止开发人员提取秘密并 运行ning 他们想要的任何代码。这是他们的机器,你给他们代码。他们总是可以 运行 它。他们有调试器;你不会隐藏任何东西。

但是,防止意外误用是一个很好的目标,而且非常简单。首先,您应该使用实例方法,而不是 class 方法。 Class 方法使所有这些变得比需要的更难。您的问题的解决方案看起来像这样,大部分访问控制依赖于 fileprivate

class Security {
    enum Error: Swift.Error {
        case unauthorized
    }

    // This feels like it should be nested in Security, but doesn't have to be
    class Secret {
        // No one outside this file can instantiate one of these. It's likely
        // that you'll be passing some parameters here of course.
        fileprivate init() {}

        // I'm assuming you want these to be single use, so people can't store
        // a reference to them an reuse them. This is one simple way. 
        fileprivate var isAuthorized = true

        private func validate() {
            // I'm treating this kind of reuse as a programming error and
            // crashing. You could throw if you wanted, but it feels like it
            // should never happen given your design.
            guard isAuthorized else { 
                fatalError("Secrets can only be used once") 
            }
        }

        func doSomeSecretAction() {
            // Every "protected" method (which may be all of them) needs to
            // call validate() before running.
            validate()
            print("SECRET!")
        }
    }

    // Public so we can test; obviously this would at least private(set)
    var isAuthorized = false 

    func withAuthorization(execute: (Secret) -> Void) throws {
        guard isAuthorized else { throw Error.unauthorized }

        // We create a new Secret for every access and invalidate it after.
        // That prevents them from being stored and reused.
        let secret = Secret()
        execute(secret)
        secret.isAuthorized = false
    }
}

// -- Some other file

let security = Security()

security.isAuthorized = true // For testing

var stealingTheSecret: Security.Secret?

do {
    try security.withAuthorization {
        [=10=].doSomeSecretAction() // This is good
        stealingTheSecret = [=10=] // Try to steal it for later use
    }
} catch Security.Error.unauthorized {
    print("Unauthorized")
}

stealingTheSecret?.doSomeSecretAction() // Let's use it: Crash!

原则上,您可以通过直接使用 UnsafeMutablePointerSecret 分配内存并在最后销毁它来摆脱 validate() 样板,但这可能比避免一行额外的代码是值得的。

(请注意,您自己分配内存仍然不能保护您免受调用者保存对象的影响;他们总是可以复制内存并使用 .load 重新实例化它;任何不安全的东西你可以这样做,调用者也可以。这也允许他们通过直接修改布尔值或在使它无效之前复制对象来规避 validate()。没有任何技术可以防止不安全的内存访问;这就是为什么你不能保护代码中的秘密。)

经过研究,我找到了一个对我来说既好又简单的解决方案:

class SecurityLayer {

private static var authorized: Bool = false

static func makeAuthorizeCheck() -> API2? {
    if authorized {
        return API2()
    }
    return nil
   }
}

第二个class(不是子class)

class Secret {

  func test() {
    print("test")
  }

  fileprivate init() {

  }
}

例子

  SecurityLayer.makeAuthorizeCheck()?.test() //working
  Secret() //forbidden
  Secret.test() //compiler find this method, but there are no permissions to use this one

Secret 中的构造函数是 private 时,这将不再有效。对我来说 fileprivate 的好处现在很明显了。 !classes 必须在一个文件中!