Declaring a class that works with a constrained generic type

The following lines declare a PartyError enum that conforms to the ErrorType protocol. This way, we will be able to throw a specific exception in the next class that we will create. The code file for the sample is included in the swift_3_oop_chapter_06_03 folder:

    public enum PartyError: Error { 
      case insufficientMembersToRemoveLeader 
      case insufficientMembersToVoteLeader 
    } 

Tip

In case you worked with previous Swift versions, take into account that Swift 3 renamed ErrorType to Error. Swift 3 uses lowerCamelCase for enumeration values.

The following lines declare a Party class that takes advantage of generics to work with many types. The class name is followed by a less than sign (<), an AnimalElement name that identifies the generic type parameter, a colon (:), and a protocol name that the AnimalElement generic type parameter must conform to, which is the AnimalProtocol protocol. The greater than sign (>) ends the type constraint declaration that is included within angle brackets (< >). Then, we follow it with the where keyword, followed by AnimalElement (which identified the type) and a colon (:) that indicates that the AnimalElement generic type parameter has to be of a type that also conforms to another protocol, that is, the Equatable protocol. The following code highlights the lines that use the AnimalElement generic type parameter. Remember that we imported Foundation in the first line when we started creating the first protocol. We require the import for the arc4random_uniform function. In case you work with the web-based sandbox or Linux, the code won’t use this function because it won’t be easily available. In these cases, the code will execute another line that generates an integer each time we run the code. It is not an exactly equivalent line but it will provide us the results we need for this example. The code file for the sample is included in the swift_3_oop_chapter_06_03 folder:

    open class Party<AnimalElement: AnimalProtocol> where 
    AnimalElement: Equatable { 
      private var members = [AnimalElement]() 
     
      open var leader: AnimalElement 
     
      init(leader: AnimalElement) { 
        self.leader = leader 
        members.append(leader) 
      } 
     
      open func add(member: AnimalElement) { 
        members.append(member) 
        leader.sayWelcomeTo(destination: member) 
      } 
     
      open func remove(member: AnimalElement) throws -> AnimalElement? 
      { 
        if (member == leader) { 
          throw PartyError.insufficientMembersToRemoveLeader 
        } 
        if let memberIndex = members.index(of: member) { 
          let removedMember = members.remove(at: memberIndex) 
          removedMember.sayGoodbyeTo(destination: leader) 
          return removedMember 
        } else { 
          return AnimalElement?.none 
        } 
      } 
     
      open func dance() { 
        for (_, member) in members.enumerated() { 
          member.dance() 
        } 
      } 
     
      open func sing() { 
        for (_, member) in members.enumerated() { 
          member.sing() 
        } 
      } 
     
      open func voteLeader() throws { 
        if (members.count == 1) { 
          throw PartyError.insufficientMembersToVoteLeader 
        } 
         
        var newLeader = leader 
        while (newLeader == leader) { 
          #if os(Linux)
          // The following line of code will only be executed if the 
          // underlying operating system is Linux
          // Only BSD-based operating systems provide 
          // arc4random_uniform in Swift
          // However, take into account that the lines aren't 
          // equivalent
          // We use this solution for this example only and to make it 
          // possible
          // to run the code in either the Swift web-based sandbox or 
          // Swift on Linux
          let randomLeaderIndex = 
          Int(NSDate().timeIntervalSinceReferenceDate) % members.count
          #else
          // The following line runs on macOS, iOS, tvOS and watchOS
          let randomLeaderIndex = 
          Int(arc4random_uniform(UInt32(members.count)))
          #endif
          newLeader = members[randomLeaderIndex] 
        } 
         
        leader.say(message: "(newLeader.name) has been voted as our 
        new party leader.") 
        newLeader.dance() 
        leader = newLeader 
      } 
    } 

Now, we will analyze many code snippets to understand how the code included in the Party<AnimalElement> class works. The following line starts the class body, declares a private Array<AnimalElement> of the type specified by AnimalElement, and initializes it with an empty Array<AnimalElement>. Array uses generics to specify the type of the elements that will be accepted and added to the array. In this case, we will use the array shorthand [AnimalElement] that is equivalent to Array<AnimalElement>, that is, an array of elements whose type is AnimalElement or conforms to the AnimalElement protocol, as follows:

    private var members = [Animalelement]() 

The previous line is equivalent to the following line:

    private var members = Array<AnimalElement>() 

The following line declares an open Leader property whose type is AnimalElement:

    open var leader: AnimalElement 

The following lines declare an initializer that receives a leader argument whose type is AnimalElement. The argument specifies the first party leader and also the first member of the party, that is, the first element added of members Array<AnimalElement>:

    init(leader: AnimalElement) { 
      self.leader = leader 
      members.append(leader) 
    } 

The following lines declare the add method, which receives a member argument whose type is AnimalElement. The code adds the member received as an argument to members Array<AnimalElement> and calls the leader.sayWelcomeTo method with member as an argument to make the party leader welcome the new member:

    open func add(member: AnimalElement) { 
      members.append(member) 
      leader.sayWelcomeTo(member) 
    } 

The following lines declare the remove method, which receives a member argument whose type is AnimalElement, returns an optional AnimalElement (AnimalElement?), and throws exceptions. The throws keyword after the method arguments and before the returned type indicates that the method can throw exceptions. The code checks whether the member to be removed is the party leader. The method throws a PartyError.insufficientMembersToRemoveLeader exception in case the member is the party leader. The code returns an optional AnimalElement (AnimalElement?). The code calls retrives the index for the member received as an argument and then calls the remove method for the Array<AnimalElement> array with this index as an argument. Finally, the code calls the sayGoodbyeTo method for the successfully removed member. This way, the member that leaves the party says goodbye to the party leader. In case the member isn't removed, the method returns none, specifically, AnimalElement?.none:

    open func remove(member: AnimalElement) throws -> AnimalElement? { 
      if (member == leader) { 
        throw PartyError.insufficientMembersToRemoveLeader 
      } 
      if let memberIndex = members.index(of: member) { 
        let removedMember = members.remove(at: memberIndex) 
        removedMember.sayGoodbyeTo(destination: leader) 
        return removedMember 
      } else { 
        return AnimalElement?.none 
      } 
    } 

The following lines declare the dance method, which calls the method with the same name for each member of members Array<AnimalElement>. As we declare the method as open, we will be able to override this method in a future subclass:

    open func dance() { 
      for (_, member) in members.enumerated() { 
        member.dance() 
      } 
    } 

The following lines declare the sing method, which calls the method with the same name for each member of members Array<AnimalElement>. We will also be able to override this method in a future subclass:

    open func sing() { 
      for (_, member) in members.enumerated() { 
        member.sing() 
      } 
    } 

Finally, the following lines declare the voteLeader method, which throws exceptions. As it happened in another method, the throws keyword after the method arguments indicates that the method can throw exceptions. The code makes sure that there are at least two members in members Array<AnimalElement> when we call this method. In case we just have one member, the method throws a PartyError.insufficientMembersToVoteLeader exception. If we have at least two members, the code generates a new random leader for the party, which is different from the existing one. The code calls the say method for the actual leader to explain to the

other party members that another leader is voted. Finally, the code calls the dance method for the new leader and sets the new value to the leader stored property:

    open func voteLeader() throws { 
      if (members.count == 1) { 
        throw PartyError.insufficientMembersToVoteLeader 
      } 
     
      var newLeader = leader 
      while (newLeader == leader) { 
        #if os(Linux)
        // The following line of code will only be executed if the 
        // underlying operating system is Linux
        // Only BSD-based operating systems provide arc4random_uniform 
        // in Swift
        // However, take into account that the lines aren't equivalent
        // We use this solution for this example only and to make it 
        // possible
        // to run the code in either the Swift web-based sandbox or 
        // Swift on Linux
        let randomLeaderIndex = 
        Int(NSDate().timeIntervalSinceReferenceDate) % members.count
        #else
        // The following line runs on macOS, iOS, tvOS and watchOS
        let randomLeaderIndex = 
        Int(arc4random_uniform(UInt32(members.count)))
        #endif

        newLeader = members[randomLeaderIndex] 
      } 
     
      leader.say(message: "(newLeader.name) has been voted as our new 
      party leader.") 
      newLeader.dance() 
      leader = newLeader 
    } 
..................Content has been hidden....................

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