Tuesday, June 8, 2010

SOLID Principles of Object-Oriented System Design

6/7/2010
Instructor: Robert Martin

a discussion on determining the size of the galaxy using globular clusters and variable stars

a discussion on programming languages
C > Java > ruby > functional languages (f#, scala, closure)
functional languages: scalable/multiple threads: moore's law has died
no locking problems

why are all our languages OO?
What can OO do that C cant?

OO is a way to manage dependencies between pieces of source code

polymorphism allows us to invert the dependency of modules
you can do this in C with function pointers, but it's dangerous

Structured Programming - Djikstra "goto may be considered harmful"

discipline applied to direct forms of control - structured
discipline applied to indirect forms of control - OO

What causes rotten software? Complexity
Fragility: breaks easily, can't anticipate consequences of a change
Rigidity: Every change forces a host of other changes
Reusability: desirable things that a module does cant be untangled from the undesirable things. It's too easy to make dependencies on things we don't need
All these things are brought about by bad dependencies

Case study: copy routine
copy characters from the keyboard to the printer
copy depends on keyboard/printer

flow of control flows with dependencies:
void copy() {
int c;
while((c=kbd()!=EOF) print(c);
}

later... add support for other input/output devices

flow of control goes opposite of dependencies:
void copy() {
int c;
while(c=getchar()!=eof) putchar(c);
}

getchar/putchar uses stdin/stdout by default
it can be maintained because "the code can already do that"

Single Responsibility Principle
A class should only have 1 reason to change
Group together things that change for the same reasons
Separate things that change for different reasons

Open/Closed Principle
Add new functionality by adding new code, not editing old code
A module should be open for extension but closed for modification

A discussion on measuring the distance to the sun

What do you do when your abstractions get in the way?
the wrong abstractions cannot protect you from unanticipated changes

Agility can be a strategy to protect you: put in almost no abstractions at first
Show code to customer as early as possible and reactively add abstractions

This is a strategy but it is not perfect

One of the keys to making OO software work is TDD
Nothing makes a system more flexible than a suite of tests (by an order of magnitude)
If you have a bad design, and a suite of tests, you are not afraid to improve the design
Therefore tests are more important than a good design

QA should be writing acceptance tests at a high level (at the frontend of the dev process)
Developers should be writing unit tests at a lower level as development progresses

Liskov Substitution Principle
All derived classes must be substitutable for their base classes
Every substitution violation eventually leads to an open/closed violation

Inheritance does not mean "is a"
Inheritance means the variables/functions of the base class are redefined in the inherited class

The fact that a square is a rectangle does not mean that the square inherits from a rectangle

Taking behavior away from the base class is the core problem. The user has a right to expect that behavior.

Dependency Inversion Principle
OO programs are programs where the flow of control is opposed by the source code dependencies

OO programs protect you from the creation of new derivative types
When you add new functions to the hierarchy, the user is not protected
When you add new data structures/types to the hierarchy, the user is protected

Procedural programs protect you from the creation of new functions
When you add new functions to the hierarchy, the user is not protected
When you add new data structures/types to the hierarchy, the user is protected

Dependency Inversion principle:
If A depends on B: B should be abstract
Java: interfaces
C++: pure virtual functions
Can you really do that all the time? no. (for example can't "new" an abstract class.)

Discussion of smalltalk vs C++ re:type-safety
TDD can replace type-safety (dont need the compiler to check, the tests can check)
This led to Ruby/Python

Whats bad about overriding a function that has an implementation?
Should be adding functionality, not taking away
If you depend on a derived class, you depend on everything the base class depends on
Inheritance is a very strong relationship: use with care

But inheritance is what gives us polymorphism, which allows this dependency inversion
This is why you should inherit from abstract classes to reduce the dependencies

Other languages (python/ruby) can do polymorphism without inheritance. This makes python/ruby code much easier to write.

A definition of a programmer is someone who is constantly learning. Every programmer should learn a new language every year just on principle.

This class is actually principles for statically typed OO languages (java/C++). The same principles apply in other languages, but in different ways.

"All solutions are short term"

Thinks Ruby is the next language to crest, then closure

Discussion on where gold comes from? Supernovas

Interface Segregation Principle
A fat class is a class with many methods and many users. A new user comes along and wants to add a new function to the fat class. All the other users get the new function too, so you have a massive recompilation and deployment.
Find groupings of methods that are only used by certain users. Create abstract interface clsses for each grouping. Have the fat class multiply inherit from the abstract interface classes.

Design is about manipulating code: if you can't envision the code you are not doing design

Comments are lies: they are far away from the things that they describe
Comments: only write them if you have to. Comments are a failure to make yourself clear in the code.

The worst kind of comment is commented-out code. The old code is in the VCS, if you need it you can go get it.

Functions should be max 4-5 lines long

Journalists work with the rule that the most important stuff should be at the top. Each line is more and more detailed. Code should be organized the same way so that it is easy to read/understand.

How many arguments should be passed into a function? Zero is best. Three is getting tough. If you are passing 5 things, you should probably be passing an object.

The worst thing you can pass into a function is a bool: you are announcing that the function does two things. Split it into two functions

Catching output in arguments is also bad, forces reader to do a doubletake

1 comment:

  1. "QA should be writing acceptance tests at a high level (at the frontend of the dev process). Developers should be writing unit tests at a lower level as development progresses."

    Amen

    "A definition of a programmer is someone who is constantly learning. Every programmer should learn a new language every year just on principle."

    Amen x 2

    "The worst kind of comment is commented-out code. The old code is in the VCS, if you need it you can go get it."

    Amen x 3 million

    ReplyDelete