Declaring classes that adopt protocols

Now, we will declare a class that specifies that it conforms to the ComicCharacter protocol in its declaration in the Playground. Instead of specifying a superclass, the class declaration includes the name of the previously declared ComicCharacter protocol after the class name (AngryDog) and the colon (:). We can read the class declaration as "the AngryDog class conforms to the ComicCharacter protocol."

However, the class doesn't implement any of the required properties and methods specified in the protocol, so it doesn't really conform to the ComicCharacter protocol, as shown in the following code. The code file for the sample is included in the swift_3_oop_chapter_05_02 folder:

    open class AngryDog: ComicCharacter { 
 
    } 

The Playground execution will fail because the AngryDog class doesn't conform to the ComicCharacter protocol, so the Swift compiler generates the following errors and notes. We will see similar error messages in the Swift REPL and in the Swift Sandbox:

error: type 'AngryDog' does not conform to protocol 'ComicCharacter'
public class AngryDog: ComicCharacter {
             ^
note: protocol requires property 'nickName' with type 'String'; do you want to add a stub?
    var nickName: String { get set }
        ^
note: protocol requires function 'drawSpeechBalloon(message:)' with type '(String) -> ()'; do you want to add a stub?
func drawSpeechBalloon(message: String)
     ^
note: protocol requires function 'drawSpeechBalloon(destination:message:)' with type '(ComicCharacter, String) -> ()'; do you want to add a stub?
func drawSpeechBalloon(destination: ComicCharacter, message: String)
     ^
note: protocol requires function 'drawThoughtBalloon(message:)' with type '(String) -> ()'; do you want to add a stub?
func drawThoughtBalloon(message: String)
     ^

Now, we will replace the previous declaration of the empty AngryDog class with a class that tries to conform to the ComicCharacter protocol, but it still doesn't achieve its goal. The following lines show the new code for the AngryDog class. The code file for the sample is included in the swift_3_oop_chapter_05_03 folder:

    open class AngryDog: ComicCharacter { 
      var nickName: String = String() 
     
      func speak(message: String) { 
        print("(nickName) -> "(message)"") 
      } 
     
      func think(message: String) { 
        print("(nickName) -> ***(message)***") 
      } 
 
      func drawSpeechBalloon(message: String) { 
        speak(message: message); 
      } 
     
      func drawSpeechBalloon(destination: ComicCharacter, 
      message: String) { 
        speak(message: "(destination.nickName), (message)") 
      } 
     
      func drawThoughtBalloon(message: String) { 
        think(message: message) 
      } 
 
      init (nickName: String) { 
        self.nickName = nickName 
      } 
    } 

The Playground execution will fail because the AngryDog class doesn't conform to the ComicCharacter protocol; therefore, the Swift compiler generates the following errors and notes:

error: property 'nickName' must be declared public because it matches a requirement in public protocol 'ComicCharacter'
var nickName: String = String()
    ^
public 
error: method 'drawSpeechBalloon(message:)' must be declared public because it matches a requirement in public protocol 'ComicCharacter'
func drawSpeechBalloon(message: String) {
     ^
public 
error: method 'drawSpeechBalloon(destination:message:)' must be declared public because it matches a requirement in public protocol 'ComicCharacter'
func drawSpeechBalloon(destination: ComicCharacter, message: String) {
     ^
public 
error: method 'drawThoughtBalloon(message:)' must be declared public because it matches a requirement in public protocol 'ComicCharacter'
func drawThoughtBalloon(message: String) {
     ^
public 

The public ComicCharacter protocol specifies property and method requirements. Thus, when we declare a class that doesn't declare the required properties and methods at least as public, the Swift compiler generates errors and indicates that they have to be declared at least as public to match the protocol requirements. We can declare the required properties and methods with the open access modifier because members declared as open have the same accesibility level as members declared as public and add the chance to be overridden. Think about open as a superset of public.

Tip

Whenever we declare a class that specifies that it conforms to a protocol, it must fulfill all the requirements specified in the protocol. If it doesn't, the Swift compiler will throw errors indicating which requirements aren't fulfilled, like what happened in the previous example. When we work with protocols, the Swift compiler makes sure that the requirements specified in protocols are honored in any class that conforms to them. Hence, when we modify a protocol, the Swift compiler needs to recompile the classes that conform to this protocol.

Finally, we will replace the previous declaration of the AngryDog class with a class that really conforms to the ComicCharacter protocol. The following lines show the new code for the AngryDog class. The code file for the sample is included in the swift_3_oop_chapter_05_04 folder:

    open class AngryDog: ComicCharacter { 
      open var nickName: String = String() 
     
      fileprivate func speak(message: String) { 
        print("(nickName) -> "(message)"") 
      } 
     
      fileprivate func think(message: String) { 
        print("(nickName) -> ***(message)***") 
      } 
     
      open func drawSpeechBalloon(message: String) { 
        speak(message: message); 
      } 
     
      open func drawSpeechBalloon(destination: ComicCharacter, 
      message: String) { 
        speak(message: "(destination.nickName), (message)") 
      } 
     
      open func drawThoughtBalloon(message: String) { 
        think(message: message) 
      } 
     
      init (nickName: String) { 
        self.nickName = nickName 
      } 
    } 

The AngryDog class declares an initializer that assigns the value of the required nickName argument to the nickName stored property. In this case, the ComicCharacter protocol doesn't include any initializer requirement, so the AngryDog class can specify any desired initializer without restrictions.

The class declares the code for the two versions of the drawSpeechBalloon method. Both methods call the private speak method that prints a message with a specific format that includes the nickName value as a prefix. In addition, the class declares the code for the drawThoughtBalloon method, which invokes the private think method, which also prints a message including the nickName value as a prefix.

The AngryDog class implements the property and methods declared in the ComicCharacter protocol. However, the class also declares two fileprivate members, specifically, two fileprivate methods. We will be able to access these two methods only in its own defining source file.

Tip

As long as we implement all the members declared in the protocol or protocols listed in the class declaration, we can add any desired additional member to the class.

Now, we will declare another class that implements the same protocol that the AngryDog class implemented, that is, the ComicCharacter protocol. The following lines show the code for the AngryCat class. The code file for the sample is included in the swift_3_oop_chapter_05_04 folder:

    open class AngryCat: ComicCharacter { 
      open var nickName: String = String() 
     
      open var age: UInt = 0 
     
      open func drawSpeechBalloon(message: String) { 

        if (age > 5) { 
          print("(nickName) -> "Meow (message)"") 
        } else { 
          print("(nickName) -> "Meeeooow Meeeooow (message)"") 
        } 
      } 
     
      open func drawSpeechBalloon(destination: ComicCharacter, 
      message: String) 
      { 
        print("(destination.nickName) === (nickName) ---> 
        "(message)"") 
      } 
     
      open func drawThoughtBalloon(message: String) { 
        print("(nickName) thinks: (message)") 
      } 
     
      init (nickName: String, age: UInt) { 
        self.nickName = nickName 
        self.age = age 
      } 
    } 

The AngryCat class declares an initializer that assigns the value of the required nickName and age arguments to the nickName and age stored properties. The class declares the code for the two versions of the drawSpeechBalloon method. The version that requires only a message argument uses the value of the age property to generate a different message when the age value is greater than 5. In addition, the class declares the code for the drawThoughtBalloon method.

The AngryCat class implements the property and method requirements declared in the ComicCharacter protocol. However, the class also declares an additional property that isn't required by the protocol: age.

If we remove the open keyword in the line that declares the nickName stored property within the AngryCat class, the class won't implement all the required members of the ComicCharacter protocol, at least as public members; therefore, it won't conform to the protocol anymore. The code file for the sample is included in the swift_3_oop_chapter_05_05 folder:

    var nickName: String = String() 

The Playground execution will fail because the AngryCat class doesn't conform to the ComicCharacter protocol anymore, so the Swift compiler generates the following error:

error: property 'nickName' must be declared public because it matches a requirement in public protocol 'ComicCharacter'
var nickName: String = String()
    ^
public 

Thus, the compiler forces us to implement all the members of a protocol in all the classes that we indicate as conforming to a protocol. If we add the open keyword again to the line that declares the nickName property, we will be able to execute the code in the Playground without compiler errors. The code file for the sample is included in the swift_3_oop_chapter_05_06 folder:

    open var nickName: String = String() 

Tip

Protocols in Swift allow us to make sure that the classes that implement them define all the members specified in the protocol. If they don't, the code won't compile.

In this case, the ComicCharacter protocol didn't specify any initializer requirements, so each class that conforms to the protocol can define its initializer without any constraint. AngryDog and AngryCat declare initializers with a different number of arguments.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.138.181.196