Be pragmatic, be SOLID
-
Upload
krzysztof-menzyk -
Category
Software
-
view
4.858 -
download
1
Transcript of Be pragmatic, be SOLID
![Page 1: Be pragmatic, be SOLID](https://reader033.fdocuments.in/reader033/viewer/2022051516/55a5257b1a28abea0e8b4688/html5/thumbnails/1.jpg)
Be pragmatic,be SOLID
![Page 2: Be pragmatic, be SOLID](https://reader033.fdocuments.in/reader033/viewer/2022051516/55a5257b1a28abea0e8b4688/html5/thumbnails/2.jpg)
Krzysztof Menżyk
practises TDDbelieves that software is a craft
loves domain modellingobsessed with brewing
plays squash
kmenzyk [email protected]
![Page 3: Be pragmatic, be SOLID](https://reader033.fdocuments.in/reader033/viewer/2022051516/55a5257b1a28abea0e8b4688/html5/thumbnails/3.jpg)
Do you consider yourselfa professional software developer?
![Page 4: Be pragmatic, be SOLID](https://reader033.fdocuments.in/reader033/viewer/2022051516/55a5257b1a28abea0e8b4688/html5/thumbnails/4.jpg)
New client
Greenfield project
Starting from scratch
![Page 5: Be pragmatic, be SOLID](https://reader033.fdocuments.in/reader033/viewer/2022051516/55a5257b1a28abea0e8b4688/html5/thumbnails/5.jpg)
![Page 6: Be pragmatic, be SOLID](https://reader033.fdocuments.in/reader033/viewer/2022051516/55a5257b1a28abea0e8b4688/html5/thumbnails/6.jpg)
![Page 7: Be pragmatic, be SOLID](https://reader033.fdocuments.in/reader033/viewer/2022051516/55a5257b1a28abea0e8b4688/html5/thumbnails/7.jpg)
What went wrong?
![Page 8: Be pragmatic, be SOLID](https://reader033.fdocuments.in/reader033/viewer/2022051516/55a5257b1a28abea0e8b4688/html5/thumbnails/8.jpg)
The code started to rot
![Page 9: Be pragmatic, be SOLID](https://reader033.fdocuments.in/reader033/viewer/2022051516/55a5257b1a28abea0e8b4688/html5/thumbnails/9.jpg)
The design is hard to change
Rigidity
![Page 10: Be pragmatic, be SOLID](https://reader033.fdocuments.in/reader033/viewer/2022051516/55a5257b1a28abea0e8b4688/html5/thumbnails/10.jpg)
The design is easy to break
Fragility
![Page 11: Be pragmatic, be SOLID](https://reader033.fdocuments.in/reader033/viewer/2022051516/55a5257b1a28abea0e8b4688/html5/thumbnails/11.jpg)
Immobility
The design is hard to reuse
![Page 12: Be pragmatic, be SOLID](https://reader033.fdocuments.in/reader033/viewer/2022051516/55a5257b1a28abea0e8b4688/html5/thumbnails/12.jpg)
It is easy to do the wrong thing, but hard to do the right thing
Viscosity
![Page 13: Be pragmatic, be SOLID](https://reader033.fdocuments.in/reader033/viewer/2022051516/55a5257b1a28abea0e8b4688/html5/thumbnails/13.jpg)
Your software is bound to change
![Page 14: Be pragmatic, be SOLID](https://reader033.fdocuments.in/reader033/viewer/2022051516/55a5257b1a28abea0e8b4688/html5/thumbnails/14.jpg)
Design stamina hypothesis
time
cumulativefunctionality
design payoff lineno design
good design
by Martin Fowler
![Page 15: Be pragmatic, be SOLID](https://reader033.fdocuments.in/reader033/viewer/2022051516/55a5257b1a28abea0e8b4688/html5/thumbnails/15.jpg)
![Page 16: Be pragmatic, be SOLID](https://reader033.fdocuments.in/reader033/viewer/2022051516/55a5257b1a28abea0e8b4688/html5/thumbnails/16.jpg)
What is Object Oriented Design
then?
![Page 17: Be pragmatic, be SOLID](https://reader033.fdocuments.in/reader033/viewer/2022051516/55a5257b1a28abea0e8b4688/html5/thumbnails/17.jpg)
Design Principles andDesign Patterns
Robert C. Martin
![Page 18: Be pragmatic, be SOLID](https://reader033.fdocuments.in/reader033/viewer/2022051516/55a5257b1a28abea0e8b4688/html5/thumbnails/18.jpg)
Single Responsibility
Open Closed
Liskov Substitution
Interface Segregation
Dependency Inversion
![Page 19: Be pragmatic, be SOLID](https://reader033.fdocuments.in/reader033/viewer/2022051516/55a5257b1a28abea0e8b4688/html5/thumbnails/19.jpg)
SingleResponsibilityPrinciple
A class should have only one reason to change
![Page 20: Be pragmatic, be SOLID](https://reader033.fdocuments.in/reader033/viewer/2022051516/55a5257b1a28abea0e8b4688/html5/thumbnails/20.jpg)
Gather together those things that change for the same reason
Separate those things that change for different reasons
![Page 21: Be pragmatic, be SOLID](https://reader033.fdocuments.in/reader033/viewer/2022051516/55a5257b1a28abea0e8b4688/html5/thumbnails/21.jpg)
class Employee{ public static function hire($name, $forPosition, Money $withSalary) { // ... }
public function promote($toNewPosition, Money $withNewSalary) { // ... }
public function asJson() { // ... }
public function save() { // ... }
public function delete() { // ... }}
![Page 22: Be pragmatic, be SOLID](https://reader033.fdocuments.in/reader033/viewer/2022051516/55a5257b1a28abea0e8b4688/html5/thumbnails/22.jpg)
Try to describe what the class does
![Page 23: Be pragmatic, be SOLID](https://reader033.fdocuments.in/reader033/viewer/2022051516/55a5257b1a28abea0e8b4688/html5/thumbnails/23.jpg)
class Employee{ public static function hire($name, $forPosition, Money $withSalary) { // ... }
public function promote($toNewPosition, Money $withNewSalary) { // ... }
public function asJson() { // ... }
public function save() { // ... }
public function delete() { // ... }}
![Page 24: Be pragmatic, be SOLID](https://reader033.fdocuments.in/reader033/viewer/2022051516/55a5257b1a28abea0e8b4688/html5/thumbnails/24.jpg)
class Employee{ public static function hire($name, $forPosition, Money $withSalary) { // ... }
public function promote($toNewPosition, Money $withNewSalary) { // ... }
public function asJson() { // ... }
public function save() { // ... }
public function delete() { // ... }}
![Page 25: Be pragmatic, be SOLID](https://reader033.fdocuments.in/reader033/viewer/2022051516/55a5257b1a28abea0e8b4688/html5/thumbnails/25.jpg)
class Employee{ public static function hire($name, $forPosition, Money $withSalary) { // ... }
public function promote($toNewPosition, Money $withNewSalary) { // ... }
public function asJson() { // ... }
public function save() { // ... }
public function delete() { // ... }}
![Page 26: Be pragmatic, be SOLID](https://reader033.fdocuments.in/reader033/viewer/2022051516/55a5257b1a28abea0e8b4688/html5/thumbnails/26.jpg)
![Page 27: Be pragmatic, be SOLID](https://reader033.fdocuments.in/reader033/viewer/2022051516/55a5257b1a28abea0e8b4688/html5/thumbnails/27.jpg)
class Employee{ public static function hire($name, $forPosition, Money $withSalary) { // ... }
public function promote($toNewPosition, Money $withNewSalary) { // ... }
public function asJson() { // ... }
public function save() { // ... }
public function delete() { // ... }} violation
![Page 28: Be pragmatic, be SOLID](https://reader033.fdocuments.in/reader033/viewer/2022051516/55a5257b1a28abea0e8b4688/html5/thumbnails/28.jpg)
class Employee { public static function hire($name, $forPosition, Money $withSalary) { // ... }
public function promote($toNewPosition, Money $withNewSalary) { // ... }}
class EmployeeSerializer{ public function toJson(Employee $employee) { // ... }}
class EmployeeRepository { public function save(Employee $employee) { // ... }
public function delete(Employee $employee) { // ... }}
the right
way
![Page 29: Be pragmatic, be SOLID](https://reader033.fdocuments.in/reader033/viewer/2022051516/55a5257b1a28abea0e8b4688/html5/thumbnails/29.jpg)
What about applying SRP to class methods?
![Page 30: Be pragmatic, be SOLID](https://reader033.fdocuments.in/reader033/viewer/2022051516/55a5257b1a28abea0e8b4688/html5/thumbnails/30.jpg)
What about applying SRP to test methods?
![Page 31: Be pragmatic, be SOLID](https://reader033.fdocuments.in/reader033/viewer/2022051516/55a5257b1a28abea0e8b4688/html5/thumbnails/31.jpg)
/** @test */public function test_employee(){ $employee = Employee::hire('John Doe', 'Junior Developer', $this->fiveHundredEuros);
$this->assertEquals($this->fiveHundredEuros, $employee->getSalary());
$employee->promote('Senior Developer', $this->sixHundredEuros);
$this->assertEquals($this->sixHundredEuros, $employee->getSalary());
$employee->promote('Technical Leader', $this->fiveHundredEuros);
$this->assertEquals($this->sixHundredEuros, $employee->getSalary());}
![Page 32: Be pragmatic, be SOLID](https://reader033.fdocuments.in/reader033/viewer/2022051516/55a5257b1a28abea0e8b4688/html5/thumbnails/32.jpg)
/** @test */public function it_hires_with_salary(){ $employee = Employee::hire('John Doe', 'Junior Developer', $this->fiveHundredEuros);
$this->assertEquals($this->fiveHundredEuros, $employee->getSalary());}
/** @test */public function it_promotes_with_new_salary(){ $employee = Employee::hire('John Doe', 'Junior Developer', $this->fiveHundredEuros); $employee->promote('Senior Developer', $this->sixHundredEuros);
$this->assertEquals($this->sixHundredEuros, $employee->getSalary());}
/** @test */public function it_does_not_promote_if_new_salary_is_not_bumped(){ $employee = Employee::hire('John Doe', 'Senior Developer', $this->sixHundredEuros); $employee->promote('Technical Leader', $this->fiveHundredEuros);
$this->assertEquals($this->sixHundredEuros, $employee->getSalary());}
the right
way
![Page 33: Be pragmatic, be SOLID](https://reader033.fdocuments.in/reader033/viewer/2022051516/55a5257b1a28abea0e8b4688/html5/thumbnails/33.jpg)
One test covers one behaviour
![Page 34: Be pragmatic, be SOLID](https://reader033.fdocuments.in/reader033/viewer/2022051516/55a5257b1a28abea0e8b4688/html5/thumbnails/34.jpg)
OpenClosedPrinciple
Software entities should be open for extension, but closed for modification
![Page 35: Be pragmatic, be SOLID](https://reader033.fdocuments.in/reader033/viewer/2022051516/55a5257b1a28abea0e8b4688/html5/thumbnails/35.jpg)
Write once, change never!
![Page 36: Be pragmatic, be SOLID](https://reader033.fdocuments.in/reader033/viewer/2022051516/55a5257b1a28abea0e8b4688/html5/thumbnails/36.jpg)
Wait! What?
![Page 37: Be pragmatic, be SOLID](https://reader033.fdocuments.in/reader033/viewer/2022051516/55a5257b1a28abea0e8b4688/html5/thumbnails/37.jpg)
class Shortener{ public function shorten(Url $longUrl) { if (!$this->hasHttpScheme($longUrl)) { throw new InvalidUrl('Url has no "http" scheme'); }
// do stuff to shorten valid url
return $shortenedUrl; }
private function hasHttpScheme(Url $longUrl) { // ... }}
/** @test */public function it_does_not_shorten_url_without_http(){ $urlToFtp = // ...
$this->setExpectedException(InvalidUrl::class, 'Url has no "http" scheme');
$this->shortener->shorten($urlToFtp);}
![Page 38: Be pragmatic, be SOLID](https://reader033.fdocuments.in/reader033/viewer/2022051516/55a5257b1a28abea0e8b4688/html5/thumbnails/38.jpg)
class Shortener{ public function shorten(Url $longUrl) { if (!$this->hasHttpScheme($longUrl)) { throw new InvalidUrl('Url has no "http" scheme'); }
if (!$this->hasPlDomain($longUrl)) { throw new InvalidUrl('Url has no .pl domain'); }
// do stuff to shorten valid url
return $shortenedUrl; }
private function hasHttpScheme(Url $longUrl) { // ... }
private function hasPlDomain(Url $longUrl) { // ... }}
![Page 39: Be pragmatic, be SOLID](https://reader033.fdocuments.in/reader033/viewer/2022051516/55a5257b1a28abea0e8b4688/html5/thumbnails/39.jpg)
/** @test */public function it_shortens_only_urls_with_pl_domains(){ $urlWithEuDomain = // ...
$this->setExpectedException(InvalidUrl::class, 'Url has no .pl domain');
$this->shortener->shorten($urlWithEuDomain);}
![Page 40: Be pragmatic, be SOLID](https://reader033.fdocuments.in/reader033/viewer/2022051516/55a5257b1a28abea0e8b4688/html5/thumbnails/40.jpg)
/** @test */public function it_shortens_only_urls_with_pl_domains(){ $urlWithEuDomainButWithHttpScheme = // ...
$this->setExpectedException(InvalidUrl::class, 'Url has no .pl domain');
$this->shortener->shorten($urlWithEuDomainButWithHttpScheme);}
![Page 41: Be pragmatic, be SOLID](https://reader033.fdocuments.in/reader033/viewer/2022051516/55a5257b1a28abea0e8b4688/html5/thumbnails/41.jpg)
/** @test */public function it_shortens_urls(){ $validUrl = // make sure the url satisfies all "ifs"
$shortenedUrl = $this->shortener->shorten($validUrl);
// assert}
![Page 42: Be pragmatic, be SOLID](https://reader033.fdocuments.in/reader033/viewer/2022051516/55a5257b1a28abea0e8b4688/html5/thumbnails/42.jpg)
Testing seems hard?
![Page 43: Be pragmatic, be SOLID](https://reader033.fdocuments.in/reader033/viewer/2022051516/55a5257b1a28abea0e8b4688/html5/thumbnails/43.jpg)
class Shortener{ public function shorten(Url $longUrl) { if (!$this->hasHttpScheme($longUrl)) { throw new InvalidUrl('Url has no "http" scheme'); }
if (!$this->hasPlDomain($longUrl)) { throw new InvalidUrl('Url has no .pl domain'); }
// do stuff to shorten valid url
return $shortenedUrl; }
private function hasHttpScheme(Url $longUrl) { // ... }
private function hasPlDomain(Url $longUrl) { // ... }} violation
![Page 44: Be pragmatic, be SOLID](https://reader033.fdocuments.in/reader033/viewer/2022051516/55a5257b1a28abea0e8b4688/html5/thumbnails/44.jpg)
Abstraction is the key
![Page 45: Be pragmatic, be SOLID](https://reader033.fdocuments.in/reader033/viewer/2022051516/55a5257b1a28abea0e8b4688/html5/thumbnails/45.jpg)
interface Rule{ /** * @param Url $url * * @return bool */ public function isSatisfiedBy(Url $url);}
![Page 46: Be pragmatic, be SOLID](https://reader033.fdocuments.in/reader033/viewer/2022051516/55a5257b1a28abea0e8b4688/html5/thumbnails/46.jpg)
class Shortener{ public function addRule(Rule $rule) { // ... }
public function shorten(Url $longUrl) { if (!$this->satisfiesAllRules($longUrl)) { throw new InvalidUrl(); }
// do stuff to shorten valid url
return $shortenedUrl; }
private function satisfiesAllRules(Url $longUrl) { // ... }}
the right
way
![Page 47: Be pragmatic, be SOLID](https://reader033.fdocuments.in/reader033/viewer/2022051516/55a5257b1a28abea0e8b4688/html5/thumbnails/47.jpg)
class HasHttp implements Rule{ public function isSatisfiedBy(Url $url) { // ... }}
class HasPlDomain implements Rule{ public function isSatisfiedBy(Url $url) { // ... }}
![Page 48: Be pragmatic, be SOLID](https://reader033.fdocuments.in/reader033/viewer/2022051516/55a5257b1a28abea0e8b4688/html5/thumbnails/48.jpg)
”There is a deep synergy between testability and good design”
– Michael Feathers
![Page 49: Be pragmatic, be SOLID](https://reader033.fdocuments.in/reader033/viewer/2022051516/55a5257b1a28abea0e8b4688/html5/thumbnails/49.jpg)
LiskovSubstitutionPrinciple
Subtypes must be substitutable for their base types
![Page 50: Be pragmatic, be SOLID](https://reader033.fdocuments.in/reader033/viewer/2022051516/55a5257b1a28abea0e8b4688/html5/thumbnails/50.jpg)
class Tweets{ protected $tweets = [];
public function add(Tweet $tweet) { $this->tweets[$tweet->id()] = $tweet; }
public function get($tweetId) { if (!isset($this->tweets[$tweetId])) { throw new TweetDoesNotExist(); }
return $this->tweets[$tweetId]; }}
![Page 51: Be pragmatic, be SOLID](https://reader033.fdocuments.in/reader033/viewer/2022051516/55a5257b1a28abea0e8b4688/html5/thumbnails/51.jpg)
class BoundedTweets extends Tweets{ const MAX = 10;
public function add(Tweet $tweet) { if (count($this->tweets) > self::MAX) { throw new \OverflowException(); }
parent::add($tweet); }}
![Page 52: Be pragmatic, be SOLID](https://reader033.fdocuments.in/reader033/viewer/2022051516/55a5257b1a28abea0e8b4688/html5/thumbnails/52.jpg)
function letsTweet(Tweets $tweets){ // ... // $tweets has already 10 tweets
$tweets->add(new Tweet('Ooooops'));}
![Page 53: Be pragmatic, be SOLID](https://reader033.fdocuments.in/reader033/viewer/2022051516/55a5257b1a28abea0e8b4688/html5/thumbnails/53.jpg)
function letsTweet(Tweets $tweets){ // ...
try { $tweets->add(new Tweet('Ooooops')); } catch (\OverflowException $e) { // What to do? }}
violation
![Page 54: Be pragmatic, be SOLID](https://reader033.fdocuments.in/reader033/viewer/2022051516/55a5257b1a28abea0e8b4688/html5/thumbnails/54.jpg)
Design by contract
![Page 55: Be pragmatic, be SOLID](https://reader033.fdocuments.in/reader033/viewer/2022051516/55a5257b1a28abea0e8b4688/html5/thumbnails/55.jpg)
Design by contract(because public API is not enough)
![Page 56: Be pragmatic, be SOLID](https://reader033.fdocuments.in/reader033/viewer/2022051516/55a5257b1a28abea0e8b4688/html5/thumbnails/56.jpg)
What does it expect?
What does it guarantee?
What does it maintain?
![Page 57: Be pragmatic, be SOLID](https://reader033.fdocuments.in/reader033/viewer/2022051516/55a5257b1a28abea0e8b4688/html5/thumbnails/57.jpg)
class Tweets{ public function add(Tweet $tweet) { // ... }
/** * @param $tweetId * * @return Tweet * * @throws TweetDoesNotExist If a tweet with the given id * has not been added yet */ public function get($tweetId) { // ... }}
![Page 58: Be pragmatic, be SOLID](https://reader033.fdocuments.in/reader033/viewer/2022051516/55a5257b1a28abea0e8b4688/html5/thumbnails/58.jpg)
interface Tweets{ public function add(Tweet $tweet);
/** * @param $tweetId * * @return Tweet * * @throws TweetDoesNotExist If a tweet with the given id * has not been added yet */ public function get($tweetId);}
class InMemoryTweets implements Tweets{ // ...}
![Page 59: Be pragmatic, be SOLID](https://reader033.fdocuments.in/reader033/viewer/2022051516/55a5257b1a28abea0e8b4688/html5/thumbnails/59.jpg)
public function retweet($tweetId){ try { $tweet = $this->tweets->get($tweetId); } catch (TweetDoesNotExist $e) { return; }
// ...}
![Page 60: Be pragmatic, be SOLID](https://reader033.fdocuments.in/reader033/viewer/2022051516/55a5257b1a28abea0e8b4688/html5/thumbnails/60.jpg)
class DoctrineORMTweets extends EntityRepository implements Tweets{ public function add(Tweet $tweet) { $this->_em->persist($tweet); $this->_em->flush(); }
public function get($tweetId) { return $this->find($tweetId); }}
![Page 61: Be pragmatic, be SOLID](https://reader033.fdocuments.in/reader033/viewer/2022051516/55a5257b1a28abea0e8b4688/html5/thumbnails/61.jpg)
class EntityRepository implements ObjectRepository, Selectable{ /** * Finds an entity by its primary key / identifier. * * @param mixed $id The identifier. * @param int $lockMode The lock mode. * @param int|null $lockVersion The lock version. * * @return object|null The entity instance * or NULL if the entity can not be found. */ public function find($id, $lockMode = LockMode::NONE, $lockVersion = null) { // ... }
// ...}
![Page 62: Be pragmatic, be SOLID](https://reader033.fdocuments.in/reader033/viewer/2022051516/55a5257b1a28abea0e8b4688/html5/thumbnails/62.jpg)
function retweet($tweetId){ if ($this->tweets instanceof DoctrineORMTweets) { $tweet = $this->tweets->get($tweetId);
if (null === $tweet) { return; } } else { try { $tweet = $this->tweets->get($tweetId); } catch (TweetDoesNotExist $e) { return; } }
// ...} violation
![Page 63: Be pragmatic, be SOLID](https://reader033.fdocuments.in/reader033/viewer/2022051516/55a5257b1a28abea0e8b4688/html5/thumbnails/63.jpg)
Violations of LSP arelatent violations of OCP
![Page 64: Be pragmatic, be SOLID](https://reader033.fdocuments.in/reader033/viewer/2022051516/55a5257b1a28abea0e8b4688/html5/thumbnails/64.jpg)
class DoctrineORMTweets extends EntityRepository implements Tweets{ // ...
public function get($tweetId) { $tweet = $this->find($tweetId);
if (null === $tweet) { throw new TweetDoesNotExist(); }
return $tweet; }}
the right
way
![Page 65: Be pragmatic, be SOLID](https://reader033.fdocuments.in/reader033/viewer/2022051516/55a5257b1a28abea0e8b4688/html5/thumbnails/65.jpg)
LSP violations are difficult to detect until it is too late
![Page 66: Be pragmatic, be SOLID](https://reader033.fdocuments.in/reader033/viewer/2022051516/55a5257b1a28abea0e8b4688/html5/thumbnails/66.jpg)
Ensure the expected behaviour of your base class is preserved in
derived classes
![Page 67: Be pragmatic, be SOLID](https://reader033.fdocuments.in/reader033/viewer/2022051516/55a5257b1a28abea0e8b4688/html5/thumbnails/67.jpg)
DependencyInversionPrinciple
High-level modules should not depend on low-level modules, both should depend on abstractions
Abstractions should not depend on details. Details should depend on abstractions
![Page 68: Be pragmatic, be SOLID](https://reader033.fdocuments.in/reader033/viewer/2022051516/55a5257b1a28abea0e8b4688/html5/thumbnails/68.jpg)
class PayForOrder { public function __construct(PayPalApi $payPalApi) { $this->payPalApi = $payPalApi; }
public function function pay(Order $order) { // ...
$token = $this->payPalApi->createMethodToken($order->creditCard()); $this->payPalApi->createTransaction($token, $order->amount());
// ... }}
![Page 69: Be pragmatic, be SOLID](https://reader033.fdocuments.in/reader033/viewer/2022051516/55a5257b1a28abea0e8b4688/html5/thumbnails/69.jpg)
PayForOrder
PayPalApi
Business Layer
Integration Layer
![Page 70: Be pragmatic, be SOLID](https://reader033.fdocuments.in/reader033/viewer/2022051516/55a5257b1a28abea0e8b4688/html5/thumbnails/70.jpg)
High-level module
Low-level module
PayForOrder
PayPalApi
![Page 71: Be pragmatic, be SOLID](https://reader033.fdocuments.in/reader033/viewer/2022051516/55a5257b1a28abea0e8b4688/html5/thumbnails/71.jpg)
Abstraction is the key
![Page 72: Be pragmatic, be SOLID](https://reader033.fdocuments.in/reader033/viewer/2022051516/55a5257b1a28abea0e8b4688/html5/thumbnails/72.jpg)
PaymentProvider
PayPalApi
PayForOrder
![Page 73: Be pragmatic, be SOLID](https://reader033.fdocuments.in/reader033/viewer/2022051516/55a5257b1a28abea0e8b4688/html5/thumbnails/73.jpg)
class PayForOrder{ public function __construct(PaymentProvider $paymentProvider) { $this->paymentProvider = $paymentProvider; }
public function function pay(Order $order) { // ...
$token = $this->paymentProvider->createMethodToken($order->creditCard()); $this->paymentProvider->createTransaction($token, $order->amount());
// ... }}
![Page 74: Be pragmatic, be SOLID](https://reader033.fdocuments.in/reader033/viewer/2022051516/55a5257b1a28abea0e8b4688/html5/thumbnails/74.jpg)
class PayForOrder{ public function __construct(PaymentProvider $paymentProvider) { $this->paymentProvider = $paymentProvider; }
public function function pay(Order $order) { // ...
$token = $this->paymentProvider->createMethodToken($order->creditCard()); $this->paymentProvider->createTransaction($token, $order->amount());
// ... }}
Abstractions should not depend on details. Details should depend on abstractions
![Page 75: Be pragmatic, be SOLID](https://reader033.fdocuments.in/reader033/viewer/2022051516/55a5257b1a28abea0e8b4688/html5/thumbnails/75.jpg)
Define the interface from the usage point of view
![Page 76: Be pragmatic, be SOLID](https://reader033.fdocuments.in/reader033/viewer/2022051516/55a5257b1a28abea0e8b4688/html5/thumbnails/76.jpg)
class PayForOrder{ public function __construct(PaymentProvider $paymentProvider) { $this->paymentProvider = $paymentProvider; }
public function function pay(Order $order) { // ...
$token = $this->paymentProvider->charge( $order->creditCard(), $order->amount() );
// ... }} the right
way
![Page 77: Be pragmatic, be SOLID](https://reader033.fdocuments.in/reader033/viewer/2022051516/55a5257b1a28abea0e8b4688/html5/thumbnails/77.jpg)
Concrete things change alot
Abstract things change muchless frequently
![Page 78: Be pragmatic, be SOLID](https://reader033.fdocuments.in/reader033/viewer/2022051516/55a5257b1a28abea0e8b4688/html5/thumbnails/78.jpg)
PaymentProvider
PayPalProvider
PayForOrder
PayPalApi
3rd party
![Page 79: Be pragmatic, be SOLID](https://reader033.fdocuments.in/reader033/viewer/2022051516/55a5257b1a28abea0e8b4688/html5/thumbnails/79.jpg)
class PayPalProvider extends PayPalApi implements PaymentProvider{ public function charge(CreditCard $creditCard, Money $forAmount) { $token = $this->createMethodToken($creditCard); $this->createTransaction($token, $forAmount); }}
![Page 80: Be pragmatic, be SOLID](https://reader033.fdocuments.in/reader033/viewer/2022051516/55a5257b1a28abea0e8b4688/html5/thumbnails/80.jpg)
PaymentProvider
PayPalProvider
PayForOrder
PayPalApi
3rd party
![Page 81: Be pragmatic, be SOLID](https://reader033.fdocuments.in/reader033/viewer/2022051516/55a5257b1a28abea0e8b4688/html5/thumbnails/81.jpg)
class PayPalProvider implements PaymentProvider{ public function __construct(PayPalApi $payPal) { $this->payPal = $payPal; }
public function charge(CreditCard $creditCard, Money $forAmount) { $token = $this->payPal->createMethodToken($creditCard); $this->payPal->createTransaction($token, $forAmount); }}
![Page 82: Be pragmatic, be SOLID](https://reader033.fdocuments.in/reader033/viewer/2022051516/55a5257b1a28abea0e8b4688/html5/thumbnails/82.jpg)
class TestPaymentProvider implements PaymentProvider { //... }
![Page 83: Be pragmatic, be SOLID](https://reader033.fdocuments.in/reader033/viewer/2022051516/55a5257b1a28abea0e8b4688/html5/thumbnails/83.jpg)
InterfaceSegregationPrinciple
Clients should not be forced to depend on methods they do not use
![Page 84: Be pragmatic, be SOLID](https://reader033.fdocuments.in/reader033/viewer/2022051516/55a5257b1a28abea0e8b4688/html5/thumbnails/84.jpg)
Many client specific interfaces are better than one general purpose
interface
![Page 85: Be pragmatic, be SOLID](https://reader033.fdocuments.in/reader033/viewer/2022051516/55a5257b1a28abea0e8b4688/html5/thumbnails/85.jpg)
interface EventDispatcherInterface{ public function dispatch($eventName, Event $event = null);
public function addListener($eventName, $listener, $priority = 0);
public function removeListener($eventName, $listener);
public function addSubscriber(EventSubscriberInterface $subscriber);
public function removeSubscriber(EventSubscriberInterface $subscriber);
public function getListeners($eventName = null);
public function hasListeners($eventName = null);}
![Page 86: Be pragmatic, be SOLID](https://reader033.fdocuments.in/reader033/viewer/2022051516/55a5257b1a28abea0e8b4688/html5/thumbnails/86.jpg)
class HttpKernel implements HttpKernelInterface, TerminableInterface{ public function terminate(Request $request, Response $response) { $this->dispatcher->dispatch( KernelEvents::TERMINATE, new PostResponseEvent($this, $request, $response) ); }
private function handleRaw(Request $request, $type = self::MASTER_REQUEST) { // ... $this->dispatcher->dispatch(KernelEvents::REQUEST, $event);
// ...
$this->dispatcher->dispatch(KernelEvents::CONTROLLER, $event);
// ... }
private function filterResponse(Response $response, Request $request, $type) { // ... $this->dispatcher->dispatch(KernelEvents::RESPONSE, $event); // ... }
// ...}
![Page 87: Be pragmatic, be SOLID](https://reader033.fdocuments.in/reader033/viewer/2022051516/55a5257b1a28abea0e8b4688/html5/thumbnails/87.jpg)
class ImmutableEventDispatcher implements EventDispatcherInterface{ public function dispatch($eventName, Event $event = null) { return $this->dispatcher->dispatch($eventName, $event); }
public function addListener($eventName, $listener, $priority = 0) { throw new \BadMethodCallException('Unmodifiable event dispatchers must not be modified.'); }
public function removeListener($eventName, $listener) { throw new \BadMethodCallException('Unmodifiable event dispatchers must not be modified.'); }
public function addSubscriber(EventSubscriberInterface $subscriber) { throw new \BadMethodCallException('Unmodifiable event dispatchers must not be modified.'); }
public function removeSubscriber(EventSubscriberInterface $subscriber) { throw new \BadMethodCallException('Unmodifiable event dispatchers must not be modified.'); }
// ...}
violation
![Page 88: Be pragmatic, be SOLID](https://reader033.fdocuments.in/reader033/viewer/2022051516/55a5257b1a28abea0e8b4688/html5/thumbnails/88.jpg)
It serves too many different types of clients
![Page 89: Be pragmatic, be SOLID](https://reader033.fdocuments.in/reader033/viewer/2022051516/55a5257b1a28abea0e8b4688/html5/thumbnails/89.jpg)
Design interfaces from clients point of view
![Page 90: Be pragmatic, be SOLID](https://reader033.fdocuments.in/reader033/viewer/2022051516/55a5257b1a28abea0e8b4688/html5/thumbnails/90.jpg)
interface EventDispatcherInterface{ public function dispatch($eventName, Event $event = null);}
interface EventListenersInterface{ public function addListener($eventName, $listener, $priority = 0); public function removeListener($eventName, $listener);}
interface EventSubscribersInterface{ public function addSubscriber(EventSubscriberInterface $subscriber); public function removeSubscriber(EventSubscriberInterface $subscriber);}
interface DebugEventListenersInterface{ public function getListeners($eventName = null); public function hasListeners($eventName = null);} the right
way
![Page 91: Be pragmatic, be SOLID](https://reader033.fdocuments.in/reader033/viewer/2022051516/55a5257b1a28abea0e8b4688/html5/thumbnails/91.jpg)
class EventDispatcher implements EventDispatcherInterface, EventListenersInterface, EventSubscribersInterface{ // ...}
class DebugEventDispatcher extends EventDispatcher implements DebugEventListenersInterface{ // ...}
![Page 92: Be pragmatic, be SOLID](https://reader033.fdocuments.in/reader033/viewer/2022051516/55a5257b1a28abea0e8b4688/html5/thumbnails/92.jpg)
Design is all about dependencies
![Page 93: Be pragmatic, be SOLID](https://reader033.fdocuments.in/reader033/viewer/2022051516/55a5257b1a28abea0e8b4688/html5/thumbnails/93.jpg)
Think about the design!
![Page 94: Be pragmatic, be SOLID](https://reader033.fdocuments.in/reader033/viewer/2022051516/55a5257b1a28abea0e8b4688/html5/thumbnails/94.jpg)
Listen to your tests
![Page 95: Be pragmatic, be SOLID](https://reader033.fdocuments.in/reader033/viewer/2022051516/55a5257b1a28abea0e8b4688/html5/thumbnails/95.jpg)
”Always leave the code a little better than you found it.”
![Page 96: Be pragmatic, be SOLID](https://reader033.fdocuments.in/reader033/viewer/2022051516/55a5257b1a28abea0e8b4688/html5/thumbnails/96.jpg)
But...
![Page 97: Be pragmatic, be SOLID](https://reader033.fdocuments.in/reader033/viewer/2022051516/55a5257b1a28abea0e8b4688/html5/thumbnails/97.jpg)
Be pragmatic
![Page 98: Be pragmatic, be SOLID](https://reader033.fdocuments.in/reader033/viewer/2022051516/55a5257b1a28abea0e8b4688/html5/thumbnails/98.jpg)
"By not considering the future of your code, you make your code
much more likely to be adaptable in the future."
![Page 99: Be pragmatic, be SOLID](https://reader033.fdocuments.in/reader033/viewer/2022051516/55a5257b1a28abea0e8b4688/html5/thumbnails/99.jpg)
At the end of the day what matters most is a business value
![Page 100: Be pragmatic, be SOLID](https://reader033.fdocuments.in/reader033/viewer/2022051516/55a5257b1a28abea0e8b4688/html5/thumbnails/100.jpg)
Know the rules well, so you can break them effectively
![Page 101: Be pragmatic, be SOLID](https://reader033.fdocuments.in/reader033/viewer/2022051516/55a5257b1a28abea0e8b4688/html5/thumbnails/101.jpg)
![Page 103: Be pragmatic, be SOLID](https://reader033.fdocuments.in/reader033/viewer/2022051516/55a5257b1a28abea0e8b4688/html5/thumbnails/103.jpg)
Worth reading
http://www.objectmentor.com/resources/articles/Principles_and_Patterns.pdf
http://butunclebob.com/ArticleS.UncleBob.PrinciplesOfOod
https://gilesey.wordpress.com/2013/09/01/single-responsibility-principle/
”Clean Code: A Handbook of Agile Software Craftsmanship” by Robert C. Martin
http://martinfowler.com/bliki/DesignStaminaHypothesis.html
![Page 104: Be pragmatic, be SOLID](https://reader033.fdocuments.in/reader033/viewer/2022051516/55a5257b1a28abea0e8b4688/html5/thumbnails/104.jpg)
Photo Creditshttps://flic.kr/p/5bTy6C
http://www.bonkersworld.net/building-software/
https://flic.kr/p/jzCox
https://flic.kr/p/n37EXH
https://flic.kr/p/9mcfh9
https://flic.kr/p/7XmGXp
http://my.csdn.net/uploads/201205/13/1336911356_6234.jpg
http://bit.ly/1cMgkPA
https://flic.kr/p/qQTMa
http://bit.ly/1EhyGEc
https://flic.kr/p/5PyErP
http://fc08.deviantart.net/fs49/i/2009/173/c/7/The_Best_Life_Style_by_Alteran_X.jpg
https://flic.kr/p/4Sw9pP
https://flic.kr/p/8RjbTS