0

I've been researching the concept of encapsulation and found some decent threads about the topic like this one and this one. But I haven't been able to find an answer to a particular question I have. I'll use an example in Swift.

Say you have an object that is of type RoadTrip:

class RoadTrip() {
    private var duration: Double
    private var totalMiles: Double
    private var gallonsOfFuel: Double

    var averageMilesPerGallon: Double
}

Now let's say the app is going to calculate the averageMilesPerGallon which is the only public property:

func calculateAverageMilePerGallon() -> Double {
    let mpg = totalMiles / gallonsOfFuel

    return mpg
}

Should the calculation of average miles per gallon be a private method of the RoadTrip object that executes and updates its averageMilesPerGallon or would it be acceptable to have the calculation performed by another method in a separate utility class that then updates the averageMilesPerGallon property of the RoadTrip object using a mutator method that will set the value?

EDIT: Here's my single class that contains my app's basic calculations. I approached it this way based on what I learned in the Stanford course on iTunes, but I'm beginning to think in my case I should move much of this to my LiftEvent class:

infix operator ^^ { }
func ^^ (radix: Double, power: Double) -> Double {
    return Double(pow(Double(radix), Double(power)))
}

class CalculatorBrain: NSObject {
    var weightLifted: Double?
    var repetitions: Double?
    var oneRepMax: Double?
    let dataManager = LiftEventDataManager()

    func calculateOneRepMax(weightLifted: Double, repetitions: Int ) -> Double {
        let preferredFormulaID = UserDefaultsManager.sharedInstance.preferredFormula!
        let formulas = dataManager.fetchSelectableFormulas()
        let formulaName = formulas[preferredFormulaID].formulaName

        switch formulaName {
        case "Epley":
            oneRepMax = weightLifted * (1 + Double(repetitions)/30.0)
            return oneRepMax!
        case "Baechle":
            oneRepMax = weightLifted * (36/(37 - Double(repetitions)))
            return oneRepMax!
        case "Brzychi":
            oneRepMax = weightLifted * ( 1 + ( 0.033 * Double(repetitions)))
            return oneRepMax!
        case "Lander":
            oneRepMax = 100 * weightLifted / (101.3 - (2.67123 * Double(repetitions)))
            return oneRepMax!
        case "Lombardi":
            oneRepMax = weightLifted * (Double(repetitions) ^^ 0.10)
            return oneRepMax!
        case "Mayhew":
            oneRepMax = 100 * weightLifted / (52.2 + (41.9 * (2.71828 ^^ (-0.055 * Double(repetitions)))))
            return oneRepMax!
        case "O'Conner":
            oneRepMax = weightLifted * (1 + 0.025 * Double(repetitions))
            return oneRepMax!
        default:
            return 0.0
        }
    }


    private func calculatePercentOfWeight(maxWeight: Double, percent: Double) -> Double {
        return maxWeight * percent
    }

    func calculateWeightPercentages(maxWeight: String) -> [Int: Double] {
        let weightPercentages = [1.0, 0.95, 0.90, 0.85, 0.80, 0.75, 0.70, 0.65, 0.60, 0.55, 0.50, 0.45, 0.40, 0.35, 0.30, 0.25]

        var percentages = [Int: Double]()
        for percent in weightPercentages {
            let integerPercent = Int(percent * 100)
            percentages[integerPercent] = calculatePercentOfWeight(Double(maxWeight)!, percent: percent)
        }
        return percentages
    }

    func convertBetweenUnits(fromUnit: Int, toUnit: Int, value: Double) -> Double {
        let units = dataManager.fetchUnits()
        let from = units[fromUnit].conversionRatio as Double
        let to = units[toUnit].conversionRatio as Double
        let result = Double(value * to / from)
        return result
    }
}
Community
  • 1
  • 1
Jim
  • 1,260
  • 15
  • 37
  • 1
    Does it sound like a practical solution to have arbitrary utility classes to contain such basic functionality – Alexander Jan 28 '17 at 17:36
  • You know your class doesn't compile, right? – Alexander Jan 28 '17 at 17:37
  • Nothing to do with "encapsulation"? Nothing to do with "mutators" either? – matt Jan 28 '17 at 17:38
  • Yes, I do. It's an example to help explain the basic properties. Since my question isn't about how to write the exact code, it's about a principle, I thought that was good enough. – Jim Jan 28 '17 at 17:39
  • @Jim Well I mean, you kind of answer part of your own question yourself. You're concerned about encapsulation, but then propose opening up a class so that it can be mutated externally by an arbitrary utility class. That's a pretty obvious violation of encapsulation. – Alexander Jan 28 '17 at 17:42
  • Thanks, Alexander. I'm learning so what's obvious to others isn't always obvious to beginners. You make a good point. – Jim Jan 28 '17 at 17:44

1 Answers1

1

I think this is the ideal use-case for a computed property:

class RoadTrip {
    private let duration: Double
    private let totalMiles: Double
    private let gallonsOfFuel: Double

    private var averageMilesPerGallon: Double {
        return totalMiles / gallonsOfFuel
    }
}
Alexander
  • 59,041
  • 12
  • 98
  • 151
  • @Jim I don't know, is `averageMilesPerGallon` intended to be public? I figured it would be private because `totalMiles` and `gallonsOfFuel` were `private`. Wouldn't really make sense to have it public while the other two were `private` – Alexander Jan 28 '17 at 17:50
  • Yes, a computed property for the calculation is probably a better way to go. But you probably meant for the `private var averageMilesPerGallon` to be just `var averageMilesPerGallon`, right? You really got me thinking though about the "arbitrary utility" class comment. That's exactly what I have in my actual project - a class that handles a variety of calculations which I picked up for the Stanford course on iTunes. Looks like I've got some refactoring to do. – Jim Jan 28 '17 at 17:58
  • @Jim Where's the encapsulation in having your most basic app logic spread over a bunch of random classes? – Alexander Jan 28 '17 at 17:59
  • I actually have one class that contains a variety of calculations so, yeah, that's not encapsulating anything in a logical way. It's just a collection of calculations. I was considering moving the calculations to my objects so it would be the objects that know what they need to know about themselves. That's how I started down this path of trying to learn more about encapsulation. – Jim Jan 28 '17 at 18:04
  • @Jim Are those calculations that are applicable to many different classes? – Alexander Jan 28 '17 at 18:05
  • Not really. I posted the calculation code above so you can see it. – Jim Jan 28 '17 at 18:17
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/134267/discussion-between-alexander-and-jim). – Alexander Jan 28 '17 at 18:17