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 the 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 is a pretty simple task. We just have to replace Float with the generic type parameter, T, and change the class declaration to include the necessary generic type constraint. In previous Swift versions, we didn't have protocols that allowed us to easily build the generic type constraint for this case because the FloatingPointType protocol didn't declare the necessary arithmetic operations that we require in our class. However, in Swift 3, the FloatingPointType protocol has been renamed to FloatingPoint and includes the necessary arithmetic operations.

The following lines show the code for the new MutableVector3D<T> class, that is able to work with any floating point numeric type that conforms to the FloatingPoint protocol, such as Float and Double. The code file for the sample is included in the swift_3_oop_chapter_06_13 folder:

    open class MutableVector3D<T: FloatingPoint> { 
      open var x: T 
      open var y: T 
      open var z: T 
     
      init(x: T, y: T, z: T) { 
        self.x = x 
        self.y = y 
        self.z = z 
      } 
     
      open func sum(deltaX: T, deltaY: T, deltaZ: T) { 
        x += deltaX 
        y += deltaY 
        z += deltaZ 
      } 
     
      open func printValues() { 
        print("X: (self.x), Y: (self.y), Z: (self.z)") 
      } 
    } 

Now, we will follow the same approach to generate an ImmutableVector3D<T> class. The following lines show the code for the new ImmutableVector3D<T> class:

    open class ImmutableVector3D<T: FloatingPoint> { 
      open let x: T 
      open let y: T 
      open let z: T 
     
      init(x: T, y: T, z: T) { 
        self.x = x 
        self.y = y 
        self.z = z 
      } 
     
      open func summed(deltaX: T, deltaY: T, deltaZ: T) -> 
      ImmutableVector3D<T> { 
        return ImmutableVector3D<T>(x: x + deltaX, y: y + deltaY, z: 
        z + deltaZ) 
      } 
     
      open func printValues() { 
        print("X: (self.x), Y: (self.y), Z: (self.z)") 
      } 

      open class func makeEqualElements(initialValue: T) -> 
      ImmutableVector3D<T> { 
        return ImmutableVector3D<T>(x: initialValue, y: initialValue, 
        z: initialValue) 
      } 
     
      open class func makeOrigin() -> ImmutableVector3D<T> { 
        return makeEqualElements(initialValue: 0) 
      } 
    } 

Double, Float and Float80 conform to the FloatingPoint protocol. Thus, we can create instances of any of the following:

  • MutableVector3D<Double>
  • MutableVector3D<Float>
  • MutableVector3D<Float80>
  • ImmutableVector3D<Double>
  • ImmutableVector3D<Float>
  • ImmutableVector3D<Float80>

The following lines create instances of the previously enumerated classes---that is, both MutableVector3D and ImmutableVector3D---with the generic type parameter set to Double, Float, and Float80. The code also calls the mutating sum or the nonmutating summed method for each instance. Then, the code calls the printValues method. The code file for the sample is included in the swift_3_oop_chapter_06_13 folder:

    let mutableVector0 = MutableVector3D<Double>(x: 10.1, y: 10.2, 
    z: 10.3) 
    mutableVector0.sum(deltaX: 3.4, deltaY: 4.52, deltaZ: 3.32) 
    mutableVector0.printValues() 
 
    let mutableVector1 = MutableVector3D<Float>(x: 3.456, y: 9.231, 
    z: 3.324) 
    mutableVector1.sum(deltaX: 3.411, deltaY: 4.232, deltaZ: 3.465) 
    mutableVector1.printValues() 
 
    let mutableVector2 = MutableVector3D<Float80>(x: 7.2345, y: 2.3489, 
    z: 1.3485) 
    mutableVector2.sum(deltaX: 3.4113, deltaY: 1.2332, deltaZ: 1.3482) 
    mutableVector2.printValues() 
 
    let immutableVector0 = ImmutableVector3D<Double>(x: 10.1, y: 10.2, 
    z: 10.3) 
    let immutableVector1 = immutableVector0.summed(deltaX: 3.4, 
    deltaY: 4.52, deltaZ: 3.32) 
    immutableVector1.printValues() 
     
    let immutableVector2 = ImmutableVector3D<Float>(x: 3.456, y: 9.231, 
    z: 3.324) 
    let immutableVector3 = immutableVector2.summed(deltaX: 3.411, 
    deltaY: 4.232, deltaZ: 3.465) 
    immutableVector3.printValues() 
 
    let immutableVector4 = ImmutableVector3D<Float80>(x: 7.2345, 
    y: 2.3489, z: 1.3485) 
    let immutableVector5 = immutableVector4.summed(deltaX: 3.4113, 
    deltaY: 1.2332, deltaZ: 1.3482) 
    immutableVector5.printValues() 

The following lines show the output generated by the preceding code:

X: 13.5, Y: 14.72, Z: 13.62
X: 6.867, Y: 13.463, Z: 6.789
X: 10.6458, Y: 3.5821, Z: 2.6967
X: 13.5, Y: 14.72, Z: 13.62
X: 6.867, Y: 13.463, Z: 6.789
X: 10.6458, Y: 3.5821, Z: 2.6967

The following screenshot shows the Playground with the types generated in each line specified on the right-hand side:

Generalizing existing classes with generics

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

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