Post on 18-May-2015
description
Don’t Be STUPIDGrasp SOLID!
Anthony Ferrara
Let’s TalkObject
OrientedProgramming
WhatIs An
Object?
Classic View
Object == Physical “Thing”
Classic View
Object == Physical “Thing”
Methods == Actions on “Thing”
Classic View
Object == Physical “Thing”
Methods == Actions on “Thing”
Properties == Description of “Thing”
Animal
MammalBird Fish
CatCow Dog
Lion Feline Cheetah
Classic View
$lion = new Lion;$lion->roar();$lion->walkTo($point);$lion->hunt($zebra);$lion->sleep();
Is This Realistic?
Classic View
$lion = new Lion;$lion->roar();$lion->walkTo($point);$lion->hunt($zebra);$lion->sleep();
(9 Months Later)
Classic View
$lion = new Lion;$lion->roar();$lion->walkTo($point);$lion->hunt($zebra);$lion->sleep();
Classic View
$lion = new Lion;$lion->roar();$lion->walkTo($point);$lion->hunt($zebra);$lion->sleep();
Does A Lion Have A Button To Make It Roar?
Classic View
$lion = new Lion;$lion->roar();$lion->walkTo($point);$lion->hunt($zebra);$lion->sleep();
Does A Lion Have A Button To Make It Roar?What Does It
Mean For An Object To “Hunt”?
Classic View
$lion = new Lion;$lion->roar();$lion->walkTo($point);$lion->hunt($zebra);$lion->sleep();
Does A Lion Have A Button To Make It Roar?What Does It
Mean For An Object To “Hunt”?
What Is A Lion In Relation To Our Application?
The Classical Model Is Easy To
Understand
The Classical Model Is
Completely Impractical
“Modern” View
Object == Collection Of (Related) Behaviors
“Modern” View
Object == Collection Of (Related) Behaviors
Methods == Behavior
“Modern” View
Object == Collection Of (Related) Behaviors
Methods == Behavior
Properties == Details Of Behavior
Classic View == “(conceptually) is a”
Modern View == “behaves as a”
interface Number {
function getValue(); function __toString();
function add(Number $n); function subtract(Number $n);
function equals(Number $n); function isLessThan(Number $n); function isGreaterThan(Number $n);
}
Number
IntegerFloat Decimal
longshort long long
unsigned signed
But Wait!
We Don’t Even Need Inheritance
All We Need IsPolymorphism
And Encapsulation
Polymorphism
Behavior Is Determined Dynamically
“Dynamic Dispatch”
Procedural Code
if ($a->isLong()) { return new Long($a->getValue() + 1);} elseif ($a->isFloat()) { return new Float($a->getValue() + 1.0);} elseif ($a->isDecimal()) { return new Decimal($a->getValue() + 1.0);
}
Polymorphic Code
return $number->add(new Integer(1));
Polymorphic Code
class Integer implements Number { public function add(Number $a) { return new Integer( $this->getValue() + (int) $a->getValue() ); }}
Polymorphic Code
class Float implements Number { public function add(Number $a) { return new Float( $this->getValue() + (float) $a->getValue() ); }}
Encapsulation
Behavior Is Completely Contained By The Object’s API
(Information Hiding)
Procedural Code
if (5 == $number->value) { print “Number Is 5”;} else { print “Number Is Not 5”;}
Encapsulated Code
if ($number->equals(new Integer(5))) { print “Number Is 5”;} else { print “Number Is Not 5”;}
Encapsulated Codeclass Decimal implements Number { protected $intValue; protected $exponent; public function equals(Number $a) { if ($a instanceof Decimal) { // Compare Directly } else { // Cast } }}
Behavior Is Defined By The
API
Two Types Of Primitive APIs
Interfaces (Explicit)
Two Types Of Primitive APIs
Interfaces (Explicit)
Duck Typing (Implicit)
If an Object Is A Collection Of Behaviors...
What Is A Collection Of
Classes/Objects?
APIs
Method
APIs
Method MethodMethod
Class
APIs
Method MethodMethod
Class ClassClass
Package
APIs
Method MethodMethod
Class ClassClass
Package PackagePackage
Library
APIs
Method MethodMethod
Class ClassClass
Package PackagePackage
Library
Framework
LibraryLibrary
What Makes A Good API?
A Good APIDoes One Thing
A Good APINever Changes
A Good APIBehaves Like Its
Contract
A Good APIHas A Narrow Responsibility
A Good APIDepends Upon Abstractions
And That’sSOLIDCode
S - Single Responsibility Principle O- L - I - D-
A Good API Does One
Thing
S - Single Responsibility Principle O- Open / Closed Principle
L - I - D-
A Good API Never Changes
S - Single Responsibility Principle O- Open / Closed Principle
L - Liskov Substitution Principle
I - D-
A Good API Behaves Like Its Contract
S - Single Responsibility Principle O- Open / Closed Principle
L - Liskov Substitution Principle
I - Interface Segregation Principle
D-
A Good API Has A Narrow Responsibility
S - Single Responsibility Principle O- Open / Closed Principle
L - Liskov Substitution Principle
I - Interface Segregation Principle
D- Dependency Inversion Principle
A Good API Depends Upon Abstractions
S - Single Responsibility Principle O- Open / Closed Principle
L - Liskov Substitution Principle
I - Interface Segregation Principle
D- Dependency Inversion Principle
Note That SOLID Does Not Dictate
What Is Good OOP
SOLID Emerges From Good OOP
So, What Makes A Bad API?
Global Variables(Spooky Action At A Distance)
Depending OnSpecifics Of An Implementation
Hidden Dependencies
Unhealthy Focus On Performance
Poorly Named APIs
Duplication
And That’sSTUPID
Code
S - Singletons
T -U -P -I -D-
Global Variables(Spooky ActionAt A Distance)
S - Singletons
T - Tight Coupling
U -P -I -D-
Depending On Specifics Of An
Implementation
S - Singletons
T - Tight Coupling
U - Untestable Code
P -I -D-
Hidden Dependencies
S - Singletons
T - Tight Coupling
U - Untestable Code
P - Premature Optimization
I -D-
Unhealthy Focus On Performance
S - Singletons
T - Tight Coupling
U - Untestable Code
P - Premature Optimization
I - Indescriptive Naming
D-
Poorly Named APIs
S - Singletons
T - Tight Coupling
U - Untestable Code
P - Premature Optimization
I - Indescriptive Naming
D- Duplication
Duplication
Duplication
DuplicationDuplication
Duplication
DuplicationDuplication
Duplication
S - Singletons
T - Tight Coupling
U - Untestable Code
P - Premature Optimization
I - Indescriptive Naming
D- Duplication
STUPID Embodies
Lessons Learned From Bad OOP
What Good OOP Gives You
Modular CodeReusable CodeExtendable CodeEasy To Read CodeMaintainable CodeEasy To Change CodeEasy To Understand CodeClean Abstractions (mostly)
What Good OOP Costs You
Tends To Have More “Layers”Tends To Be Slower At RuntimeTends To Have Larger CodebasesTends To Result In Over-AbstractionTends To Require More Effort To WriteTends To Require More Tacit Knowledge
Let’s Look At Some Code!
interface Car {
public function turnLeft(); public function turnRight(); public function goFaster(); public function goSlower(); public function shiftUp(); public function shiftDown(); public function start();
}
interface Steerable { public function steer($angle);}interface Acceleratable { public function accelerate($amt);}interface Shiftable { public function shiftDown(); public function shiftUp();}
Let’s Look At Drupal Code!
interface MailSystemInterface {
public function format(array $message);
public function mail(array $message);
}
interface MailSystemInterface {
public function format(array $message);
public function mail(array $message);
}
What Responsibility?
interface MailSystemInterface {
public function format(array $message);
public function mail(array $message);
}
What Responsibility?
Formatting Messages
interface MailSystemInterface {
public function format(array $message);
public function mail(array $message);
}
What Responsibility?
Formatting Messages
Encoding Messages
interface MailSystemInterface {
public function format(array $message);
public function mail(array $message);
}
What Responsibility?
Formatting Messages
Encoding Messages
Assembling Headers
interface MailSystemInterface {
public function format(array $message);
public function mail(array $message);
}
What Responsibility?
Formatting Messages
Encoding Messages
Assembling Headers
Calling Sendmail
interface MailSystemInterface {
public function format(array $message);
public function mail(array $message);
}
What Responsibility?
Formatting Messages
Encoding Messages
Assembling Headers
Calling Sendmail
Setting INI settings…?
interface MailSystemInterface {
public function format(array $message);
public function mail(array $message);
}
What Responsibility?
Open For Extension?
interface MailSystemInterface {
public function format(array $message);
public function mail(array $message);
}
What Responsibility?
Open For Extension?
Edits Require Copy/Paste
interface MailSystemInterface {
public function format(array $message);
public function mail(array $message);
}
What Responsibility?
Open For Extension?
Liskov Substitution...
interface MailSystemInterface {
public function format(array $message);
public function mail(array $message);
}
What Responsibility?
Open For Extension?
Liskov Substitution...
One Interface...
Many Responsibilites
interface MailSystemInterface {
public function format(array $message);
public function mail(array $message);
}
What Responsibility?
Open For Extension?
Liskov Substitution...
One Interface...
What Dependencies?
interface MessageFormatter { public function format(Message $message);
}interface MessageEncoder { public function encode(Message $message);
}interface MessageTransport { public function send(Message $message);
}
class MailSystem { public function __construct( MessageFormatter $messageFormatter, MessageEncoder $messageEncoder, MessageTransport $messageTransport
) {} public function mail(Message $message);
}
Principle Of
GoodEnough
Know the rules wellso you can break them
effectively...Dalai Lama XIV
Anthony Ferrara@ircmaxell
blog.ircmaxell.comme@ircmaxell.comyoutube.com/ircmaxell