简体   繁体   English

在 Swift 中,如何防止函数在子类上被调用?

[英]In Swift, how do I prevent a function from being called on a subclass?

I have a base class that stores a lot of other data objects within some data structures, and this class manages those data objects in my collections through a group of add / remove functions that keep my data structures in sync.我有一个基类,它在某些数据结构中存储了许多其他数据对象,并且此类通过一组使我的数据结构保持同步的add / remove函数来管理我集合中的这些数据对象。

Now I go to subclass this base class, and the subclass is just like the base class except that it has special rules for what kinds of data objects it will accept (it looks at the values on the data objects, and rejects them if the values aren't right).现在我去子类化这个基类,子类就像基类一样,除了它有关于它将接受什么样的数据对象的特殊规则(它查看数据对象上的值,如果值不正确则拒绝它们不对)。 Because of this "validity" check, I created a new function just for the subclass called change .由于这种“有效性”检查,我为名为change的子类创建了一个新函数。 In the change function, it examines all of the incoming data objects and verifies that they are ok, and replaces all of the data objects in the data structures with these data objects.change功能中,它检查所有传入的数据对象并验证它们是否正常,并用这些数据对象替换数据结构中的所有数据对象。

The problem is that I don't want someone to be able to make a subclass object and allow them to call the base class add / remove functions, because I only want the subclass to be able to be changed through the subclass's change function.问题是我不希望有人能够创建子类对象并允许他们调用基类的add / remove函数,因为我只希望子类能够通过子类的change函数进行更改。

I haven't found a good way to "disable" the use of those base class functions in the subclass.我还没有找到一个好的方法来“禁止”在子类中使用那些基类函数。 I can override the functions and implement empty implementations, but there's no feedback to the caller that the function didn't do anything.我可以override函数并实现空的实现,但是没有向调用者反馈该函数没有做任何事情。 And if I use something like fatalError , it isn't compile time, it's runtime.如果我使用类似fatalError的东西,那不是编译时间,而是运行时间。

My other thoughts are to break the functionality of the base class into multiple protocols, and change the base class to simply have all of the data structures, but conforming to none of the protocols and then have multiple subclasses, where one that wants the add functionality can inherit from the base and additionally conform to the add protocol, but one that doesn't want add or remove can inherit from the base, and simply not conform to any of the protocols and instead create their own change functions to modify the data structures.我的其他想法是将基类的功能分解为多个协议,并将基类更改为仅具有所有数据结构,但不符合任何协议,然后有多个子类,其中一个想要add功能可以从基础继承并另外符合add协议,但是不需要addremove的可以从基础继承,并且根本不符合任何协议,而是创建自己的change函数来修改数据结构.

Here's a simpler hierarchy to explain:这是一个更简单的层次结构来解释:

class Parent {
  
  func doThis() { print("Parent Did This") }
  func doThat() { print("Parent Did That") }
  
}

class Child: Parent {
  
  override func doThis() {
    print("Child Did This")
  }
  
  // I want this to be a compile time error
  override func doThat() {
    print("Not Supported")
    return
  }
  
}

Is there an easier way to "hide" a function in a subclass?有没有更简单的方法来“隐藏”子类中的函数?

EDIT 1编辑 1

To better explain my proposed solution, and whether or not there is an easier way to achieve it with my current hierarchy, here's what the hierarchy would have to look like using some protocols:为了更好地解释我提出的解决方案,以及是否有更简单的方法来使用我当前的层次结构来实现它,下面是使用某些协议的层次结构必须看起来的样子:

protocol CanDoThis {
  func doThis()
}

protocol CanDoThat {
  func doThat()
}

class Parent {
  // Important properties that all children should have
  var property1: Int = 0
  var property2: String = ""
}

class Child1: Parent, CanDoThis {
  func doThis() { print("Child1 Can Do This") }
}

class Child2: Parent, CanDoThat {
  func doThat() { print("Child2 Can Do That") }
}

class Child3: Parent, CanDoThis, CanDoThat {
  func doThis() { print("Child3 Can Do This") }
  func doThat() { print("Child3 Can Do That") }
}

Disclaimer免责声明

The protocol version would probably be better design - see the Liskov Substitution Principle amount the other things you mentioned.协议版本可能是更好的设计 - 请参阅Liskov Substitution Principle以及您提到的其他内容。

Answering the question回答问题

You can use the attribute @available() (see Swift -> Language Reference -> Attributes ).您可以使用属性@available() (参见Swift -> Language Reference -> Attributes )。

class Parent {
    func doThis() { print("Parent Did This") }
    func doThat() { print("Parent Did That") }
}

class Child: Parent {
    override func doThis() {
        print("Child Did This")
    }

    @available(*, unavailable, message:"Child can't doThat")
    override func doThat() {
        print("Not Supported")
    }
}


let parent = Parent()
parent.doThis()
parent.doThat()

let child = Child()
child.doThis()
child.doThat()

You get the following error message on child.doThat() :您在child.doThat()上收到以下错误消息:

'doThat()' is unavailable: Child can't doThat 'doThat()' 不可用:孩子不能这样做

However you can get around this by doing the following (again, see Liskov substitution principle):但是,您可以通过执行以下操作来解决这个问题(再次参见 Liskov 替换原则):

let maybeChild: Parent = Child()
maybeChild.doThis()
maybeChild.doThat()

I'd handle this using @availability .我会使用@availability来处理这个问题。 Something like:就像是:

@available(*, deprecated=1.0, message="Do not call this method")
final func foo(){ //function you want to protect
}

This will give you a compiler warning any time you call the function.每当您调用该函数时,这都会给您一个编译器警告。

Interestingly, marking the function as unavailable will throw a compiler error, making it impossible to build the app if anyone calls this function.有趣的是,将函数标记为unavailable会引发编译器错误,如果有人调用此函数,则无法构建应用程序。 Unfortunately, there is no way to suppress that error in Swift, so that will break it for your good use cases as well.不幸的是,没有办法在 Swift 中抑制该错误,因此对于您的良好用例也会破坏它。 Maybe you can get around this by using self.performSelector("foo") .也许你可以通过使用self.performSelector("foo")来解决这个问题。

Also note I marked foo() as final , which prevents it from being overridden by a subclass.另请注意,我将foo()标记为final ,以防止它被子类覆盖。

What you want is access control.你想要的是访问控制。 Swift's modifier for this is internal . Swift 对此的修饰符是internal You can read more about it here in the docs under Access Control. 您可以在访问控制下的文档中阅读更多相关信息。

I think the most obvious solution is this:我认为最明显的解决方案是:

just set the add and remove function in the base class as private只需将基类中的添加和删除函数设置为私有即可

Example例子

class Parent {
  
  func doThis() { print("Parent Did This") }
  private func doThat() { print("Parent Did That") }
  
}

class Child: Parent {
  
  override func doThis() {
    print("Child Did This")
  }
  
  // This is a compile time error as the subclass will not be allowed to override this class, because it's private.
  override func doThat() {
    print("Not Supported")
    return
  }
  
}

in this example, overriding doThat method will cause a compile time error as the subclass will not be allowed to override this method, because it's private.在此示例中,覆盖 doThat 方法将导致编译时错误,因为不允许子类覆盖此方法,因为它是私有的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM