Having had to implement convertible measurements and units for a project a
couple of years ago, I thought I would take a look at the upcoming Measurements
and Units implementations as found in the Github archive.
Since we are continually being told that protocol-oriented code and the
preferred use of structs were good things, I was surprised at the class
inheritance I found.
As a long-time proponent of OO design (some 29 years) I know that classes are
not "evil" but was interested to see if Swift 2.2 could manage to implement
Measurements and Units without falling into the inheritance trap where
unnecessary implementation has to be included throughout a class hierarchy.
I replaced the name Dimension with ConvertibleUnit as I felt a Dimension was
not truly a Unit; in fact, a Dimension could be better described in terms of
composition rather than inheritance i.e. a Dimension "has a" Unit rather than a
Dimension "is a" Unit.
Using a protocol for ConvertibleUnit also removes the requirement for methods
in a base class that were either empty or that carried out a conversion at a
coefficient of 1, which is essentially no conversion at all.
Therefore, if I may indulge, here is my attempt at a more protocol-orientd
version. The code is not complete with regards to bridging but, looking at the
bridging code in Github, that should not be a problem.
public protocol UnitConverter
{
func baseUnitValue(fromValue value: Double) -> Double
func value(fromBaseUnitValue baseUnitValue: Double) -> Double
}
// example converters
public struct UnitConverterLinear : UnitConverter
{
private let coefficient: Double
private let constant: Double
public init(coefficient: Double, constant: Double)
{
self.coefficient = coefficient
self.constant = constant
}
public init(coefficient: Double)
{
self.init(coefficient: coefficient, constant: 0)
}
public func baseUnitValue(fromValue value: Double) -> Double
{
return value * coefficient + constant
}
public func value(fromBaseUnitValue baseUnitValue: Double) -> Double
{
return (baseUnitValue - constant) / coefficient
}
}
public struct UnitConverterReciprocal : UnitConverter
{
private let reciprocal: Double
public init(reciprocal: Double)
{
self.reciprocal = reciprocal
}
public func baseUnitValue(fromValue value: Double) -> Double
{
return reciprocal / value
}
public func value(fromBaseUnitValue baseUnitValue: Double) -> Double
{
return baseUnitValue * reciprocal
}
}
// base protocols
public protocol Unit
{
var symbol: String { get }
}
public protocol ConvertibleUnit : Unit
{
var converter: UnitConverter { get }
static var baseUnit : Self { get }
}
// generic Measurement
public struct Measurement<UnitType : ConvertibleUnit>
{
var value: Double
let unit: UnitType
public init(value: Double, unit: UnitType)
{
self.value = value
self.unit = unit
}
public func canBeConverted<TargetUnit : ConvertibleUnit>(to unit: TargetUnit)
-> Bool
{
return unit is UnitType
}
public func converting<TargetUnit : ConvertibleUnit>(to unit: TargetUnit) ->
Measurement<TargetUnit>
{
if !canBeConverted(to: unit)
{
fatalError("Unit type not compatible")
}
let baseUnitValue = self.unit.converter.baseUnitValue(fromValue: value)
let convertedValue = unit.converter.value(fromBaseUnitValue: baseUnitValue)
return Measurement<TargetUnit>(value: convertedValue, unit: unit)
}
}
// example implementing ConvertibleUnit
public struct LengthUnit : ConvertibleUnit
{
public let symbol: String
public let converter: UnitConverter
private struct Symbol
{
static let kilometers = "km"
static let meters = "m"
// ...
}
private struct Coefficient
{
static let kilometers = 1000.0
static let meters = 1.0
// ...
}
public static var kilometers: LengthUnit
{
return LengthUnit(symbol: Symbol.kilometers, converter:
UnitConverterLinear(coefficient: Coefficient.kilometers))
}
public static var meters: LengthUnit
{
return LengthUnit(symbol: Symbol.meters, converter:
UnitConverterLinear(coefficient: Coefficient.meters))
}
// ...
public static var baseUnit : LengthUnit
{
return LengthUnit.meters
}
}
--
Joanna Carter
Carter Consulting
_______________________________________________
swift-corelibs-dev mailing list
[email protected]
https://lists.swift.org/mailman/listinfo/swift-corelibs-dev