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()
可以从Security
class外部调用,但是Secret.doSomeSecretAction()
只能在Security
内部调用class ].
根据评论更新:
一个可行的解决方案是将 Security
的初始值设定项声明为私有,因此它只能从 Security
class 内部调用并声明一个计算变量(现在我称之为 shared
) 这是初始化程序的唯一访问点。此计算变量 returns nil
或基于 Security.isAuthorized
的 Secret
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!
原则上,您可以通过直接使用 UnsafeMutablePointer
为 Secret
分配内存并在最后销毁它来摆脱 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 必须在一个文件中!
我尝试实现 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()
可以从Security
class外部调用,但是Secret.doSomeSecretAction()
只能在Security
内部调用class ].
根据评论更新:
一个可行的解决方案是将 Security
的初始值设定项声明为私有,因此它只能从 Security
class 内部调用并声明一个计算变量(现在我称之为 shared
) 这是初始化程序的唯一访问点。此计算变量 returns nil
或基于 Security.isAuthorized
的 Secret
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!
原则上,您可以通过直接使用 UnsafeMutablePointer
为 Secret
分配内存并在最后销毁它来摆脱 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 必须在一个文件中!