Reader small image

You're reading from  Object???Oriented Programming with Swift 2

Product typeBook
Published inJan 2016
Reading LevelIntermediate
Publisher
ISBN-139781785885693
Edition1st Edition
Languages
Tools
Right arrow
Author (1)
Gaston C. Hillar
Gaston C. Hillar
author image
Gaston C. Hillar

Gaston C. Hillar is Italian and has been working with computers since he was 8 years old. Gaston has a Bachelor's degree in computer science (graduated with honors) and an MBA. Currently, Gaston is an independent IT consultant and a freelance author who is always looking for new adventures anywhere in the world. He was a senior contributing editor at Dr. Dobb's, and has written more than a hundred articles on software development topics. He has received the prestigious Intel Black Belt Software Developer award eight times. He has written many articles about Java for Oracle Java Magazine. Gaston was also a former Microsoft MVP in technical computing. He lives with his wife, Vanesa, and his two sons, Kevin and Brandon.
Read more about Gaston C. Hillar

Right arrow

Chapter 6. Maximization of Code Reuse with Generic Code

In this chapter, you will learn about parametric polymorphism and how Swift implements this object-oriented concept through the possibility to write generic code. We will use classes that work with one and two constrained generic types.

In addition, we will learn to combine generic code with inheritance and multiple inheritance to demonstrate the usage of generic code in real-life situations in which the code becomes more complex than the usage of a simple generic class.

Understanding parametric polymorphism and generic code


Let's imagine we want to organize a party of specific animals. We don't want to mix cats with dogs because the party would end up with the dogs chasing cats. We want a party, and we don't want intruders. However, at the same time, we want to take advantage of the procedures we create to organize the party and replicate them with frogs in another party; it would be a party of frogs. We want to reuse the procedures for either dogs or frogs. However, in future, we will probably want to use them with other animals, such as parrots, lions, tigers, and horses.

In the previous chapter, we learned to work with protocols. We can declare a protocol to specify the requirements for an animal and then take advantage of Swift features to write generic code that works with any class that implements the protocol. Parametric polymorphism allows us to write generic and reusable code that can work with values without depending on the type while keeping...

Declaring a protocol to be used as a constraint


We will create an AnimalProtocol protocol to specify the requirements that a type must meet in order to be considered an animal. Then, we will create an Animal base class that conforms to this protocol, and then, we will specialize this class in three subclasses: Dog, Frog, and Lion. Then, we will create a Party class that will be able to work with instances of any class that conforms to the AnimalProtocol protocol through generics. We will work with a party of dogs, one of frogs, and another of lions.

Then, we will create a DeeJayProtocol protocol and generate a HorseDeeJay class that conforms to this new protocol. We will create a subclass of the Party class named PartyWithDeeJay that will use generics to work with instances of any type that conforms to the AnimalProtoocol protocol and instances of any type that conforms to the DeeJaypProtocol interface. We will work with a party of dogs with a DJ.

Tip

In this case, we will use the Protocol...

Declaring a class that conforms to multiple protocols


Now, we will declare a class named Animal that conforms to both the previously defined AnimalProtocol protocol and the Equatable protocol. The latter is a fundamental type in Swift. In order to conform to the Equatable protocol, we must implement the == operator function for the Animal class to determine the equality of the instances after we declare the class. This way, we will be able to determine the equality of instances of classes that implement the AnimalProtocol protocol. We can read the class declaration as "the Animal class implements both the AnimalProtocol and Equatable protocols." Take a look at the following code:

public class Animal: AnimalProtocol, Equatable {
    public let name: String
    
    public var danceCharacters: String {
        get {
            return String()
        }
    }

    public var spelledSound1: String {
        get {
            return String()
        }
    }

    public var spelledSound2: String...

Declaring subclasses that inherit the conformance to protocols


We have an Animal class that conforms to both, the AnimalProtocol and Equatable protocols. Now, we will create a subclass of Animal, a Dog class that overrides the string computed properties defined in the Animal class to provide the appropriate values for a dog:

public class Dog: Animal {
    public override var spelledSound1: String {
        get {
            return "Woof"
        }
    }
    
    public override var spelledSound2: String {
        get {
            return "Wooooof"
        }
    }
    
    public override var spelledSound3: String {
        get {
            return "Grr"
        }
    }
    
    public override var danceCharacters: String {
        get {
            return "/-\\ \\-\\ /-/"
        }
    }
}

With just a few additional lines of code, we will create another subclass of Animal, which is a Frog class that also overrides the string's read-only properties defined in the Animal class to provide the...

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:

public enum PartyError: ErrorType {
    case InsufficientMembersToRemoveLeader
    case InsufficientMembersToVoteLeader
}

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 (<), a T that identifies the generic type parameter, a colon (:), and a protocol name that the T generic type parameter must conform to, which is the AnimalProtocol protocol. Then, the where keyword, followed by T (which identified the type) and a colon (:) that indicates that the T generic type parameter has to be a type that also conforms to another protocol—that is, the Equatable protocol. Finally, the greater than sign (>) ends the type constraints declaration that is included...

Using a generic class for multiple types


We can create instances of the Party<T> class by replacing the T generic type parameter with any type name that conforms to the constraints specified in the declaration of the Party<T> class. So far, we have three concrete classes that implement both the AnimalProtocol and Equatable protocols: Dog, Frog, and Lion. Thus, we can use Dog to create an instance of Party<Dog>—that is, a Party instance of Dog objects.

The following code shows the lines that create four instances of the Dog class: jake, duke, lady, and dakota. Then, the code creates a Party<Dog> instance named dogsParty and passes jake as the leader argument to the initializer. This way, we will create a party of dogs, and Jake is the party leader:

var jake = Dog(name: "Jake")
var duke = Dog(name: "Duke")
var lady = Dog(name: "Lady")
var dakota = Dog(name: "Dakota")
var dogsParty = Party<Dog>(leader: jake)

The dogsParty instance will only accept a Dog instance for...

Combining initializer requirements in protocols with generic types


We included an initializer requirement when we declared the AnimalProtocol protocol, so we know the necessary arguments to create an instance of any class that conforms to this protocol. We will add a new method that creates an instance of the generic type T and adds it to the party members in Party<T> class.

The following lines show the code for the new createAndAddMember method that receives a name String argument and returns an instance of the generic type T. We add the method to the body after the Party<T: AnimalProtocol where T: Equatable> { public class declaration:

public func createAndAddMember(name: String) -> T {
    let newMember = T(name: name)
    addMember(newMember)
    
    return newMember
}

The method uses the generic type T and passes the name argument to create a new instance called newMember. Then, the code calls the addMember method with newMember as an argument and finally returns the recently...

Declaring associated types in protocols


Now, we want to declare a PartyProtocol protocol and make the generic Party<T> class conform to this new protocol. The main challenge is to specify the type for both the method arguments and returned values. In the generic class, we will use the generic type parameter, but protocols don't allow us to use them.

Associated types allow us to solve the problem. We can declare one or more associated types as part of the protocol definition. In this case, we just need one associated type to provide us with a placeholder name—also known as alias—to a type that we will use as part of the protocol and that will be specified during the protocol implementation—that is, when we declare a class that conforms to the protocol. It is just necessary to use the typealias keyword followed by the desired name for the associated type, and then, we can use the name in our requirements' declarations.

The following lines show the declaration of the PartyProtocol protocol...

Creating shortcuts with subscripts


We want to create a shortcut to access the members of the party. Subscripts are very useful to generate shortcuts to access members of any array, collection, list, or sequence. Subscripts can define getter and/or setter methods that receive the argument specified in the subscript declaration. In this case, we will add a read-only subscript to allow us to retrieve a member of the party through its index value. Thus, the subscript will only define a getter method.

We will use UInt as the type for the index argument because we don't want negative integer values, and the getter for the subscript will return an optional type. In case the index value received is an invalid value, the getter will return None.

First, we will add the following line to the PartyProtocol protocol body:

   subscript(index: UInt) -> MemberType? { get }

We included the subscript keyword followed by the argument name and its required type—which is the returned type, MemberType?—and the...

Declaring a class that works with two constrained generic types


Now, it is time to code another protocol that will be used as a constraint later when we define another class that takes advantage of generics with two constrained generic types. The following lines show the code for the DeeJayProtocol protocol. The public modifier followed by the protocol keyword and the protocol name, DeeJayProtocol, composes the protocol declaration, as follows:

public protocol DeeJayProtocol {
    var name: String { get }
    
    init(name: String)
    
    func playMusicToDance()
    func playMusicToSing()
}

The protocol declares a name String read-only stored property and two method requirements: playMusicToDance and playMusicToSing. As you learned in the previous chapter, the protocol includes only the method declaration because the classes that conform to the DeejayProtocol protocol will be responsible for providing the implementation of the name stored property and the other two methods.

In addition...

Using a generic class with two generic type parameters


We can create instances of the PartyWithDeeJay<T, K> class by replacing both the T and K generic type parameters with any type names that conform to the constraints specified in the declaration of the PartyWithDeeJay<T, K> class. We have three concrete classes that implement both the AnimalProtocol and Equatable protocols: Dog, Frog, and Lion. We have one class that conforms to the DeeJayProtocol protocol: HorseDeeJay. Thus, we can use Dog and HorseDeeJay to create an instance of PartyWithDeeJay<Dog, HorseDeeJay>.

The following lines create a HorseDeeJay instance named silver. Then, the code creates a PartyWithDeeJay<Dog, HorseDeeJay> instance named silverParty and passes jake and silver as arguments. This way, we can create a party of dogs with a horse DJ, where Jake is the party leader, and Silver is the DJ:

var silver = HorseDeeJay(name: "Silver")
var silverParty = PartyWithDeeJay<Dog, HorseDeeJay>(leader...

Inheriting and adding associated types in protocols


Now, we want to declare a PartyWithDeeJayProtocol protocol and make the generic PartyWithDeeJay<T, K> class conform to this new protocol. We will make this protocol inherit from the previously created PartyProtocol that defined a MemberType associated type. Thus, the PartyWithDeeJayProtocol protocol will inherit this associated type. We have to specify another associated type that will be specified during the protocol implementation—that is, when we declare the class that conforms to the new protocol.

The following lines show the declaration of the PartyWithDeeJayProtocol protocol that inherits from the PartyProtocol protocol. We must declare the protocol before the public class PartyWithDeeJay<T: AnimalProtocol, K: DeeJayProtocol where T: Equatable>: Party<T> line that starts the declaration of the Party<T, K> class that we want to edit to make it conform to this new protocol:

public protocol PartyWithDeeJayProtocol...

Generalizing existing classes with generics


In Chapter 3, Encapsulation of Data with Properties, we created a class to represent a mutable 3D vector named MutableVector3D and a class to represent an immutable version of a 3D vector named ImmutableVector3D.

Both versions were capable of working with 3D vectors with Float values for x, y, and z. We now realize that we also have to work with 3D vectors with Double values for x, y, and z in both classes. We definitely don't want to create two new classes, such as MutableDoubleVector3D and ImmutableDoubleVector3D. We can take advantage of generics to create two classes capable of working with elements of any floating point type supported in Swift—that is, either Float, Float80, or Double.

We want to create the following two classes:

  • MutableVector3D<T>

  • ImmutableVector3D<T>

It seems to be a pretty simple task. We just have to replace Float with the generic type parameter, T, and change the class declaration to include the generic type...

Extending base types to conform to custom protocols


Now, we want to be able to use any of the integer types as types in our MutableVector3D<T> and ImmutableVector3D<T> classes. We just need to extend the desired types to make them conform to the previously created NumericForVector protocol.

We want to make the two classes capable of working with elements of any integer type supported in Swift—that is, any of the following types:

  • Int

  • Int16

  • Int32

  • Int64

  • Int8

  • UInt

  • UInt16

  • UInt32

  • UInt64

  • UInt8

The following lines extend the previously enumerated types to conform to the NumericForVector protocol:

// Signed integers
extension Int: NumericForVector { }
extension UInt: NumericForVector { }
extension Int16: NumericForVector { }
extension Int32: NumericForVector { }
extension Int64: NumericForVector { }
extension Int8: NumericForVector { }

// Unsigned integers
extension UInt16: NumericForVector { }
extension UInt32: NumericForVector { }
extension UInt64: NumericForVector { }
extension...

Test your knowledge


  1. When we declare protocols, the Self keyword signifies:

    1. The type that implements the protocol.

    2. The instance of a class that conforms to the protocol.

    3. The instance of a struct that conforms to the protocol.

  2. Generics allow us to declare a class that:

    1. Can use a generic type only as the type for stored and type properties.

    2. Can use a genric type only as an argument for its initializers.

    3. Can work with many generic types.

  3. The public class ImmutableVector3D<T: FloatingPointType> line means:

    1. The generic type constraint specifies that T must conform to the ImmutableVector3D protocol or belong to the ImmutableVector3D class hierarchy.

    2. The generic type constraint specifies that T must conform to the FloatingPointType protocol or belong to the FloatingPointType class hierarchy.

    3. The class is a subclass of FloatingPointType.

  4. The public class Party<T: AnimalProtocol where T: Equatable> line means:

    1. The generic type constraint specifies that T must conform to both the AnimalProtocol and...

Exercises


Add the following operators to work with both MutableVector3D<T> and ImmutableVector3D<T>:

  • ==: This determines whether all the elements that compose a 3D vector (x, y, and z) are equal.

  • +: This sums each element that composes a 3D vector and saves the result in each element or in the new returned instance according to the class version (mutable or immutable). The new x must have the result of the left-hand side x + right-hand side x, the new y must be that of left-hand side y + right-hand side y, and the new z must be that of left-hand side z + right-hand side z.

In Chapter 4, Inheritance, Abstraction, and Specialization we created an Animal class and then defined specific operator functions to allow us to use operators with instances of this class. Redefine this class to conform to both the Comparable and Equatable protocols.

The following lines show the source code for the Equatable protocol:

public protocol Equatable {
    @warn_unused_result
    public func ==(lhs...

Summary


In this chapter, you learned how to maximize code reuse by writing code capable of working with objects of different types—that is, instances of classes that conform to specific protocols or whose class hierarchy includes specific superclasses. We worked with protocols and generics. We created classes capable of working with one or two constrained generic types.

We combined inheritance, protocols, and extensions to maximize the reusability of code. We could make classes work with many different types.

Now that we have learned about parametric polymorphism and generics, we are ready to combine object-oriented programming and functional programming, which is the topic of the next chapter.

lock icon
The rest of the chapter is locked
You have been reading a chapter from
Object???Oriented Programming with Swift 2
Published in: Jan 2016Publisher: ISBN-13: 9781785885693
Register for a free Packt account to unlock a world of extra content!
A free Packt account unlocks extra newsletters, articles, discounted offers, and much more. Start advancing your knowledge today.
undefined
Unlock this book and the full library FREE for 7 days
Get unlimited access to 7000+ expert-authored eBooks and videos courses covering every tech area you can think of
Renews at €14.99/month. Cancel anytime

Author (1)

author image
Gaston C. Hillar

Gaston C. Hillar is Italian and has been working with computers since he was 8 years old. Gaston has a Bachelor's degree in computer science (graduated with honors) and an MBA. Currently, Gaston is an independent IT consultant and a freelance author who is always looking for new adventures anywhere in the world. He was a senior contributing editor at Dr. Dobb's, and has written more than a hundred articles on software development topics. He has received the prestigious Intel Black Belt Software Developer award eight times. He has written many articles about Java for Oracle Java Magazine. Gaston was also a former Microsoft MVP in technical computing. He lives with his wife, Vanesa, and his two sons, Kevin and Brandon.
Read more about Gaston C. Hillar