96
Константность как инструмент повышения качества кода Кудрин Игорь

Используем неизменяемые данные и создаем качественный код — Игорь Кудрин, 2ГИС

  • Upload
    2-

  • View
    158

  • Download
    2

Embed Size (px)

Citation preview

Page 1: Используем неизменяемые данные и создаем качественный код — Игорь Кудрин, 2ГИС

Константность как

инструмент повышения

качества кода Кудрин Игорь

Page 2: Используем неизменяемые данные и создаем качественный код — Игорь Кудрин, 2ГИС

Обо мне

Page 3: Используем неизменяемые данные и создаем качественный код — Игорь Кудрин, 2ГИС
Page 4: Используем неизменяемые данные и создаем качественный код — Игорь Кудрин, 2ГИС
Page 5: Используем неизменяемые данные и создаем качественный код — Игорь Кудрин, 2ГИС
Page 6: Используем неизменяемые данные и создаем качественный код — Игорь Кудрин, 2ГИС

Data Analysis designed by Brennan Novak from the thenounproject.com

Page 7: Используем неизменяемые данные и создаем качественный код — Игорь Кудрин, 2ГИС

Mesh Network designed by Lance Weisser from the thenounproject.com

Page 8: Используем неизменяемые данные и создаем качественный код — Игорь Кудрин, 2ГИС

Overlap designed by Nadir Balcikli from the thenounproject.com

Page 9: Используем неизменяемые данные и создаем качественный код — Игорь Кудрин, 2ГИС

Fibonacci Circles designed by Jae Aquino from the thenounproject.com

Page 10: Используем неизменяемые данные и создаем качественный код — Игорь Кудрин, 2ГИС

Проблема

Page 11: Используем неизменяемые данные и создаем качественный код — Игорь Кудрин, 2ГИС

• Язык C++ предоставляет слишком много возможностей

template <typename T> T f(T&& t);

#define M(...) { __VA_ARGS__ }

class virtual

public decltype

const

auto a = [](){}; throw

Page 12: Используем неизменяемые данные и создаем качественный код — Игорь Кудрин, 2ГИС

• Язык C++ предоставляет слишком много возможностей

• Нет времени научиться использовать их правильно

Page 13: Используем неизменяемые данные и создаем качественный код — Игорь Кудрин, 2ГИС

• Язык C++ предоставляет слишком много возможностей

• Нет времени научиться использовать их правильно

• Следствие: сложный и ненадёжный код

Page 14: Используем неизменяемые данные и создаем качественный код — Игорь Кудрин, 2ГИС

Что делать?

Page 15: Используем неизменяемые данные и создаем качественный код — Игорь Кудрин, 2ГИС

• Не использовать лишнего

Page 16: Используем неизменяемые данные и создаем качественный код — Игорь Кудрин, 2ГИС

• Не использовать лишнего • Множественное наследование

Page 17: Используем неизменяемые данные и создаем качественный код — Игорь Кудрин, 2ГИС

• Не использовать лишнего • Множественное наследование • Макросы

Page 18: Используем неизменяемые данные и создаем качественный код — Игорь Кудрин, 2ГИС

• Не использовать лишнего • Множественное наследование • Макросы • Шаблоны

Page 19: Используем неизменяемые данные и создаем качественный код — Игорь Кудрин, 2ГИС

• Не использовать лишнего • Множественное наследование • Макросы • Шаблоны • Исключения

Page 20: Используем неизменяемые данные и создаем качественный код — Игорь Кудрин, 2ГИС

• Не использовать лишнего • Множественное наследование • Макросы • Шаблоны • Исключения • Наследование реализации

Page 21: Используем неизменяемые данные и создаем качественный код — Игорь Кудрин, 2ГИС

• Не использовать лишнего • Упрощать интерфейс

Page 22: Используем неизменяемые данные и создаем качественный код — Игорь Кудрин, 2ГИС

• Не использовать лишнего • Упрощать интерфейс

• Минимум публичных методов

Page 23: Используем неизменяемые данные и создаем качественный код — Игорь Кудрин, 2ГИС

• Не использовать лишнего • Упрощать интерфейс

• Минимум публичных методов • Избегать парных операций

Page 24: Используем неизменяемые данные и создаем качественный код — Игорь Кудрин, 2ГИС

• Не использовать лишнего • Упрощать интерфейс

• Минимум публичных методов • Избегать парных операций • Стабильное состояние

Page 25: Используем неизменяемые данные и создаем качественный код — Игорь Кудрин, 2ГИС

• Не использовать лишнего • Упрощать интерфейс • Использовать const

Page 26: Используем неизменяемые данные и создаем качественный код — Игорь Кудрин, 2ГИС

Неизменяемая переменная

Page 27: Используем неизменяемые данные и создаем качественный код — Игорь Кудрин, 2ГИС

std::vector<int> v = gen_v(); int s; for (int i : v) { s += i; } ... f(s); h(s);

Page 28: Используем неизменяемые данные и создаем качественный код — Игорь Кудрин, 2ГИС

std::vector<int> v = gen_v(); int s; for (int i : v) { s += i; } ... f(s); h(s);

Page 29: Используем неизменяемые данные и создаем качественный код — Игорь Кудрин, 2ГИС

> gcc –Wall –std=c++11 x.cpp No warnings

std::vector<int> v = gen_v(); int s; for (int i : v) { s += i; } ... f(s); h(s);

Page 30: Используем неизменяемые данные и создаем качественный код — Игорь Кудрин, 2ГИС

> gcc –Wall –std=c++11 x.cpp No warnings > gcc –Wall –O –std=c++11 x.cpp warning: ‘s’ may be used uninitialized

std::vector<int> v = gen_v(); int s; for (int i : v) { s += i; } ... f(s); h(s);

Page 31: Используем неизменяемые данные и создаем качественный код — Игорь Кудрин, 2ГИС

std::vector<int> v = gen_v(); int s = 0; for (int i : v) { s += i; } ... f(s); h(s);

Page 32: Используем неизменяемые данные и создаем качественный код — Игорь Кудрин, 2ГИС

std::vector<int> v = gen_v(); int s = 0; for (int i : v) { s += i; } ... f(s); h(s);

А вдруг? void f(int&);

Page 33: Используем неизменяемые данные и создаем качественный код — Игорь Кудрин, 2ГИС

std::vector<int> v = gen_v(); int s = std::accumulate(begin(v), end(v), 0); ... f(s); h(s);

Page 34: Используем неизменяемые данные и создаем качественный код — Игорь Кудрин, 2ГИС

const std::vector<int> v = gen_v(); const int s = std::accumulate(begin(v), end(v), 0); ... f(s); h(s);

Page 35: Используем неизменяемые данные и создаем качественный код — Игорь Кудрин, 2ГИС

const std::vector<int> v = gen_v(); const int s = std::accumulate(begin(v), end(v), 0); ... f(s); h(s);

• Код становится проще

Page 36: Используем неизменяемые данные и создаем качественный код — Игорь Кудрин, 2ГИС

const std::vector<int> v = gen_v(); const int s = std::accumulate(begin(v), end(v), 0); ... f(s); h(s);

• Код становится проще • Защищает от части ошибок

Page 37: Используем неизменяемые данные и создаем качественный код — Игорь Кудрин, 2ГИС

const std::vector<int> v = gen_v(); const int s = std::accumulate(begin(v), end(v), 0); ... f(s); h(s);

• Код становится проще • Защищает от части ошибок • Способствует рефакторингу

Page 38: Используем неизменяемые данные и создаем качественный код — Игорь Кудрин, 2ГИС

Неизменяемый класс

Page 39: Используем неизменяемые данные и создаем качественный код — Игорь Кудрин, 2ГИС

class Point { public: Point(int x, int y) : x_(x), y_(y) {} int X() const { return x_; } int Y() const { return y_; } void Move(int ox, int oy) { x_ += ox; y_ += oy; } private: int x_, y_; };

Page 40: Используем неизменяемые данные и создаем качественный код — Игорь Кудрин, 2ГИС

Point Thread1 Thread2

Move()

X()

Y()

Page 41: Используем неизменяемые данные и создаем качественный код — Игорь Кудрин, 2ГИС

class Point { public: Point(int x, int y) : x_(x), y_(y) {} int X() const { Lock lock(mtx_); return x_; } int Y() const { Lock lock(mtx_); return y_; } void Move(int ox, int oy) { Lock lock(mtx_); x_ += ox; y_ += oy; } private: int x_, y_; mutable std::mutex mtx_; typedef std::lock_guard<std::mutex> Lock; };

Page 42: Используем неизменяемые данные и создаем качественный код — Игорь Кудрин, 2ГИС

Point Thread1 Thread2

Move() X()

Y()

Page 43: Используем неизменяемые данные и создаем качественный код — Игорь Кудрин, 2ГИС

// External synchronization is required! class Point { public: Point(int x, int y) : x_(x), y_(y) {} int X() const { return x_; } int Y() const { return y_; } void Move(int ox, int oy) { x_ += ox; y_ += oy; } private: int x_, y_; };

Page 44: Используем неизменяемые данные и создаем качественный код — Игорь Кудрин, 2ГИС

Point Thread1 Thread2 Mutex

Lock()

X()

Y()

Lock()

Move()

Unlock()

Unlock()

Page 45: Используем неизменяемые данные и создаем качественный код — Игорь Кудрин, 2ГИС

class Point { public: Point(int x, int y) : x_(x), y_(y) {} int X() const { return x_; } int Y() const { return y_; } Point Move(int ox, int oy) const { return Point(x_ + ox, y_ + oy); } private: int x_, y_; };

Page 46: Используем неизменяемые данные и создаем качественный код — Игорь Кудрин, 2ГИС

class Point { public: Point(int x, int y) : x_(x), y_(y) {} int X() const { return x_; } int Y() const { return y_; } Point Move(int ox, int oy) const { return Point(x_ + ox, y_ + oy); } private: int x_, y_; };

Point p(10, 20); p = Point(20, 10);

Page 47: Используем неизменяемые данные и создаем качественный код — Игорь Кудрин, 2ГИС

class Point { public: Point(int x, int y) : x_(x), y_(y) {} int X() const { return x_; } int Y() const { return y_; } Point Move(int ox, int oy) const { return Point(x_ + ox, y_ + oy); } private: const int x_, y_; };

Page 48: Используем неизменяемые данные и создаем качественный код — Игорь Кудрин, 2ГИС

class Point { public: Point(int x, int y) : x_(x), y_(y) {} Point& operator=(const Point&) = delete; int X() const { return x_; } int Y() const { return y_; } Point Move(int ox, int oy) const { return Point(x_ + ox, y_ + oy); } private: const int x_, y_; };

Page 49: Используем неизменяемые данные и создаем качественный код — Игорь Кудрин, 2ГИС

Point2

Thread1 Thread2 Point1

X()

Y()

Move()

Page 50: Используем неизменяемые данные и создаем качественный код — Игорь Кудрин, 2ГИС

Point2

Thread1 Thread2 Point1

X()

Y()

Move()

X()

Y()

Page 51: Используем неизменяемые данные и создаем качественный код — Игорь Кудрин, 2ГИС

Point2

Thread1 Thread2 Point1

X()

Y()

Move()

X()

Y() X()

Y()

Page 52: Используем неизменяемые данные и создаем качественный код — Игорь Кудрин, 2ГИС

• Стабильное состояние

Page 53: Используем неизменяемые данные и создаем качественный код — Игорь Кудрин, 2ГИС

• Стабильное состояние • Невозможно сломать

Page 54: Используем неизменяемые данные и создаем качественный код — Игорь Кудрин, 2ГИС

• Стабильное состояние • Невозможно сломать

например, неправильной внешней синхронизацией

Page 55: Используем неизменяемые данные и создаем качественный код — Игорь Кудрин, 2ГИС

• Стабильное состояние • Невозможно сломать • Экономит время на синхронизации

Page 56: Используем неизменяемые данные и создаем качественный код — Игорь Кудрин, 2ГИС

• Стабильное состояние • Невозможно сломать • Экономит время на синхронизации • Удобно для многопоточной среды

Page 57: Используем неизменяемые данные и создаем качественный код — Игорь Кудрин, 2ГИС

Персистентные структуры

Page 58: Используем неизменяемые данные и создаем качественный код — Игорь Кудрин, 2ГИС

K

D

Q L

N

F

G B

Page 59: Используем неизменяемые данные и создаем качественный код — Игорь Кудрин, 2ГИС

K

D

Q L

N

F

G B

P

Page 60: Используем неизменяемые данные и создаем качественный код — Игорь Кудрин, 2ГИС

K

D

Q L

N

F

G B

P

?

Page 61: Используем неизменяемые данные и создаем качественный код — Игорь Кудрин, 2ГИС

K

D

Q L

N

F

G B

P

Page 62: Используем неизменяемые данные и создаем качественный код — Игорь Кудрин, 2ГИС

K

D

Q L

N

F

G B

P

K’

N’

Q’

D’

L’ B’ G’

F’

Page 63: Используем неизменяемые данные и создаем качественный код — Игорь Кудрин, 2ГИС

K

D

Q L

N

F

G B

P

K’

N’

Q’

Page 64: Используем неизменяемые данные и создаем качественный код — Игорь Кудрин, 2ГИС

K

D

Q L

N

F

G B

P

K’

N’

Q’

std::shared_ptr<T>

Page 65: Используем неизменяемые данные и создаем качественный код — Игорь Кудрин, 2ГИС

Небольшой пример

Page 66: Используем неизменяемые данные и создаем качественный код — Игорь Кудрин, 2ГИС
Page 67: Используем неизменяемые данные и создаем качественный код — Игорь Кудрин, 2ГИС

Magnifying Glass designed by Karl Schaeffler from the thenounproject.com

Поиск

Page 68: Используем неизменяемые данные и создаем качественный код — Игорь Кудрин, 2ГИС

Magnifying Glass designed by Karl Schaeffler from the thenounproject.com

Book designed by Olivier Guin from the thenounproject.com

Поиск

Справочник

Page 69: Используем неизменяемые данные и создаем качественный код — Игорь Кудрин, 2ГИС

Magnifying Glass designed by Karl Schaeffler from the thenounproject.com

Book designed by Olivier Guin from the thenounproject.com

Map designed by Nicholas Menghini from the thenounproject.com

Поиск

Справочник

Карта

Page 70: Используем неизменяемые данные и создаем качественный код — Игорь Кудрин, 2ГИС

Magnifying Glass designed by Karl Schaeffler from the thenounproject.com

Book designed by Olivier Guin from the thenounproject.com

Map designed by Nicholas Menghini from the thenounproject.com

Folder Tree designed by Juan Pablo Bravo from the thenounproject.com

Поиск

Справочник

Карта

VFS

Page 71: Используем неизменяемые данные и создаем качественный код — Игорь Кудрин, 2ГИС

Magnifying Glass designed by Karl Schaeffler from the thenounproject.com

Book designed by Olivier Guin from the thenounproject.com

Map designed by Nicholas Menghini from the thenounproject.com

Folder Tree designed by Juan Pablo Bravo from the thenounproject.com

Поиск

Справочник

Карта

VFS

Page 72: Используем неизменяемые данные и создаем качественный код — Игорь Кудрин, 2ГИС

Magnifying Glass designed by Karl Schaeffler from the thenounproject.com

Book designed by Olivier Guin from the thenounproject.com

Map designed by Nicholas Menghini from the thenounproject.com

Folder Tree designed by Juan Pablo Bravo from the thenounproject.com

Поиск

Справочник

Карта

Обновления

VFS

Page 73: Используем неизменяемые данные и создаем качественный код — Игорь Кудрин, 2ГИС

Magnifying Glass designed by Karl Schaeffler from the thenounproject.com

Book designed by Olivier Guin from the thenounproject.com

Map designed by Nicholas Menghini from the thenounproject.com

Folder Tree designed by Juan Pablo Bravo from the thenounproject.com

Поиск

Справочник

Карта

Обновления

VFS

Page 74: Используем неизменяемые данные и создаем качественный код — Игорь Кудрин, 2ГИС

Виртуальная файловая система

Page 75: Используем неизменяемые данные и создаем качественный код — Игорь Кудрин, 2ГИС

IDir

File(string): IFilePtr Dir(string): IDirPtr

VfsNode

Create(path, dir): VfsNodePtr AddDir(path, dir): VfsNodePtr RemoveDir(path): VfsNodePtr

0..*

Path

Head(): string Tail(): Path

Vfs

Root(): IDirPtr AddDir(path, dir) RemoveDir(path)

1

typedef std::shared_ptr<const IFile> IFilePtr; typedef std::shared_ptr<const IDir> IDirPtr;

Page 76: Используем неизменяемые данные и создаем качественный код — Игорь Кудрин, 2ГИС

IDir

File(string): IFilePtr Dir(string): IDirPtr

VfsNode

Create(path, dir): VfsNodePtr AddDir(path, dir): VfsNodePtr RemoveDir(path): VfsNodePtr

0..*

Path

Head(): string Tail(): Path

Vfs

1 Root(): IDirPtr AddDir(path, dir) RemoveDir(path)

typedef std::shared_ptr<const IFile> IFilePtr; typedef std::shared_ptr<const IDir> IDirPtr;

Page 77: Используем неизменяемые данные и создаем качественный код — Игорь Кудрин, 2ГИС

IDir

File(string): IFilePtr Dir(string): IDirPtr

VfsNode

Create(path, dir): VfsNodePtr AddDir(path, dir): VfsNodePtr RemoveDir(path): VfsNodePtr

0..*

Path

Head(): string Tail(): Path

Vfs

1 Root(): IDirPtr AddDir(path, dir) RemoveDir(path)

Page 78: Используем неизменяемые данные и создаем качественный код — Игорь Кудрин, 2ГИС

IDir

File(string): IFilePtr Dir(string): IDirPtr

VfsNode

Create(path, dir): VfsNodePtr AddDir(path, dir): VfsNodePtr RemoveDir(path): VfsNodePtr

0..*

Path

Head(): string Tail(): Path

Vfs

1 Root(): IDirPtr AddDir(path, dir) RemoveDir(path)

typedef std::shared_ptr<const VfsNode> VfsNodePtr;

Page 79: Используем неизменяемые данные и создаем качественный код — Игорь Кудрин, 2ГИС

IDir

File(string): IFilePtr Dir(string): IDirPtr

VfsNode

Create(path, dir): VfsNodePtr AddDir(path, dir): VfsNodePtr RemoveDir(path): VfsNodePtr

0..*

Path

Head(): string Tail(): Path

Vfs

1 Root(): IDirPtr AddDir(path, dir) RemoveDir(path)

typedef std::shared_ptr<const VfsNode> VfsNodePtr;

Page 80: Используем неизменяемые данные и создаем качественный код — Игорь Кудрин, 2ГИС

IDir

File(string): IFilePtr Dir(string): IDirPtr

VfsNode

Create(path, dir): VfsNodePtr AddDir(path, dir): VfsNodePtr RemoveDir(path): VfsNodePtr

0..*

Path

Head(): string Tail(): Path

Vfs

1 Root(): IDirPtr AddDir(path, dir) RemoveDir(path)

typedef std::shared_ptr<const VfsNode> VfsNodePtr;

Page 81: Используем неизменяемые данные и создаем качественный код — Игорь Кудрин, 2ГИС

class Vfs { public: IDirPtr Root() const; void AddDir(const Path& path, IDirPtr dir); void RemoveDir(const Path& path); };

Page 82: Используем неизменяемые данные и создаем качественный код — Игорь Кудрин, 2ГИС

class Vfs { public: IDirPtr Root() const; void AddDir(const Path& path, IDirPtr dir); void RemoveDir(const Path& path); private: std::mutex update_mtx_; std::atomic<VfsNodePtr> root_; };

Page 83: Используем неизменяемые данные и создаем качественный код — Игорь Кудрин, 2ГИС

void Vfs::AddDir(const Path& path, IDirPtr dir) { std::lock_guard<std::mutex> lock(update_mtx_); const VfsNodePtr root = root_; root_ = root ? root->AddDir(path, std::move(dir)) : VfsNode::Create(path, std::move(dir)); }

std::mutex update_mtx_; std::atomic<VfsNodePtr> root_;

Page 84: Используем неизменяемые данные и создаем качественный код — Игорь Кудрин, 2ГИС

void Vfs::RemoveDir(const Path& path) { std::lock_guard<std::mutex> lock(update_mtx_); if (const VfsNodePtr root = root_) { root_ = root->RemoveDir(path); } }

std::mutex update_mtx_; std::atomic<VfsNodePtr> root_;

Page 85: Используем неизменяемые данные и создаем качественный код — Игорь Кудрин, 2ГИС

IDirPtr Vfs::Root() const { return root_.load(); }

std::mutex update_mtx_; std::atomic<VfsNodePtr> root_;

Page 86: Используем неизменяемые данные и создаем качественный код — Игорь Кудрин, 2ГИС

Выводы

Page 87: Используем неизменяемые данные и создаем качественный код — Игорь Кудрин, 2ГИС

• Используем const

Page 88: Используем неизменяемые данные и создаем качественный код — Игорь Кудрин, 2ГИС

• Используем const • Делаем «неубиваемые» классы

Page 89: Используем неизменяемые данные и создаем качественный код — Игорь Кудрин, 2ГИС

• Используем const • Делаем «неубиваемые» классы • Используем персистентные структуры

Page 90: Используем неизменяемые данные и создаем качественный код — Игорь Кудрин, 2ГИС

Кудрин Игорь [email protected]

Mail designed by Andy Fuchs from the thenounproject.com

[email protected]

Page 91: Используем неизменяемые данные и создаем качественный код — Игорь Кудрин, 2ГИС

struct IFile; typedef std::shared_ptr<const IFile> IFilePtr; struct IDir; typedef std::shared_ptr<const IDir> IDirPtr; struct IDir { virtual IFilePtr File(const std::string& name) const = 0; virtual IDirPtr Dir(const std::string& name) const = 0; };

Бонус: IDir

Page 92: Используем неизменяемые данные и создаем качественный код — Игорь Кудрин, 2ГИС

class Path { public: const std::string& Head() const; Path Tail() const; };

Бонус: Path

Page 93: Используем неизменяемые данные и создаем качественный код — Игорь Кудрин, 2ГИС

class VfsNode : public IDir, public std::enable_shared_from_this<VfsNode> { typedef std::unordered_map<std::string, VfsNodePtr> ChildrenMap; const IDirPtr dir_; const ChildrenMap children_; public: explicit VfsNode(IDirPtr dir) : dir_(std::move(dir)), children_() {} explicit VfsNode(ChildrenMap children) : dir_(), children_(std::move(children)) {} };

Бонус: VfsNode

Page 94: Используем неизменяемые данные и создаем качественный код — Игорь Кудрин, 2ГИС

VfsNodePtr VfsNode::Create(const Path& path, IDirPtr dir) { if (path.Head().empty()) { return std::make_shared<VfsNode>(std::move(dir)); } else { ChildrenMap children = { { path.Head(), Create(path.Tail(), std::move(dir)) } }; return std::make_shared<VfsNode>(std::move(children)); } }

Бонус: VfsNode

Page 95: Используем неизменяемые данные и создаем качественный код — Игорь Кудрин, 2ГИС

VfsNodePtr VfsNode::AddDir(const Path& path, IDirPtr dir) const { if (path.Head().empty()) { return std::make_shared<VfsNode>(std::move(dir)); } ChildrenMap children = children_; VfsNodePtr& node = children[path.Head()]; node = (nullptr != node) ? node->AddDir(path.Tail(), std::move(dir)) : VfsNode::Create(path.Tail(), std::move(dir)); return std::make_shared<VfsNode>(std::move(children)); }

Бонус: VfsNode

Page 96: Используем неизменяемые данные и создаем качественный код — Игорь Кудрин, 2ГИС

VfsNodePtr VfsNode::RemoveDir(const Path& path) const { if (path.Head().empty()) return VfsNodePtr(); ChildrenMap children = children_; const auto it = children.find(path.Head()); if (it == children.end()) return shared_from_this(); VfsNodePtr new_child_node = it->second->RemoveDir(path.Tail()); if (new_child_node == it->second) return shared_from_this(); if (new_child_node) it->second.swap(new_child_node); else { children.erase(it); if (children.empty()) return VfsNodePtr(); } return std::make_shared<VfsNode>(std::move(children)); }

Бонус: VfsNode