Last active
October 2, 2015 17:32
-
-
Save arlobelshee/25a17ce7676b807c1dde to your computer and use it in GitHub Desktop.
Combine 2 methods into one with a discriminator variable
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Goal: Restore Drive method to all operate at one level of abstraction. | |
// Refactorings performed: Extract Method x 3 | |
// Hand-edits made: update comment | |
// Why: A method with multiple levels of abstraction is harder to reason about. Drive | |
// was calling a method to do some of its work, then doing direct field operations later. | |
// That causes readers to distrust the one method that was called and have to read it to | |
// understand the whole. | |
// | |
// Now Drive is written entirely in terms of intent. They can choose | |
// to read it and trust all sub-methods. After they accept Drive, they can choose to read | |
// any sub-method that interests them. They don't have to keep all of Drive in their minds | |
// at one time. | |
using System.Collections.Generic; | |
using System.Linq; | |
using JetBrains.Annotations; | |
namespace Blog_gists | |
{ | |
public class Car | |
{ | |
[NotNull] private readonly List<TravelSegment> drivingHistory = new List<TravelSegment>(); | |
public Car() | |
{ | |
Odometer = 0; | |
IsAtDestination = false; | |
} | |
public void Drive(int speed, int distance, DriveStyle how) | |
{ | |
PhysicallyMoveTheCar(speed, distance); | |
if (DriveStyle.WithFuelMonitoring == how) | |
{ | |
UpdateFuelConsumed(speed, distance); | |
} | |
UpdateDistanceTravelled(distance); | |
if (DriveStyle.WithAverageSpeedCalculation == how) | |
{ | |
UpdateDrivingHistory(speed, distance); | |
} | |
} | |
private void UpdateDrivingHistory(int speed, int distance) | |
{ | |
drivingHistory.Add(new TravelSegment(speed, distance)); | |
} | |
private void UpdateDistanceTravelled(int distance) | |
{ | |
Odometer += distance; | |
} | |
private void UpdateFuelConsumed(int speed, int distance) | |
{ | |
FuelRemaining -= CalculateFuelConsumed(speed, distance); | |
} | |
private void PhysicallyMoveTheCar(int speed, int distance) | |
{ | |
for (var i = 0; i < distance; ++i) | |
{ | |
DriveOneMile(speed); | |
} | |
FinishDriving(); | |
} | |
public enum DriveStyle | |
{ | |
WithFuelMonitoring, | |
WithAverageSpeedCalculation | |
} | |
public decimal? AverageSpeed | |
{ | |
get | |
{ | |
if (drivingHistory.Count == 0) return null; | |
var totalDriveTime = drivingHistory.Select(h => h.Distance/h.Speed).Sum(); | |
return Odometer/totalDriveTime; | |
} | |
} | |
public void FillGasTank(decimal numGallons) | |
{ | |
FuelRemaining += numGallons; | |
} | |
private void FinishDriving() | |
{ | |
IsAtDestination = true; | |
} | |
public class TravelSegment | |
{ | |
public decimal Speed; | |
public decimal Distance; | |
public TravelSegment(int speed, int distance) | |
{ | |
Speed = speed; | |
Distance = distance; | |
} | |
} | |
private decimal CalculateFuelConsumed(int speed, int distance) | |
{ | |
return 3; | |
} | |
private void DriveOneMile(int speed) | |
{ | |
IsAtDestination = false; | |
} | |
public int Odometer { get; private set; } | |
public decimal FuelRemaining { get; private set; } | |
public bool IsAtDestination { get; private set; } | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using FluentAssertions; | |
using NUnit.Framework; | |
namespace Blog_gists | |
{ | |
[TestFixture] | |
public class CarsDrive | |
{ | |
private const int Speed = 50; | |
private const int Distance = 100; | |
[Test] | |
public void DrivingWhileUpdatingFuelGuageShouldMoveTheCarAndRequireGas() | |
{ | |
var testSubject = new Car(); | |
testSubject.FillGasTank(12); | |
testSubject.Drive(Speed, Distance, Car.DriveStyle.WithFuelMonitoring); | |
testSubject.IsAtDestination.Should().BeTrue(); | |
testSubject.Odometer.Should().Be(100); | |
testSubject.FuelRemaining.Should().Be(9); | |
testSubject.AverageSpeed.Should().NotHaveValue(); | |
} | |
[Test] | |
public void DrivingWhileUpdatingMilageShouldMoveTheCarAndCalculateMilage() | |
{ | |
var testSubject = new Car(); | |
testSubject.FuelRemaining.Should().Be(0); | |
testSubject.Drive(Speed, Distance, Car.DriveStyle.WithAverageSpeedCalculation); | |
testSubject.IsAtDestination.Should().BeTrue(); | |
testSubject.Odometer.Should().Be(100); | |
testSubject.FuelRemaining.Should().Be(0); | |
testSubject.AverageSpeed.Should().Be(50); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment