So far, we have created many protocols for our animals. Some of these protocols inherit from other protocols; therefore, we have a protocol hierarchy tree. Now, it is time to combine class inheritance with protocol inheritance to recreate our animal classes.
The following lines show the new version of the Animal
class that conforms to the AnimalProtocol
protocol. The code file for the sample is included in the swift_3_oop_chapter_05_12
folder:
open class Animal: AnimalProtocol {
open class var numberOfLegs: Int {
get {
return 0;
}
}
open class var averageNumberOfChildren: Int {
get {
return 0;
}
}
open class var abilityToFly: Bool {
get {
return false;
}
}
open var age: Int
init(age : Int) {
self.age = age
print("Animal created")
}
open class func printALeg() {
preconditionFailure("The pringALeg method must be overriden")
}
open func printLegs() {
for _ in 0..<type(of: self).numberOfLegs {
type(of: self).printALeg()
}
print(String())
}
open class func printAChild() {
preconditionFailure("The printChild method must be overriden")
}
public func printChildren() {
for _ in 0..<type(of: self).averageNumberOfChildren {
type(of: self).printAChild()
}
print(String())
}
public func printAge() {
print("I am (age) years old.")
}
}
The following lines show the new version of the Mammal
class that inherits from the Animal
class and conforms to the MammalProtocol
protocol. The code file for the sample is included in the swift_3_oop_chapter_05_12
folder:
open class Mammal: Animal, MammalProtocol {
open var isPregnant: Bool = false
private func initialize(isPregnant: Bool) {
self.isPregnant = isPregnant
print("Mammal created")
}
override init(age: Int) {
super.init(age: age)
initialize(isPregnant: false)
}
init(age: Int, isPregnant: Bool) {
super.init(age: age)
initialize(isPregnant: isPregnant)
}
}
The following lines show the new version of the DomesticMammal
class that inherits from the Mammal
class and conforms to the DomesticProtocol
protocol. Remember that the DomesticProtocol
protocol doesn't inherit from any other protocol. The code file for the sample is included in the swift_3_oop_chapter_05_12
folder:
open class DomesticMammal: Mammal, DomesticProtocol {
open var name = String()
open var favoriteToy = String()
private func initialize(name: String, favoriteToy: String) {
self.name = name
self.favoriteToy = favoriteToy
print("DomesticMammal created")
}
init(age: Int, name: String, favoriteToy: String) {
super.init(age: age)
initialize(name: name, favoriteToy: favoriteToy)
}
init(age: Int, isPregnant: Bool, name: String,
favoriteToy: String) {
super.init(age: age, isPregnant: isPregnant)
initialize(name: name, favoriteToy: favoriteToy)
}
public func talk() {
print("(name): talks")
}
}
The following lines show the new version of the Dog
class that inherits from the DomesticMammal
class and conforms to the DogProtocol
protocol. The code file for the sample is included in the swift_3_oop_chapter_05_12
folder:
open class Dog: DomesticMammal, DogProtocol { open static override var numberOfLegs: Int { get { return 4; } } open static override var abilityToFly: Bool { get { return false; } } open var breed: String { get { return "Just a dog" } } open var breedFamily: String { get { return "Dog" } } private func initializeDog() { print("Dog created") } override init(age: Int, name: String, favoriteToy: String) { super.init(age: age, name: name, favoriteToy: favoriteToy) initializeDog() } override init(age: Int, isPregnant: Bool, name: String, favoriteToy: String) { super.init(age: age, isPregnant: isPregnant, name: name, favoriteToy: favoriteToy) initializeDog() } public final func printBreed() { print(breed) } public final func printBreedFamily() { print(breedFamily) } open func printBark(times: Int, otherDomestic: DomesticProtocol?, isAngry: Bool) { var bark = "(name)" if let unwrappedOtherDomestic = otherDomestic { bark += " to (unwrappedOtherDomestic.name): " } else { bark += ": " } if isAngry { bark += "Grr " } for _ in 0 ..< times { bark += "Woof " } print(bark) } open func bark() { printBark(times: 1, otherDomestic: nil, isAngry: false) } open func bark(times: Int) { printBark(times: times, otherDomestic: nil, isAngry: false) } open func bark(times: Int, otherDomestic: DomesticProtocol) { printBark(times: times, otherDomestic: otherDomestic, isAngry: false) } open func bark(times: Int, otherDomestic: DomesticProtocol, isAngry: Bool) { printBark(times: times, otherDomestic: otherDomestic, isAngry: isAngry) } open override func talk() { bark() } }
The previous version overloaded bark
methods, which required an otherDomesticMammal
argument of the DomesticMammal
type. The printBark
method required an optional otherDomesticMammal
argument of the DomesticMammal?
type. The new version of the overloaded bark
methods replaces the otherDomesticMammal
argument with otherDomestic
of the DomesticProtocol
type. This way, it is possible to pass any class that implements the DomesticProtocol
protocol. The new version of the printBark
method requires an optional otherDomestic
argument of the DomesticProtocol
type. These changes allow dogs to bark at any other domestic animal, just like the domestic bird we will create later. The previous version was only capable of barking at other domestic mammals. However, in real-life scenarios, dogs do bark at birds.
It is not necessary to make any changes to the classes that inherit from Dog: TerrierDog
and SmoothFoxTerrier
. These classes remain with the same code that we introduced in Chapter 4, Inheritance, Abstraction, and Specialization.
The following lines show the new version of the Cat
class that inherits from the DomesticMammal
class and conforms to the CatProtocol
protocol. The code file for the sample is included in the swift_3_oop_chapter_05_12
folder:
open class Cat: DomesticMammal, CatProtocol {
open static override var numberOfLegs: Int {
get {
return 4;
}
}
open static override var abilityToFly: Bool {
get {
return false;
}
}
open override class var averageNumberOfChildren: Int {
get {
return 6;
}
}
private func initializeCat() {
print("Cat created")
}
override init(age: Int, name: String, favoriteToy: String) {
super.init(age: age, name: name, favoriteToy: favoriteToy)
initializeCat()
}
override init(age: Int, isPregnant: Bool, name: String,
favoriteToy: String) {
super.init(age: age, isPregnant: isPregnant, name: name,
favoriteToy: favoriteToy)
initializeCat()
}
open func printMeow(times: Int) {
var meow = "(name): "
for _ in 0 ..< times {
meow += "Meow "
}
print(meow)
}
open override func talk() {
printMeow(times: 1)
}
open override class func printALeg() {
print("*_*", terminator: String())
}
open override class func printAChild() {
// Print grinning cat face with smiling eyes emoji
print(String(UnicodeScalar(0x01F638)!), terminator: String())
}
}
The following lines show the new version of the Bird
class, which inherits from the Animal
class and conforms to the BirdProtocol
protocol. The code file for the sample is included in the swift_3_oop_chapter_05_12
folder:
open class Bird: Animal, BirdProtocol {
open var feathersColor: String = String()
open static override var numberOfLegs: Int {
get {
return 2;
}
}
private func initializeBird(feathersColor: String) {
self.feathersColor = feathersColor
print("Bird created")
}
override init(age: Int) {
super.init(age: age)
initializeBird(feathersColor: "Undefined / Too many colors")
}
init(age: Int, feathersColor: String) {
super.init(age: age)
initializeBird(feathersColor: feathersColor)
}
}
The following lines show the new version of the DomesticBird
class, which inherits from the Bird
class and conforms to the DomesticProtocol
protocol. Remember that the DomesticProtocol
protocol doesn't inherit from any other protocol. The code file for the sample is included in the swift_3_oop_chapter_05_12
folder:
open class DomesticBird: Bird, DomesticProtocol { open var name = String() open var favoriteToy = String() private func initializeDomesticBird(name: String, favoriteToy: String) { self.name = name self.favoriteToy = favoriteToy print("DomesticBird created") } open func talk() { print("(name): Tweet Tweet") } init(age: Int, name: String, favoriteToy: String) { super.init(age: age) initializeDomesticBird(name: name, favoriteToy: favoriteToy) } init(age: Int, feathersColor: String, name: String, favoriteToy: String) { super.init(age: age, feathersColor: feathersColor) initializeDomesticBird(name: name, favoriteToy: favoriteToy) } }
The new DomesticBird
class adds the favoriteToy
stored property and the talk
method to conform to the DomesticProtocol
protocol. In addition, the initializers add new parameters to make it possible to assign an initial value to favoriteToy
.
The following lines show the new version of the DomesticCanary
class, which inherits from the DomesticBird
class. The code file for the sample is included in the swift_3_oop_chapter_05_12
folder:
open class DomesticCanary: DomesticBird { open override class var averageNumberOfChildren: Int { get { return 5; } } private func initializeDomesticCanary() { print("DomesticCanary created") } override init(age: Int, name: String, favoriteToy: String) { super.init(age: age, name: name, favoriteToy: favoriteToy) initializeDomesticCanary() } override init(age: Int, feathersColor: String, name: String, favoriteToy: String) { super.init(age: age, feathersColor: feathersColor, name: name, favoriteToy: favoriteToy) initializeDomesticCanary() } open override class func printALeg() { print("^", terminator: String()) } open override class func printAChild() { // Print bird emoji print(String(UnicodeScalar(0x01F426)!), terminator: String()) } }
The DomesticCanary
class changes the initializers to match the edits made in its superclass: DomesticBird
.
The following table summarizes the list of protocols to which each of the new versions of the classes we created conforms:
Class name |
Conforms to the following protocol(s) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
The following simplified UML diagram shows the hierarchy tree for the protocols and classes and their relationship:
The following lines create an instance of Dog
named pluto
, an instance of Cat
named marie
, and an instance of DomesticCanary
named tweety
. Then, the next lines call the talk
method for the three instances and make pluto
bark at tweety
. It is possible to use tweety as the otherDomestic
argument for the bark
method because it is an instance of DomesticCanary
, and it conforms to the DomesticProtocol
protocol. The code file for the sample is included in the swift_3_oop_chapter_05_12
folder:
var pluto = Dog(age: 7, name: "Pluto", favoriteToy: "Teddy bear") var marie = Cat(age: 4, isPregnant: true, name: "Marie", favoriteToy: "Tennis ball") var tweety = DomesticCanary(age: 2, feathersColor: "Yellow", name: "Tweety", favoriteToy: "Small bell") tweety.talk() pluto.bark(times: 3, otherDomestic: tweety) marie.talk() pluto.talk()
The following lines show the output generated by the last four lines of code:
Tweety: Tweet Tweet Pluto to Tweety: Woof Woof Woof Marie: Meow Pluto: Woof
If we execute the following lines in the Playground, all of them will display true
as a result because tweety
is an instance of a class that conforms to three protocols: AnimalProtocol
, BirdProtocol
, and DomesticProtocol
. In addition, tweety
belongs to Animal
, Bird
, DomesticBird
, and DomesticCanary
:
print(tweety is AnimalProtocol) print(tweety is BirdProtocol) print(tweety is DomesticProtocol) print(tweety is Animal) print(tweety is Bird) print(tweety is DomesticBird) print(tweety is DomesticCanary)
The following screenshot shows the results of executing the previous lines in the Playground. Note that the Playground uses an icon to let us know that all the is
tests will always be evaluated to true
:
18.188.96.232