Working with c++ legacy code

26
Working with C++ Legacy Code Dror Helper | www.helpercode.com | @dhelper

Transcript of Working with c++ legacy code

Page 1: Working with c++ legacy code

Working with C++ Legacy CodeDror Helper | www.helpercode.com | @dhelper

Page 2: Working with c++ legacy code

Consultant @CodeValue

Developing software since 2002Clean Coder & Test Driven Developer

Pluralsight authorhttps://www.pluralsight.com/authors/dror-helper

B: http://helpercode.comT: @dhelper

About.ME

Page 3: Working with c++ legacy code

¯\_(ツ)_/¯What is “Legacy Code”?

Covfefe?

Page 4: Working with c++ legacy code

No longer engineered – continuedly patched

Page 5: Working with c++ legacy code

Difficult to change without breaking functionality

Page 6: Working with c++ legacy code

Written by someone else

Page 7: Working with c++ legacy code

Legacy code has users!

Page 8: Working with c++ legacy code

The good news and bad news

There’s a lot of legacy C++ Code

There’s a good chance you’re doing it

C++ makes it easy to make critical mistakes

C++ is a powerful language

Page 9: Working with c++ legacy code

Gaining control over legacy code

To stop fearing legacy code:

1. Learn what the code does

2. Make sure it keeps doing it!

3. Iterative improvement via Refactoring

Page 10: Working with c++ legacy code

This is a unit test (GTest)

TEST(HolyGrailTests, WhoWeAreTest)

{

Knight knight;

ASSERT_EQ(“Ni!”, knight.Say());

}

Page 11: Working with c++ legacy code

A quick reality check

• Code have dependencies

• Refactoring == code change

• Code change == breaking functionality (78.3%)

• Breaking functionality go home == false

Page 12: Working with c++ legacy code

Sensing and Separation

public void SendEmailTest(){

auto sut = new ...User user;sut->SendEmailToUser(user);

// Sensing problemASSERT???

}

void SendEmailToUser(User user){

EmailClient client;

// Separation problemclient.Send(user.Email, ...);

// Important business logic...

}

Page 13: Working with c++ legacy code

“My focus is to forget the pain of life. Forget the pain, mock the pain, reduce it. And laugh.” Jim Carrey

Page 14: Working with c++ legacy code

The problem - dependencies

My Code

DB

FilesFiles

FilesFiles

Services

Page 15: Working with c++ legacy code

The solution – fake/mock objects

My Code

Fake

Fake

Fake

Page 16: Working with c++ legacy code

Fake objects using Google Mock

class FakeRestApiClient : public RestApiClient

{

public:

MOCK_METHOD2(HttpPost, void(string&, string&));

MOCK_METHOD1(HttpGet, string(string&));

MOCK_METHOD2(HttpPut, void(string&, string&));

MOCK_METHOD1(HttpDelete, void(string&));

};

Page 17: Working with c++ legacy code

Injecting fake objects into existing code

Page 18: Working with c++ legacy code

Not all dependency are created equal

Some are harder/impossible to fake

Non virtual methods

Hard to inherit classes

Static method

callsSingletons

Internally instantiated

Heavy classes

Page 19: Working with c++ legacy code

Faking Singletons

class MySingleton {

static std::once_flag onceFlag;static MySingleton* instance;

public:static MySingleton* GetInstance() {

std::call_once(onceFlag, [] {instance = new MySingleton();

});

return instance;}

private:

MySingleton(){}

}

Page 20: Working with c++ legacy code

Friend is your friend

class MySingletons;

class Rectangle

{

int width, height;

public:

friend FakeRectangle;

};

Page 21: Working with c++ legacy code

Problem: un-fakeable methods

• Static methods

• Hard/impossible to instantiate class

• Non-virtual methods

Cannot be inherited == cannot be injected!

Page 22: Working with c++ legacy code

Using templates for injection

Compile time duck typing a.k.a hi-perf dependency injection

• Can fake unfakeables

• Can fake internally instantiated classes

Page 23: Working with c++ legacy code

Faking Private and Protected Methods

class Foo

{

public:

virtual bool MyPublicMethod(MyClass* c){...};

protected:

virtual void MyProtectedMethod(int a, int b){...};

private:

virtual int MyPrivateMethod(){...}

}

Page 24: Working with c++ legacy code

Faking Private and Protected Methods

class Foo

{

public:

MOCK_METHOD1(MyPublicMethod, bool(MyClass*));

MOCK_METHOD2(MyProtectedMethod, void(int, int));

MOCK_METHOD0(MyPrivateMethod, int());

}

Page 25: Working with c++ legacy code

Taking control of existing legacy code

Legacy code

Write testsCreate fake

objects

Swap dependencies

Refactor & cleanup

Maintainable code

Page 26: Working with c++ legacy code

Thank youDror Helper | @dhelper | http://helpercode.com