Slide 1

Slide 1 text

A presentation by @stuherbert
 for @GanbaroDigital The Final Word about Final? A SOLID Approach To Extensibility

Slide 2

Slide 2 text

@GanbaroDigital I’m here to talk to you about `final` in a type-hinting world.

Slide 3

Slide 3 text

@GanbaroDigital final class Bar { // ... }

Slide 4

Slide 4 text

@GanbaroDigital final class Foo { public function doAction( Bar $input ) { // ... } }

Slide 5

Slide 5 text

@GanbaroDigital final class Foo { public function doAction( Bar $input ) { // ... } }

Slide 6

Slide 6 text

@GanbaroDigital ?? ?? Do you use `final` in your own code?

Slide 7

Slide 7 text

@GanbaroDigital ?? ?? Why do you mark your classes as final?

Slide 8

Slide 8 text

@GanbaroDigital In This Talk 1. `final` and PHP type-hinting 2. An example of `final` in the wild 3. A SOLID approach

Slide 9

Slide 9 text

@GanbaroDigital In This Talk 1. `final` and PHP type-hinting 2. An example of `final` in the wild 3. A SOLID approach

Slide 10

Slide 10 text

@GanbaroDigital In This Talk 1. `final` and PHP type-hinting 2. An example of `final` in the wild 3. A SOLID approach

Slide 11

Slide 11 text

@GanbaroDigital Not everyone will agree that `final` causes problems.

Slide 12

Slide 12 text

@GanbaroDigital These are my experiences.

Slide 13

Slide 13 text

@GanbaroDigital Other opinions are out there.

Slide 14

Slide 14 text

@GanbaroDigital final & PHP Type-Hinting

Slide 15

Slide 15 text

@GanbaroDigital ?? ?? When was `final` introduced to the PHP language?

Slide 16

Slide 16 text

@GanbaroDigital `final` is a PHP keyword introduced in PHP 5.0.0. 13th July 2004.

Slide 17

Slide 17 text

@GanbaroDigital ?? ?? When was type-hinting introduced to the PHP language?

Slide 18

Slide 18 text

@GanbaroDigital class names as type-hints also introduced in PHP 5.0.0. 13th July 2004.

Slide 19

Slide 19 text

@GanbaroDigital Let’s look at classical inheritance first ... ... with type-hinting.

Slide 20

Slide 20 text

@GanbaroDigital class Foo { public function doAction( Bar $input ) { // ... } }

Slide 21

Slide 21 text

@GanbaroDigital class Foo { public function doAction( Bar $input ) { // ... } }

Slide 22

Slide 22 text

@GanbaroDigital Class Foo Method doAction(Bar $input)

Slide 23

Slide 23 text

@GanbaroDigital Class Bar Class Foo Method doAction(Bar $input)

Slide 24

Slide 24 text

@GanbaroDigital Class Bar Class Foo Method doAction(Bar $input) ✓

Slide 25

Slide 25 text

@GanbaroDigital Class Bar

Slide 26

Slide 26 text

@GanbaroDigital Class Bar Class C1 extends Bar

Slide 27

Slide 27 text

@GanbaroDigital Class Bar Class C1 extends Bar Class C2 extends Bar

Slide 28

Slide 28 text

@GanbaroDigital Class Bar Class C1 extends Bar Class C2 extends Bar Class C3 extends Bar

Slide 29

Slide 29 text

@GanbaroDigital Class Bar Class Foo Method doAction(Bar $input)

Slide 30

Slide 30 text

@GanbaroDigital Class Bar Class Foo Method doAction(Bar $input) ✓

Slide 31

Slide 31 text

@GanbaroDigital Class C1 extends Bar Class C2 extends Bar Class C3 extends Bar Class Foo Method doAction(Bar $input)

Slide 32

Slide 32 text

@GanbaroDigital Class C1 extends Bar Class C2 extends Bar Class C3 extends Bar Class Foo Method doAction(Bar $input) ✓ ✓ ✓

Slide 33

Slide 33 text

@GanbaroDigital ?? ?? What changes if we mark class Bar (our method type-hint) as `final`?

Slide 34

Slide 34 text

@GanbaroDigital class Foo { public function doAction( Bar $input ) { // ... } }

Slide 35

Slide 35 text

@GanbaroDigital class Bar { // ... }

Slide 36

Slide 36 text

@GanbaroDigital final class Bar { // ... }

Slide 37

Slide 37 text

@GanbaroDigital Class Bar Class Foo Method doAction(Bar $input)

Slide 38

Slide 38 text

@GanbaroDigital Class Bar Class Foo Method doAction(Bar $input) ✓

Slide 39

Slide 39 text

@GanbaroDigital Class C1 extends Bar Class C2 extends Bar Class C3 extends Bar Class Foo Method doAction(Bar $input)

Slide 40

Slide 40 text

@GanbaroDigital Class C1 extends Bar Class C2 extends Bar Class C3 extends Bar Class Foo Method doAction(Bar $input) ✗ ✗ ✗

Slide 41

Slide 41 text

@GanbaroDigital ?? ?? Why won’t our method accept child classes of class Bar?

Slide 42

Slide 42 text

@GanbaroDigital Class Bar

Slide 43

Slide 43 text

@GanbaroDigital Class C1 extends Bar Class C2 extends Bar Class C3 extends Bar ✗ ✗ ✗ Class Bar

Slide 44

Slide 44 text

@GanbaroDigital `final` classes cannot be extended via inheritance

Slide 45

Slide 45 text

@GanbaroDigital “ The behaviour of `final` classes can be consumed, not modified.

Slide 46

Slide 46 text

@GanbaroDigital “ `final` classes lock down type-hinted method parameters.

Slide 47

Slide 47 text

@GanbaroDigital “It’s unreasonable to expect package maintainers to know 100% of all possible variants.

Slide 48

Slide 48 text

@GanbaroDigital ?? ?? How do we workaround a `final` class in type-hints?

Slide 49

Slide 49 text

@GanbaroDigital ?? ?? What if ... we extend Foo to override the type-hint?

Slide 50

Slide 50 text

@GanbaroDigital class Foo { public function doAction( Bar $input ) { // ... } }

Slide 51

Slide 51 text

@GanbaroDigital class MyFoo extends Foo { public function doAction( Bar $input ) { // ... } }

Slide 52

Slide 52 text

@GanbaroDigital class MyFoo extends Foo { public function doAction( MyBar $input ) { // ... } }

Slide 53

Slide 53 text

@GanbaroDigital PHP Warning: Declaration of MyFoo::doAction(MyBar $input) should be compatible with Foo::doAction(Bar $input) in test.php on line 19

Slide 54

Slide 54 text

@GanbaroDigital The code runs today. Assume today’s warnings may become tomorrow’s fatal errors.

Slide 55

Slide 55 text

@GanbaroDigital

Slide 56

Slide 56 text

@GanbaroDigital class Foo { public function doAction( Bar $input ) { // ... } }

Slide 57

Slide 57 text

@GanbaroDigital final class Foo { public function doAction( Bar $input ) { // ... } }

Slide 58

Slide 58 text

@GanbaroDigital PHP Fatal error: Class MyFoo may not inherit from final class (Foo) in test.php on line 19

Slide 59

Slide 59 text

@GanbaroDigital `final` is excellent at protecting behaviour from third-party change.

Slide 60

Slide 60 text

@GanbaroDigital But when that behaviour is incomplete or outright broken ...

Slide 61

Slide 61 text

@GanbaroDigital final In The Wild

Slide 62

Slide 62 text

@GanbaroDigital UUID RFC

Slide 63

Slide 63 text

@GanbaroDigital https://wiki.php.net/rfc/uuid

Slide 64

Slide 64 text

@GanbaroDigital final class UUID { // ... }

Slide 65

Slide 65 text

@GanbaroDigital UUIDs are globally-unique, non co-ordinated values.

Slide 66

Slide 66 text

@GanbaroDigital They are useful as primary keys.

Slide 67

Slide 67 text

@GanbaroDigital function doSomething (int $id, ...) { // ... }

Slide 68

Slide 68 text

@GanbaroDigital function doSomething (UUID $id, ...) { // ... }

Slide 69

Slide 69 text

@GanbaroDigital ?? ?? What if we want to abstract away our ID scheme?

Slide 70

Slide 70 text

@GanbaroDigital function doSomething (UUID $id, ...) { // ... }

Slide 71

Slide 71 text

@GanbaroDigital function doSomething (MyID $id, ...) { // ... }

Slide 72

Slide 72 text

@GanbaroDigital class MyID extends UUID { // ... }

Slide 73

Slide 73 text

@GanbaroDigital class MyID extends UUID { // ... } ✗

Slide 74

Slide 74 text

@GanbaroDigital `final` class UUID prevents us doing so via inheritance.

Slide 75

Slide 75 text

@GanbaroDigital ?? ?? How could we work around this?

Slide 76

Slide 76 text

@GanbaroDigital “ Use the decorator pattern for abstracting underlying behaviour.

Slide 77

Slide 77 text

@GanbaroDigital class MyID extends UUID { // ... }

Slide 78

Slide 78 text

@GanbaroDigital class MyID { private $uuid; public function __toString( return (string)$this->uuid; ); }

Slide 79

Slide 79 text

@GanbaroDigital class MyID { private $uuid; public function __toString( return (string)$this->uuid; ); }

Slide 80

Slide 80 text

@GanbaroDigital class MyID { private $uuid; public function __toString( return (string)$this->uuid; ); }

Slide 81

Slide 81 text

@GanbaroDigital UUIDs can be used as primary keys.

Slide 82

Slide 82 text

@GanbaroDigital UUIDs perform poorly as primary keys in popular database engines.

Slide 83

Slide 83 text

@GanbaroDigital The solution: CombGUIDs

Slide 84

Slide 84 text

@GanbaroDigital Jimmy Nelson, The Cost of GUIDs as Primary Keys, March 8th, 2002 http://www.informit.com/articles/ article.aspx?p=25862

Slide 85

Slide 85 text

@GanbaroDigital xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx

Slide 86

Slide 86 text

@GanbaroDigital xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx { -- MySQL -- }

Slide 87

Slide 87 text

@GanbaroDigital xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx { --- SQL Server --- }

Slide 88

Slide 88 text

@GanbaroDigital ?? ?? Should we use the decorator pattern to provide CombGUIDs?

Slide 89

Slide 89 text

@GanbaroDigital In this case, we are not adding an abstraction. We are adding a variant.

Slide 90

Slide 90 text

@GanbaroDigital The PHP UUID RFC did not include CombGUIDs.

Slide 91

Slide 91 text

@GanbaroDigital class MysqlCombGUID extends UUID { // ... }

Slide 92

Slide 92 text

@GanbaroDigital class SqlServerCombGUID extends UUID { // ... }

Slide 93

Slide 93 text

@GanbaroDigital final class UUID { // ... }

Slide 94

Slide 94 text

@GanbaroDigital The PHP UUID RFC did not include CombGUIDs ...

Slide 95

Slide 95 text

@GanbaroDigital ... and prevented anyone else from doing so from userland.

Slide 96

Slide 96 text

@GanbaroDigital “It’s unreasonable to expect package maintainers to know 100% of all possible variants.

Slide 97

Slide 97 text

@GanbaroDigital ?? ?? When is it reasonable to actively prevent third-party variants?

Slide 98

Slide 98 text

@GanbaroDigital ?? ?? How do we solve this?

Slide 99

Slide 99 text

@GanbaroDigital A SOLID Approach

Slide 100

Slide 100 text

@GanbaroDigital SOLID are design principles for object-oriented code

Slide 101

Slide 101 text

@GanbaroDigital SOLID Principles • Single responsibility • Open/closed • Liskov substitution • Interface segregation • Dependency inversion

Slide 102

Slide 102 text

@GanbaroDigital SOLID Principles • Single responsibility • Open/closed • Liskov substitution • Interface segregation • Dependency inversion

Slide 103

Slide 103 text

@GanbaroDigital SOLID Principles • Single responsibility • Open/closed • Liskov substitution • Interface segregation • Dependency inversion

Slide 104

Slide 104 text

@GanbaroDigital SOLID Principles • Single responsibility • Open/closed • Liskov substitution • Interface segregation • Dependency inversion

Slide 105

Slide 105 text

@GanbaroDigital SOLID Principles • Single responsibility • Open/closed • Liskov substitution • Interface segregation • Dependency inversion

Slide 106

Slide 106 text

@GanbaroDigital I’m going to focus on how `final` affects two SOLID principles.

Slide 107

Slide 107 text

@GanbaroDigital `final` Affects ...? • Single responsibility • Open/closed • Liskov substitution • Interface segregation • Dependency inversion

Slide 108

Slide 108 text

@GanbaroDigital `final` Affects ... • Single responsibility • Open/closed • Liskov substitution • Interface segregation • Dependency inversion

Slide 109

Slide 109 text

@GanbaroDigital Open/closed: open for extension, closed for modification.

Slide 110

Slide 110 text

@GanbaroDigital `final` ensures that a class is closed for modification!

Slide 111

Slide 111 text

@GanbaroDigital Class C1 extends Bar Class C2 extends Bar Class C3 extends Bar ✗ ✗ ✗ Class Bar

Slide 112

Slide 112 text

@GanbaroDigital We’ve also seen that `final` ensures that the consuming class
 is closed for extension.

Slide 113

Slide 113 text

@GanbaroDigital Class C1 extends Bar Class C2 extends Bar Class C3 extends Bar Class Foo Method doAction(Bar $input) ✗ ✗ ✗

Slide 114

Slide 114 text

@GanbaroDigital Liskov substitution principle: type T may be substituted by a sub-type S without altering the desirable properties of the program.

Slide 115

Slide 115 text

@GanbaroDigital Class Bar Class C1 extends Bar Class C2 extends Bar Class C3 extends Bar

Slide 116

Slide 116 text

@GanbaroDigital Class Bar Class C1 extends Bar Class C2 extends Bar Class C3 extends Bar with caveats!

Slide 117

Slide 117 text

@GanbaroDigital `final` prevents us creating sub-type S at all!

Slide 118

Slide 118 text

@GanbaroDigital final class Bar { // ... }

Slide 119

Slide 119 text

@GanbaroDigital Class C1 extends Bar Class C2 extends Bar Class C3 extends Bar ✗ ✗ ✗ Class Bar

Slide 120

Slide 120 text

@GanbaroDigital ?? ?? Can `final` classes also be SOLID?

Slide 121

Slide 121 text

@GanbaroDigital SOLID Principles • Single responsibility • Open/closed • Liskov substitution • Interface segregation • Dependency inversion

Slide 122

Slide 122 text

@GanbaroDigital ?? ?? What if we used interfaces for type-hinting?

Slide 123

Slide 123 text

@GanbaroDigital final class Bar { // ... }

Slide 124

Slide 124 text

@GanbaroDigital final class Bar implements Interface1 { // ... }

Slide 125

Slide 125 text

@GanbaroDigital class Foo { public function doAction( Bar $input ) { // ... } }

Slide 126

Slide 126 text

@GanbaroDigital class Foo { public function doAction( Interface1 $input ) { // ... } }

Slide 127

Slide 127 text

@GanbaroDigital Class Foo Method doAction(Bar $input)

Slide 128

Slide 128 text

@GanbaroDigital Class Foo Method doAction(Interface1 $input)

Slide 129

Slide 129 text

@GanbaroDigital Class Bar Class Foo Method doAction(Interface1 $input)

Slide 130

Slide 130 text

@GanbaroDigital Class Bar Class Foo Method doAction(Interface1 $input) ✗

Slide 131

Slide 131 text

@GanbaroDigital Class Bar (Interface1) Class Foo Method doAction(Interface1 $input)

Slide 132

Slide 132 text

@GanbaroDigital Class Bar (Interface1) Class Foo Method doAction(Interface1 $input) ✓

Slide 133

Slide 133 text

@GanbaroDigital Class Bar (Interface1)

Slide 134

Slide 134 text

@GanbaroDigital Class C1 extends Bar Class C2 extends Bar Class C3 extends Bar ✗ ✗ ✗ Class Bar (Interface1)

Slide 135

Slide 135 text

@GanbaroDigital Class Bar is still closed for modification. ✓

Slide 136

Slide 136 text

@GanbaroDigital Class Foo is open to new variants. ✓

Slide 137

Slide 137 text

@GanbaroDigital Class Bar (Interface1) Class C1 (Interface1) Class C2 (Interface1) Class C3 (Interface1)

Slide 138

Slide 138 text

@GanbaroDigital Class C1 (Interface1) Class C2 (Interface1) Class C3 (Interface1) Class Foo Method doAction(Interface1 $input)

Slide 139

Slide 139 text

@GanbaroDigital Class C1 (Interface1) Class C2 (Interface1) Class C3 (Interface1) Class Foo Method doAction(Interface1 $input) ✓ ✓ ✓

Slide 140

Slide 140 text

@GanbaroDigital ?? ?? What about sub-types and Liskov’s substitution principle?

Slide 141

Slide 141 text

@GanbaroDigital Class C1 extends Bar Class C2 extends Bar Class C3 extends Bar ✗ ✗ ✗ Class Bar (Interface1)

Slide 142

Slide 142 text

@GanbaroDigital Class Bar (Interface1) Class C1 (Interface1) Class C2 (Interface1) Class C3 (Interface1)

Slide 143

Slide 143 text

@GanbaroDigital final class Bar implements Interface1 { // ... }

Slide 144

Slide 144 text

@GanbaroDigital interface SubType1 extends Interface1 { // ... }

Slide 145

Slide 145 text

@GanbaroDigital Class Bar Class C1 extends Bar Class C2 extends Bar Class C3 extends Bar with caveats!

Slide 146

Slide 146 text

@GanbaroDigital Interface1 SubType 2 SubType1 SubType3

Slide 147

Slide 147 text

@GanbaroDigital

Slide 148

Slide 148 text

@GanbaroDigital final class Foo { public function doAction( Interface1 $input ) { // ... } }

Slide 149

Slide 149 text

@GanbaroDigital

Slide 150

Slide 150 text

@GanbaroDigital final class Foo { public function doAction( Interface1 $input ) { // ... } }

Slide 151

Slide 151 text

@GanbaroDigital final class Foo implements Interface2 { public function doAction( Interface1 $input ) { // ... } }

Slide 152

Slide 152 text

@GanbaroDigital “ Behaviour is closed to modification. The system is open for extension (via variants).

Slide 153

Slide 153 text

@GanbaroDigital UUID RFC

Slide 154

Slide 154 text

@GanbaroDigital final class UUID { // ... }

Slide 155

Slide 155 text

@GanbaroDigital function doSomething (UUID $id, ...) { // ... }

Slide 156

Slide 156 text

@GanbaroDigital

Slide 157

Slide 157 text

@GanbaroDigital interface UUID { // ... }

Slide 158

Slide 158 text

@GanbaroDigital class MysqlCombGUID extends UUID { // ... }

Slide 159

Slide 159 text

@GanbaroDigital class MysqlCombGUID extends UUID { // ... } ✗

Slide 160

Slide 160 text

@GanbaroDigital class MysqlCombGUID implements UUID { // ... }

Slide 161

Slide 161 text

@GanbaroDigital class SqlServerCombGUID implements UUID
 { // ... }

Slide 162

Slide 162 text

@GanbaroDigital Userland UUID variants would be fully supported.

Slide 163

Slide 163 text

@GanbaroDigital class MyID extends UUID { // ... }

Slide 164

Slide 164 text

@GanbaroDigital class MyID extends UUID { // ... } ✗

Slide 165

Slide 165 text

@GanbaroDigital class MyID { private $uuid; public function __toString( return (string)$this->uuid; ); }

Slide 166

Slide 166 text

@GanbaroDigital Abstractions should still be done via aggregation & the decorator pattern.

Slide 167

Slide 167 text

@GanbaroDigital Abstractions would benefit from some interfaces too!

Slide 168

Slide 168 text

@GanbaroDigital In Summary

Slide 169

Slide 169 text

@GanbaroDigital “ Use the decorator pattern for abstracting underlying behaviour.

Slide 170

Slide 170 text

@GanbaroDigital “ The behaviour of `final` classes can be consumed, not modified.

Slide 171

Slide 171 text

@GanbaroDigital “ `final` classes lock down type-hinted method parameters.

Slide 172

Slide 172 text

@GanbaroDigital “It’s unreasonable to expect package maintainers to know 100% of all possible variants.

Slide 173

Slide 173 text

@GanbaroDigital “ Use interface inheritance for new variants.

Slide 174

Slide 174 text

@GanbaroDigital “ `final` classes that don’t implement interfaces restrict package re-use.

Slide 175

Slide 175 text

@GanbaroDigital “ `final` classes that don’t implement interfaces are not SOLID.

Slide 176

Slide 176 text

@GanbaroDigital “ `final` classes that don’t implement interfaces are a code smell.

Slide 177

Slide 177 text

@GanbaroDigital “Don’t use `final` classes as type-hints. Use interfaces.

Slide 178

Slide 178 text

@GanbaroDigital

Slide 179

Slide 179 text

@GanbaroDigital “ Behaviour is closed to modification. The system is open for extension (via variants).

Slide 180

Slide 180 text

Thank You Any Questions? A presentation by @stuherbert
 for @GanbaroDigital