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:
3.145.97.109