architecture logicielle #3 : object oriented design
TRANSCRIPT
Architecture logicielle : Object-oriented design
0. Object 101
Object
In computer science, an object is a location in memory having a value and possibly referenced by an identifier. An object can be a variable, a data structure, or a function.Source : http://en.wikipedia.org
Object-oriented programming
Object-Oriented programming is an approach to designing modular reusable software systems. The object-oriented approach is fundamentally a modelling approach.Source : http://en.wikipedia.org
Class
In object-oriented programming, a class is an extensible program-code-template for creating objects, providing initial values for state and implementations of behavior.Source : http://en.wikipedia.org
Class - PHP exemple
class Character {
private $firstName; private $lastName;
public function __construct($firstName, $lastName) { $this->firstName = $firstName; $this->lastName = $lastName; } }
Instance
In object-oriented programming (OOP), an instance is a specific realization of any object. The creation of a realized instance is called instantiation.Source : http://en.wikipedia.org
Instance - PHP exemple
$nedStark = new Character('Eddard', 'Stark');
$robertBaratheon = new Character('Robert', 'Baratheon');
Attribute & method
A class contains data field descriptions (or properties, fields, data members, or attributes).Source : http://en.wikipedia.org
A method in object-oriented programming (OOP) is a procedure associated with an object class.Source : http://en.wikipedia.org
Attribute & method - PHP exempleclass Character {
private $firstName; private $lastName; private $nickname;
public function __construct($firstName, $lastName, $nickname) { $this->firstName = $firstName; $this->lastName = $lastName; $this->nickname = $nickname; }
public function getFullName(){ return $this->firstName . ' ' . $this->lastName; }
public function getNickname(){ return $this->nickname; } }
$nedStark = new Character('Eddard', 'Stark', 'Ned'); echo $nedStark->getFullName(); // Eddard Stark echo $nedStark->getNickname(); // Ned
Interface
In object-oriented languages, the term interface is often used to define an abstract type that contains no data or code, but defines behaviors as method signatures. A class having code and data for all the methods corresponding to that interface is said to implement that interface.Source : http://en.wikipedia.org
Interface - PHP exempleinterface CharacterInterface { public function getFullName(); }
class Human implements CharacterInterface {
private $firstName; private $lastName;
public function __construct($firstName, $lastName) {…}
public function getFullName(){ return $this->firstName . ' ' . $this->lastName; } }
class Animal implements CharacterInterface {
private $name;
public function __construct($name) {…}
public function getFullName(){ return $this->name; } }
$nedStark = new Human('Eddard', 'Stark', 'Ned'); $nymeria = new Animal('Nymeria'); echo $nedStark->getFullName(); // Eddard Stark echo $nymeria->getFullName(); // Nymeria
Inheritance
In object-oriented programming (OOP), inheritance is when an object or class is based on another object or class, using the same implementation (inheriting from a class) specifying implementation to maintain the same behavior.Source : http://en.wikipedia.org
Inheritance - PHP exempleclass Human {
private $firstName; private $lastName;
public function __construct($firstName, $lastName) { $this->firstName = $firstName; $this->lastName = $lastName; }
public function getFullName(){ return $this->firstName . ' ' . $this->lastName; } }
class King extends Human {
private $reignStart; private $reignEnd;
public function setReign($reignStart, $reignEnd){ $this->reignStart = $reignStart; $this->reignEnd = $reignEnd; }
public function getReign(){ return $this->reignStart . " - " . $this->reignEnd; } } $robertBaratheon = new King('Robert', 'Baratheon'); $robertBaratheon->setReign(283, 298); echo $robertBaratheon->getFullName() . ' ( '.$robertBaratheon->getReign().' )'; // Robert Baratheon ( 283 - 298 )
2. Are You Stupid?
STUPID code, seriously?
Singleton
Tight Coupling
Untestability
Premature Optimization
Indescriptive Naming
Duplication
2.1 Singleton
Singleton syndrome
class Database { const DB_DSN = 'mysql:host=localhost;port=3306;dbname=westeros'; const DB_USER = 'root'; const DB_PWD = 'root';
private $dbh; static private $instance;
private function connect() { if ($this->dbh instanceOf PDO) {return;}
$this->dbh = new PDO(self::DB_DSN, self::DB_USER, self::DB_PWD); }
public function query($sql) { $this->connect(); return $this->dbh->query($sql); }
public static function getInstance() { if (null !== self::$instance) { return self::$instance; }
self::$instance = new self(); return self::$instance; } }
Configuration difficile
singletons ~ variables globales
Couplages forts
Difficile à tester
2.2 Tight Coupling
Coupling ?
In software engineering, coupling is the manner and degree of interdependence between software modules; a measure of how closely connected two routines or modules are; the strength of the relationships between modules.Source : http://en.wikipedia.org
Et en claire ?
If making a change in one module in your application requires you to change another
module, then coupling exists.
Exemple de couplage fortclass Location { private $name; private $type; }
class Character {
private $firstName; private $lastName; private $location;
public function __construct($firstName, $lastName, $locationName, $locationType) { $this->firstName = $firstName; $this->lastName = $lastName; $this->location = new Location($locationName, $locationType); }
public function getFullName(){ return $this->firstName . ' ' . $this->lastName; } }
$nedStark = new Character('Eddard', 'Stark', 'Winterfell', 'Castle');
Exemple de couplage faibleclass Location { private $name; private $type; }
class Character {
private $firstName; private $lastName; private $location;
public function __construct($firstName, $lastName, $location) { $this->firstName = $firstName; $this->lastName = $lastName; $this->location = $location; }
public function getFullName(){ return $this->firstName . ' ' . $this->lastName; } }
$winterfell = new Location($locationName, $locationType); $nedStark = new Character('Eddard', 'Stark', $winterfell);
2.3 Untestability
Testing ?
In my opinion, testing should not be hard! No, really. Whenever you don't write unit tests because you don't have time, the real issue is that your code is bad, but that is another story.
Source : http://williamdurand.fr
Untested and Untestable
2.4 Premature Optimization
« Premature optimization is the root of all evil. »
Donald Knuth
« If it doesn't work, it doesn't matter how fast it
doesn't work. »Mich Ravera
2.5 Indescriptive Naming
Give all entities mentioned in the code (DB tables, DB tables’ fields, variables, classes, functions, etc.) meaningful, descriptive names that make the code easily understood. The names should be so self-explanatory that it eliminates the need for comments in most cases.Source : Michael Zuskin
Self-explanatory
Exemple : bad names
« If you can't find a decent name for a class or a method, something is
probably wrong »William Durand
class Char {
private $fn; private $ln;
public function __construct($fn, $ln) { $this->fn = $fn; $this->ln = $ln; }
public function getFnLn(){ return $this->fn . ' ' . $this->ln; } }
Exemple : abbreviations
2.6 Duplication
« Write Everything Twice »
WET ?
« We Enjoy Typing »
Copy-and-paste programming is the production of highly repetitive computer programming code, as by copy and paste operations. It is primarily a pejorative term; those who use the term are often implying a lack of programming competence.Source : http://en.wikipedia.org
Copy And Paste Programming
« Every piece of knowledge must have a single, unambiguous, authoritative representation within a system. »
Don’t Repeat Yourself
Andy Hunt & Dave Thomas
The KISS principle states that most systems work best if they are kept simple rather than made complicated; therefore simplicity should be a key goal in design and unnecessary complexity should be avoided.
Keep it simple, stupid
Source : http://en.wikipedia.org
3. Nope, Im solid !
SOLID code ?
Single Responsibility Principle
Open/Closed Principle
Liskov Substitution Principle
Interface Segregation Principle
Dependency Inversion Principle
3.1 Single Responsibility Principle
Single Responsibility
A class should have one, and only one, reason to change.
Robert C. Martin
The problemclass DataImporter { public function import($file) { $records = $this->loadFile($file); $this->importData($records); }
private function loadFile($file) { $records = array(); // transform CSV in $records return $records; }
private function importData(array $records) { // insert records in DB } }
The solutionclass DataImporter {
private $loader; private $importer;
public function __construct($loader, $gateway) { $this->loader = $loader; $this->gateway = $gateway; }
public function import($file) { $records = $this->loader->load($file);
foreach ($records as $record) { $this->gateway->insert($record); } } }
Kill the god class !
A "God Class" is an object that controls way too many other objects in the system and has grown beyond all logic to become The Class That
Does Everything.
3.2 Open/Closed Principle
Open/Closed
Objects or entities should be open for extension, but closed
for modification.Robert C. Martin
The problem
class Circle { public $radius;
// Constructor function }
class Square { public $length;
// Constructor function }
class AreaCalculator {
protected $shapes;
// Constructor function
public function sum() { foreach($this->shapes as $shape) { if(is_a($shape, 'Square')) { $area[] = pow($shape->length, 2); } else if(is_a($shape, 'Circle')) { $area[] = pi() * pow($shape->radius, 2); } }
return array_sum($area); } }
$shapes = array( new Circle(2), new Square(5), new Square(6) );
$areas = new AreaCalculator($shapes); echo $areas->sum();
Source : https://scotch.io
The solution (1)class Circle { public $radius;
// Constructor function
public function area() { return pi() * pow($this->radius, 2); } }
class Square { public $length;
// Constructor function
public function area() { return pow($this->length, 2); } }
class AreaCalculator {
protected $shapes;
// Constructor function
public function sum() { foreach($this->shapes as $shape) { $area[] = $shape->area(); } return array_sum($area); } }
$shapes = array( new Circle(2), new Square(5), new Square(6) );
$areas = new AreaCalculator($shapes); echo $areas->sum();
Source : https://scotch.io
The solution (2)class AreaCalculator {
protected $shapes;
// Constructor function
public function sum() { foreach($this->shapes as $shape) { $area[] = $shape->area(); } return array_sum($area); } }
$shapes = array( new Circle(2), new Square(5), new Square(6) );
$areas = new AreaCalculator($shapes); echo $areas->sum();
interface ShapeInterface { public function area(); }
class Circle implements ShapeInterface { public $radius;
// Constructor function
public function area() { return pi() * pow($this->radius, 2); } }
class Square implements ShapeInterface { public $length;
// Constructor function
public function area() { return pow($this->length, 2); } }
Source : https://scotch.io
3.3 Liskov Substitution Principle
Liskov Substitution
Derived classes must be substitutable for their
base classes.Robert C. Martin
Exemple
abstract class AbstractLoader implements FileLoader { public function load($file) { if (!file_exists($file)) { throw new \InvalidArgumentException(sprintf('%s does not exist.', $file)); } return []; } }
class CsvFileLoader extends AbstractLoader { public function load($file) { $records = parent::load($file); // Get records from file return $records; } }
Source : http://afsy.fr
3.4 Interface Segregation Principle
Interface Segregation
A client should never be forced to implement an interface that it doesn’t use or clients shouldn’t be forced to depend on methods they do not use.
Robert C. Martin
Exemple
interface UrlGeneratorInterface { public function generate($name, $parameters = array()); }
interface UrlMatcherInterface { public function match($pathinfo); }
interface RouterInterface extends UrlMatcherInterface, UrlGeneratorInterface { public function getRouteCollection(); }
Source : http://afsy.fr
3.5 Dependency Inversion Principle
Dependency Inversion
Entities must depend on abstractions not on concretions. It states that the high level module must not depend on the
low level module, but they should depend on abstractions.
Robert C. Martin
The problem
class DataImporter { private $loader; private $gateway;
public function __construct(CsvFileLoader $loader, DataGateway $gateway) { $this->loader = $loader; $this->gateway = $gateway; } }
Source : http://afsy.fr
Classes
The solution
Source : http://afsy.fr
class DataImporter { private $loader; private $gateway;
public function __construct(FileLoader $loader, Gateway $gateway) { $this->loader = $loader; $this->gateway = $gateway; } }
Interfaces
To be continued …