Reputation: 1725
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.
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
. 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.
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.
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. And if I use something like fatalError
, it isn't compile time, it's runtime.
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.
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?
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") }
}
Upvotes: 9
Views: 8148
Reputation: 17576
The protocol version would probably be better design - see the Liskov Substitution Principle amount the other things you mentioned.
You can use the attribute @available()
(see 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()
:
'doThat()' is unavailable: Child can't doThat
However you can get around this by doing the following (again, see Liskov substitution principle):
let maybeChild: Parent = Child()
maybeChild.doThis()
maybeChild.doThat()
Upvotes: 17
Reputation: 1522
I think the most obvious solution is this:
just set the add and remove function in the base class as private
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.
Upvotes: 0
Reputation: 2369
I'd handle this using @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. Unfortunately, there is no way to suppress that error in Swift, so that will break it for your good use cases as well. Maybe you can get around this by using self.performSelector("foo")
.
Also note I marked foo()
as final
, which prevents it from being overridden by a subclass.
Upvotes: 5
Reputation: 455
What you want is access control. Swift's modifier for this is internal
. You can read more about it here in the docs under Access Control.
Upvotes: 1