extreme coding take control of your code n.
Download
Skip this Video
Loading SlideShow in 5 Seconds..
Extreme Coding : Take control of your code PowerPoint Presentation
Download Presentation
Extreme Coding : Take control of your code

Loading in 2 Seconds...

play fullscreen
1 / 93

Extreme Coding : Take control of your code - PowerPoint PPT Presentation


  • 136 Views
  • Uploaded on

Extreme Coding : Take control of your code. Exilesoft Johannes Brodwall Exilesoft Chief scientist @ jhannes. TODO: Preparator refactoring of MyTime. Are you in control of the code ?. Or is the code in control of you ?. Kata New code Old code. Part I.

loader
I am the owner, or an agent authorized to act on behalf of the owner, of the copyrighted work described.
capcha
Download Presentation

PowerPoint Slideshow about 'Extreme Coding : Take control of your code' - tamyra


An Image/Link below is provided (as is) to download presentation

Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author.While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server.


- - - - - - - - - - - - - - - - - - - - - - - - - - E N D - - - - - - - - - - - - - - - - - - - - - - - - - -
Presentation Transcript
extreme coding take control of your code

Extreme Coding:Takecontrolofyourcode

Exilesoft

Johannes Brodwall

Exilesoft Chief scientist

@jhannes

TODO:

Preparator refactoring of MyTime

slide4

Kata

New code

Old code

slide7

publicclassPrimeFactorsTest {

@Test

publicvoidoneHasNoFactors() {

assertTrue(getPrimeFactors(1).isEmpty());

}

}

Think of the simplest test case

slide8

publicclassPrimeFactorsTest {

@Test

publicvoidoneHasNoFactors() {

assertTrue(getPrimeFactors(1).isEmpty());

}

private List<Integer> getPrimeFactors(inti) {

// TODO Auto-generated method stub

returnnull;

}

}

Make the code compile

=> Test runs red

slide9

publicclassPrimeFactorsTest {

@Test

publicvoidoneHasNoFactors() {

assertTrue(getPrimeFactors(1).isEmpty());

}

private List<Integer> getPrimeFactors(inti) {

returnnewArrayList<>();

}

}

The simplest thing to green

slide10

publicclassPrimeFactorsTest {

@Test

publicvoidoneHasNoFactors() {

assertTrue(getPrimeFactors(1).isEmpty());

}

private List<Integer> getPrimeFactors(inti) {

List<Integer> factors = newArrayList<>();

return factors;

}

}

Refactor

slide11

publicclassPrimeFactorsTest {

@Test

publicvoidoneHasNoFactors() {

assertTrue(getPrimeFactors(1).isEmpty());

}

@Test

publicvoidfactorsOfTwo() {

assertEquals(Arrays.asList(2), getPrimeFactors(2));

}

private List<Integer> getPrimeFactors(inti) {

List<Integer> factors = newArrayList<>();

return factors;

}

}

The next simplest test

=> Tests fail

slide12

publicclassPrimeFactorsTest {

@Test

publicvoidoneHasNoFactors() {

assertTrue(getPrimeFactors(1).isEmpty());

}

@Test

publicvoidfactorsOfTwo() {

assertEquals(Arrays.asList(2), getPrimeFactors(2));

}

private List<Integer> getPrimeFactors(inti) {

List<Integer> factors = newArrayList<>();

if (i == 2) factors.add(2);

return factors;

}

}

Simplest possible thing: Special case it

slide13

publicclassPrimeFactorsTest {

@Test

publicvoidoneHasNoFactors() {

assertTrue(getPrimeFactors(1).isEmpty());

}

@Test

publicvoidfactorsOfTwo() {

assertEquals(Arrays.asList(2), getPrimeFactors(2));

}

private List<Integer> getPrimeFactors(int number) {

List<Integer> factors = newArrayList<>();

if (number == 2) factors.add(2);

return factors;

}

}

Refactor – improve naming

slide14

publicclassPrimeFactorsTest {

// ...

@Test

publicvoidfactorsOfTwo() {

assertEquals(Arrays.asList(2), getPrimeFactors(2));

}

@Test

publicvoidfactorsOfThree() {

assertEquals(Arrays.asList(3), getPrimeFactors(3));

}

private List<Integer> getPrimeFactors(int number) {

List<Integer> factors = newArrayList<>();

if (number == 2) factors.add(2);

if (number == 3) factors.add(3);

return factors;

}

}

Simplest next case + code

slide15

publicclassPrimeFactorsTest {

// ...

@Test

publicvoidfactorsOfTwo() {

assertEquals(Arrays.asList(2), getPrimeFactors(2));

}

@Test

publicvoidfactorsOfThree() {

assertEquals(Arrays.asList(3), getPrimeFactors(3));

}

private List<Integer> getPrimeFactors(int number) {

List<Integer> factors = newArrayList<>();

if (number > 1) factors.add(number);

return factors;

}

}

Refactor away duplication

slide16

publicclassPrimeFactorsTest {

// ...

@Test

publicvoidfactorsOfFour() {

assertEquals(Arrays.asList(2,2), getPrimeFactors(4));

}

private List<Integer> getPrimeFactors(int number) {

List<Integer> factors = newArrayList<>();

if (number == 4) {

factors.add(2);

number /= 2;

}

if (number > 1) factors.add(number);

return factors;

}

}

Next case

Special case result

slide17

publicclassPrimeFactorsTest {

// ...

@Test

publicvoidfactorsOfSix() {

assertEquals(Arrays.asList(2,3), getPrimeFactors(6));

}

private List<Integer> getPrimeFactors(int number) {

List<Integer> factors = newArrayList<>();

if (number == 6) {

factors.add(2);

number /= 2;

}

if (number == 4) {

factors.add(2);

number /= 2;

}

if (number > 1) factors.add(number);

return factors;

}

}

Next case – duplicate special case

slide18

publicclassPrimeFactorsTest {

// ...

@Test

publicvoidfactorsOfSix() {

assertEquals(Arrays.asList(2,3), getPrimeFactors(6));

}

private List<Integer> getPrimeFactors(int number) {

List<Integer> factors = newArrayList<>();

int factor = 2;

if (number % factor == 0) {

factors.add(factor);

number /= factor;

}

if (number > 1) factors.add(number);

return factors;

}

}

Refactor away duplication

Important design step!

slide19

publicclassPrimeFactorsTest {

// ...

@Test

publicvoidfactorsOfEight() {

assertEquals(Arrays.asList(2,2,2), getPrimeFactors(8));

}

private List<Integer> getPrimeFactors(int number) {

List<Integer> factors = newArrayList<>();

int factor = 2;

if (number % factor == 0) {

factors.add(factor);

number /= factor;

}

if (number > 1) factors.add(number);

return factors;

}

}

Next test – fails

slide20

publicclassPrimeFactorsTest {

// ...

@Test

publicvoidfactorsOfEight() {

assertEquals(Arrays.asList(2,2,2), getPrimeFactors(8));

}

private List<Integer> getPrimeFactors(int number) {

List<Integer> factors = newArrayList<>();

int factor = 2;

while (number % factor == 0) {

factors.add(factor);

number /= factor;

}

if (number > 1) factors.add(number);

return factors;

}

}

Simplest thing to make it work!

slide21

publicclassPrimeFactorsTest {

// ...

@Test

publicvoidfactorsOfNine() {

assertEquals(Arrays.asList(3,3), getPrimeFactors(9));

}

private List<Integer> getPrimeFactors(int number) {

List<Integer> factors = newArrayList<>();

for (int factor = 2; factor<number; factor++) {

while (number % factor == 0) {

factors.add(factor);

number /= factor;

}

}

if (number > 1) factors.add(number);

return factors;

}

}

Next test and code

slide23

Think of next tests

Write a test

Make it pass as simply as possible

Refactor: Add names, remove duplication

slide24

Think of next tests

Write a test

Make it pass as simply as possible

Refactor: Add names, remove duplication

slide25

Think of next tests

Write a test

Make it pass as simply as possible

Refactor: Add names, remove duplication

slide26

Think of next tests

Write a test

Make it pass as simply as possible

Refactor: Add names, remove duplication

slide27

This is where the design comes:

Refactor: Add names, remove duplication

slide29

You think too much!

(about the wrong things)

slide38

describe("Modbus data server", function() {

it("should read saved data from correct register", function(done) {

var measurements = [

sampleMeasurement({ cassette: 1, transponder: 1, thickness: 50.1 }),

sampleMeasurement({ cassette: 1, transponder: 2, thickness: 50.2 }),

];

updateData(measurements, function() {

readRegisters(cassette_start_pos(1), 4, function(err, data) {

data.readFloatLE(0).should.be.approximately(50.1, 0.0001);

data.readFloatLE(4).should.be.approximately(50.2, 0.0001);

done();

});

});

});

});

slide39

describe("Modbus data server", function() {

it("should read saved data from correct register", function(done) {

var measurements = [

sampleMeasurement({ cassette: 1, transponder: 1, thickness: 50.1 }),

sampleMeasurement({ cassette: 1, transponder: 2, thickness: 50.2 }),

];

updateData(measurements, function() {

readRegisters(cassette_start_pos(1), 4, function(err, data) {

data.readFloatLE(0).should.be.approximately(50.1, 0.0001);

data.readFloatLE(4).should.be.approximately(50.2, 0.0001);

done();

});

});

});

});

Given

slide40

describe("Modbus data server", function() {

it("should read saved data from correct register", function(done) {

var measurements = [

sampleMeasurement({ cassette: 1, transponder: 1, thickness: 50.1 }),

sampleMeasurement({ cassette: 1, transponder: 2, thickness: 50.2 }),

];

updateData(measurements, function() {

readRegisters(cassette_start_pos(1), 4, function(err, data) {

data.readFloatLE(0).should.be.approximately(50.1, 0.0001);

data.readFloatLE(4).should.be.approximately(50.2, 0.0001);

done();

});

});

});

});

When

slide41

describe("Modbus data server", function() {

it("should read saved data from correct register", function(done) {

var measurements = [

sampleMeasurement({ cassette: 1, transponder: 1, thickness: 50.1 }),

sampleMeasurement({ cassette: 1, transponder: 2, thickness: 50.2 }),

];

updateData(measurements, function() {

readRegisters(cassette_start_pos(1), 4, function(err, data) {

data.readFloatLE(0).should.be.approximately(50.1, 0.0001);

data.readFloatLE(4).should.be.approximately(50.2, 0.0001);

done();

});

});

});

});

Then

slide43

var registers = newbuffer.Buffer(10000);

varcassette_start_register = function(cassette_id) {

return0;

};

varreadRegisters = function(start_register, register_count, callback) {

var message = registers.slice(start_register*2, start_register*2 + register_count*2);

callback(null, message);

};

varupdateData = function(objects, callback) {

for (vari=0; i<objects.length; i++) {

var measurement = objects[i];

vartransducer_pos =

2*cassette_start_register(measurement.cassette_id)

+ 4*(measurement.transponder_id - 1);

registers.writeFloatLE(measurement.thickness, transducer_pos);

}

callback();

};

slide45

varSequelize = require("sequelize");

varsequelize = newSequelize('modbusfun', 'modbus', 'secret',

{ dialect: 'mysql', port: 3306, logging: false });

var Measurement = require('../lib/modbus-fun/measurement') .init(sequelize);

varupdateRegisters = function(callback) {

Measurement.findMeasurements(function(err, objects) {

for (vari=0; i<objects.length; i++) {

var measurement = objects[i];

vartransducer_pos =

2*cassette_start_register(measurement.cassette_id)

+ 4*(measurement.transponder_id - 1);

registers.writeFloatLE(measurement.thickness, transducer_pos);

}

callback();

});

};

slide47

varmyRegisters = Registers.createRegisters(Measurement);

var server = myRegisters.createSocket();

varmodbusClient;

varreadRegisters = function(start, count, callback) {

modbusClient.readRegisters(start, count, function(err, data) {

callback(err, data);

});

};

slide48

varreadRegisters = function(offset, register_count, callback) {

callbacks[++transactionId] = function(response_message) {

callback(0, response_message.slice(10, 10 + register_count*2));

};

var fc = 0x04;

var message = newbuffer.Buffer(12);

message.writeUInt16LE(transactionId, 0);

message.writeUInt16LE(protocolId, 2);

message.writeUInt16LE(6, 4);

message.writeUInt8(unitId, 6);

message.writeUInt8(fc, 7);

message.writeUInt16LE(offset, 8);

message.writeUInt16LE(register_count, 10);

client.write(message);

};

slide55

publicclassHomeControllerTest

{

[Test]

publicvoidGetEmployeeAvailabilityShouldXXX()

{

varhomeController = newHomeController();

var result = homeController.GetEmployeeAvailability();

result.Should().BeNull();

}

}

System.NullReferenceException : Object reference not set to an instance of an object.

at Exilesoft.MyTime.Controllers.HomeController.GetEmployeeAvailability() in HomeController.cs: line 190 at HomeControllerTest.GetEmployeeAvailabilityShouldXXX() in HomeControllerTest.cs: line 11

slide56

publicJsonResultGetEmployeeAvailability()

{

stringspecialEventFullText = string.Empty;

DateTime? selectedDate = (DateTime)Session[selectedDateKey];

if (selectedDate == null)

selectedDate = DateTime.Now;

Ctrl-r, ctrl-m

privateDateTimeSelectedDate

{

get

{

if (Session == null) returnDateTime.Now;

return ((DateTime?) Session[selectedDateKey]) ?? DateTime.Now;

}

}

slide57

Test goes reeeely slow and bombs:

System.Data.SqlClient.SqlException : A network-related or instance-specific error occurred while establishing a connection to SQL Server.

slide58

privatestringDisplaySpecialEvent(stringspecialEventFullText)

{

List<SpecialEvent> specilEventList = dbContext.SpecialEvents.Where(c => c.EventFromDate <= DateTime.Today && DateTime.Today <= c.EventToDate).ToList();

Ctrl-r, ctrl-m

privateIEnumerable<SpecialEvent> SpecialEvents

{

get { returndbContext.SpecialEvents; }

}

slide59

publicHomeController()

{

dbContext = newContext();

SpecialEvents = dbContext.SpecialEvents;

}

publicHomeController(IEnumerable<SpecialEvent> specialEvents)

{

SpecialEvents = specialEvents;

}

slide60

[Test]

publicvoidGetEmployeeAvailabilityShouldXXX()

{

varhomeController = newHomeController(newList<SpecialEvent>());

var result = homeController.GetEmployeeAvailability();

result.Should().BeNull();

}

slide62

publicJsonResultGetEmployeeAvailability()

{

stringspecialEventFullText = string.Empty;

varselectedDate = SelectedDate;

//SpecialEventspecilEvent = dbContext.SpecialEvents.Where(c => c.EventFromDate.Value.Year == selectedDate.Value.Year &&

// c.EventFromDate.Value.Month == selectedDate.Value.Month && c.EventFromDate.Value.Day == selectedDate.Value.Day).SingleOrDefault();

specialEventFullText = this.DisplaySpecialEvent(specialEventFullText);

varallAttendanceForDay = dbContext.Attendances.Where(a => a.Employee.IsEnable == true && a.Year == selectedDate.Year &&

a.Month == selectedDate.Month && a.Day == selectedDate.Day);

slide63

publicJsonResultGetEmployeeAvailability()

{

stringspecialEventFullText = string.Empty;

varselectedDate = SelectedDate;

specialEventFullText = this.DisplaySpecialEvent(specialEventFullText);

varallAttendanceForDay = dbContext.Attendances.Where(a => a.Employee.IsEnable == true && a.Year == selectedDate.Year &&

a.Month == selectedDate.Month && a.Day == selectedDate.Day);

slide64

publicJsonResultGetEmployeeAvailability()

{

varselectedDate = SelectedDate;

stringspecialEventFullText = DisplaySpecialEvent(string.Empty);

varallAttendanceForDay = dbContext.Attendances.Where(a => a.Employee.IsEnable == true && a.Year == selectedDate.Year &&

a.Month == selectedDate.Month && a.Day == selectedDate.Day);

slide65

Session[attendanceListKey] = result.ToList();

if (Session != null)

Session[attendanceListKey] = result.ToList();

slide66

publicList<int> GetExitLocationMachins()

{

string _exitLocationMachines = ConfigurationManager.AppSettings["ExitLocationMachines"].ToString();

List<int> _locationIDList = newList<int>();

foreach (stringlocationIDin _exitLocationMachines.Split(','))

_locationIDList.Add(int.Parse(locationID));

return _locationIDList;

}

Really!?

“Machins”?!

slide67

publicList<int> GetExitLocationMachins()

{

string _exitLocationMachines = ConfigurationManager.AppSettings["ExitLocationMachines"].ToString();

List<int> _locationIDList = newList<int>();

foreach (stringlocationIDin _exitLocationMachines.Split(','))

_locationIDList.Add(int.Parse(locationID));

return _locationIDList;

}

Ctrl-r, ctrl-r

publicList<int> GetExitLocationMachines()

{

string _exitLocationMachines = ConfigurationManager.AppSettings["ExitLocationMachines"].ToString();

List<int> _locationIDList = newList<int>();

foreach (stringlocationIDin _exitLocationMachines.Split(','))

_locationIDList.Add(int.Parse(locationID));

return _locationIDList;

}

slide68

[Test]

publicvoidGetEmployeeAvailabilityShouldXXX()

{

ConfigurationManager.AppSettings["ExitLocationMachines"] = "200,300";

varhomeController = newHomeController(newList<SpecialEvent>(), newList<Attendance>());

var result = homeController.GetEmployeeAvailability();

result.Should().BeNull();

}

slide69

internalstaticList<EmployeeData> GetAllEmployees()

{

//vartempValue = CasheEmployeeData();

string data = newWebClient().DownloadString(string.Format(ConfigurationManager.AppSettings["EmployeeWebApi"], "GetMyTimeEmployees"));

JavaScriptSerializerjsonSerializer = newJavaScriptSerializer();

cacheEmployeeDataList = (List<EmployeeData>)jsonSerializer.Deserialize(data, typeof(List<EmployeeData>));

List<int> employeeEnrollmentsIds = EmployeeEnrollmentRepository.GetEmployeeEnrollments().Select(s=>s.EmployeeId).ToList();

returncacheEmployeeDataList.Where(s => employeeEnrollmentsIds.Contains(s.Id)).ToList();

}

slide70

publicIEnumerable<EmployeeData> EmployeeWebList { get; set; }

publicHomeController()

{

dbContext = newContext();

SpecialEvents = dbContext.SpecialEvents;

Attendances = dbContext.Attendances;

EmployeeWebList = newEmployeeWebGateway();

}

publicHomeController(IEnumerable<SpecialEvent> specialEvents, IEnumerable<Attendance> attendances, IEnumerable<EmployeeData> employeeWebList)

{

Attendances = attendances;

SpecialEvents = specialEvents;

EmployeeWebList = employeeWebList;

}

slide71

publicJsonResultGetEmployeeAvailability()

{

...

intonsiteEmployeeCount = EmployeeRepository.EmployeesOnSite(selectedDate);

Ctrl-r, ctrl-i

intonsiteEmployeeCount = dbContext.EmployeesOnSite.Count(a => a.FromDate <= (DateTime?) selectedDate && a.ToDate >= (DateTime?) selectedDate);

slide72

Expected object to be <null>, but found System.Web.Mvc.JsonResult{ ContentEncoding = <null> ContentType = <null> Data = { GraphData = Time,Count, PeopleIn = 0, PeopleOut = 0, absentEmployeeCount = 0, totalEmployeeCount = 0, onSiteCount = 0, specilEvent = } JsonRequestBehavior = DenyGetMaxJsonLength = <null> RecursionLimit = <null>}.

Hurrah!

slide73

[Test]

publicvoidGetEmployeeAvailabilityShouldXXX()

{

ConfigurationManager.AppSettings["ExitLocationMachines"] = "200,300";

varhomeController = newHomeController(

newList<SpecialEvent>(),

newList<Attendance>(),

newList<EmployeeData>(),

newList<EmployeeOnSite>());

var result = homeController.GetEmployeeAvailability();

result.Should().BeNull();

}

slide74

publicHomeController()

{

dbContext = newContext();

SpecialEvents = dbContext.SpecialEvents;

EmployeesOnSite = dbContext.EmployeesOnSite;

Attendances = dbContext.Attendances;

EmployeeWebList = newEmployeeWebGateway();

}

publicHomeController(IEnumerable<SpecialEvent> specialEvents, IEnumerable<Attendance> attendances, IEnumerable<EmployeeData> employeeWebList, IEnumerable<EmployeeOnSite> employeesOnSite)

{

EmployeesOnSite = employeesOnSite;

Attendances = attendances;

SpecialEvents = specialEvents;

EmployeeWebList = employeeWebList;

}

slide75

[Test]

publicvoidGetEmployeeAvailabilityShouldTrackArrivedEmployees()

{

ConfigurationManager.AppSettings["ExitLocationMachines"] = "200,300";

var today = DateTime.Today.AddDays(-1);

var employee1 = newEmployeeEnrollment { IsEnable = true, EnrollNo = 1 };

var employee2 = newEmployeeEnrollment { IsEnable = true, EnrollNo = 2 };

_attendances.Add(SampleAttendance(today.AddHours(12), employee1));

_attendances.Add(SampleAttendance(today.AddHours(13).AddMinutes(30), employee2));

varhomeController = newHomeController(

newList<SpecialEvent>(),

_attendances,

newList<EmployeeData>(),

newList<EmployeeOnSite>());

homeController.SelectedDate = today.AddHours(19).AddMinutes(23);

var result = homeController.GetEmployeeAvailability();

stringgraphData = GetProperty(result.Data, "GraphData").ToString();

graphData.Should().Contain("Time,Count\n");

graphData.Should().Contain("11:00:00,0");

graphData.Should().Contain("13:00:00,1");

graphData.Should().Contain("14:00:00,2");

graphData.Should().Contain("19:23:00,2");

graphData.Should().NotContain("20:20:00,");

}

slide81

[Test]

publicvoidGetEmployeeAvailabilityShouldReduceEmployeesWhenExit()

{

_attendances.Add(SampleAttendance(_today.AddHours(12).AddMinutes(30), _employee1));

_attendances.Add(SampleAttendance(_today.AddHours(13).AddMinutes(20), _employee1, "out", 200));

_homeController.SelectedDate = _today.AddHours(19).AddMinutes(23);

var result = _homeController.GetEmployeeAvailability();

stringgraphData = GetProperty(result.Data, "GraphData").ToString();

graphData.Should().Contain("12:00:00,0");

graphData.Should().Contain("13:00:00,1");

graphData.Should().Contain("14:00:00,0");

}

slide82

[Test]

publicvoidGetEmployeeAvailabilityShouldCountEmployeesInside()

{

_attendances.Add(SampleAttendance(_today.AddHours(12).AddMinutes(30), _employee1));

_attendances.Add(SampleAttendance(_today.AddHours(13).AddMinutes(20), _employee1, "out", 200));

_homeController.SelectedDate = _today.AddHours(8).AddMinutes(31);

GetProperty(_homeController.GetEmployeeAvailability().Data, "PeopleOut").Should().Be(0);

_homeController.SelectedDate = _today.AddHours(12).AddMinutes(31);

GetProperty(_homeController.GetEmployeeAvailability().Data, "PeopleIn").Should().Be(1);

GetProperty(_homeController.GetEmployeeAvailability().Data, "PeopleOut").Should().Be(0);

_homeController.SelectedDate = _today.AddHours(13).AddMinutes(21);

GetProperty(_homeController.GetEmployeeAvailability().Data, "PeopleIn").Should().Be(0);

GetProperty(_homeController.GetEmployeeAvailability().Data, "PeopleOut").Should().Be(1);

}

slide85

Rename

Extract method

Introduce variable

Introduce parameters

Inline method

slide88

Do 100 refactorings per day

Print out a cheat sheet with resharperrefactorings

slide91

Give me feedback:

How likely are you to recommend this session to a co-worker on a scale from 1 (lowest) to 10 (highest)?

Optional: What's the reason for your number?

thank you
Thank you

jbr@exilesoft.com

http://JohannesBrodwall.com

http://exilesoft.com

http://twitter.com/jhannes

slide93

Give me feedback:

How likely are you to recommend this session to a co-worker on a scale from 1 (lowest) to 10 (highest)?

Optional: What's the reason for your number?