What is this guy even talking about? Object Oriented Design is the pinnacle of software engineering and nothing better will be invented!
He is one of these pesky functional programmers who want to sell us their snake oil!
That's true I prefer functional approach but this time I won't be preaching about it. After years of working with and reading through object oriented codebases I just started to see the cracks.
In this talk we will look at the "rules" for object oriented design. Why the focus on the main idea of encapsulation and combining functions and data can lead to problems. We will see that no one really writes truly object oriented code and probably for the better.
10. Just some state and behaviours?
What is an object?
Object
State
Behaviour
State State
Behaviour
Behaviour
11. Private information hidden behind a public
interface
What is an object?
Object
Message
Message
Message Message
12. Message passing, or is it? How do we pass the
messages? Most importantly what do we pass?
What is an object?
Object Object
Object Object
Object
Object
Object
13. Or is it a bad practice?
Should messages pass references?
class Foo(val value: Int) {
fun doSomething(bar: Bar) { //... }
}
class Bar(val value: String) {
fun doElse(baz: Baz) { //... }
}
class Baz(val value: String) {
fun doDifferent(foo: Foo) { //... }
}
val foo = Foo(value = 2023)
val bar = Bar(value = "DevDay")
val baz = Baz(value = "TomTom")
foo.doSomething(bar) //if we pass the references
bar.doElse(baz) //and they become the internal
//state
baz.doDifferent(foo) //then who owns them?
14. Object should be solely responsible for its internal state?
Should objects share references?
class Foo(
val bar: Bar,
val baz: Baz,
)
class Bar(val value: String)
class Baz(val value: String)
val bar = Bar(value = "DevDay")
val baz = Baz(value = "TomTom")
val foo = Foo(bar, baz)
val qux = Qux(bar, baz)
//both Bar and Baz instances are part of the internal
//state of Foo and Qux, so which one of these two
//has full responsibility of its encapsulated state?
class Qux(
val bar: Bar,
val baz: Baz,
)
15. But with some extra steps!
Isn't this just shared state?
Bar
Baz
Foo Qux
If Baz and Bar are part of Foo's internal state...
If Baz and Bar are part of Qux's internal state...
Does it mean that Qux can affect Foo's state?
Does it mean that Foo can affect Qux's state?
16. Then objects should not be shared and have only a single parent?
If encapsulation is about hiding...
A B
D F
G
C
E
A
B C
D
E F
G
Let's change a free
form graph to a tree!
17. Then what should we do if we need to communication between objects?
If everything only has a single parent...
A
B C
D
E F
G
A
B C
D
E F
G
19. If you squint your eyes isn't this the same as with any taxonomy?
Maybe we need to change something...
A
B C
D
E F
G
A
B
D
E F H
G C
New Object
Maybe soon will not be
needed?
New root object
How many times we will have to do it?
21. Yes, yes it can!
Can encapsulation
be premature?
22. Imposing structure is worse than no structure at all!
Think about data and processing separately!
Class
hierarchy
Single
responsibility
Composition
graph
Objects
data flow
Object
transformation
Temporal
dependencies
Design
patterns
Behaviors
Module
relationship
23. Nothing special just a simple association
Let's play a game
State
State
State
State
State
State
State
Behavior Behavior
Behavior
Behavior Behavior
Behavior
Behavior Behavior
What to do with leftovers?
27. 1. Write methods when the association with the data type is certain. It is easy to get it
wrong!
2. Minimize state instead of only segregating it.
3. Prefer immutability for the shared state.
4. When all hope is lost parametrize the behavior.
5. Polymorphism is not limited to only classes!
6. Prefer pure functions, if not constrained by the efficiency.
7. Encapsulate on module/namespace level.
8. It is ok to sometimes repeat or have redundant pieces. Long functions are not wrong.
9. Limit surface layer. It is much easier to comprehend only a few functions comparing to
thousands of small ones.
So how should we design our code?