14. SOLID PRINCIPLES
SINGLE RESPONSIBILITY PRINCIPLE VIOLATIONS
▸ Many instance variables
▸ Many public methods
▸ Each method use variables from other instance
▸ Specific tasks are done by private methods
19. SOLID PRINCIPLES
SINGLE RESPONSIBILITY PRINCIPLE ADVANTAGES
▸ Smaller classes
▸ Classes easier to understand
▸ Classes easier to test
▸ Classes simpler to maintain
25. OPEN/CLOSED PRINCIPLE
“A UNIT OF CODE CAN BE
CONSIDERED ‘OPEN’ FOR EXTENSION
WHEN ITS BEHAVIOR CAN BE EASILY
CHANGED WITHOUT MODIFYING IT”
SOLID PRINCIPLES
26. SOLID PRINCIPLES
OPEN/CLOSED PRINCIPLE VIOLATIONS
▸ Conditions to determine a strategy
▸ Conditions using the same variables or constants are
recurring inside the class or related classes
▸ Contains hard-coded references to other classes or class
names
▸ Objects are being created using the new operator
34. <?php
interface EncoderFactoryInterface
{
/**
* Create an encoder for the given format
*
* @param string $format
* @return EncoderInterface
*/
public function createForFormat($format)
}
class EncoderFactory implements EncoderFactoryInterface
{
...
}
class GenericEncoder
{
public function __construct(
EncoderFactoryInterface $encoderFactory
) {
...
}
...
}
51. <?php
/**
* A concrete class, all methods are implemented, but can be
* overridden by derived classes
*/
class ConcreteClass
{
public function implementedMethod()
{
}
}
/**
* An abstract class, some methods need to be implemented by
* by derived classes
*/
abstract class AbstractClass
{
abstract public function abstractMethod();
public function implementedMethod()
{
}
}
/**
* An interface, all methods need to be implemented by derived
* classes
*/
interface AnInterface
{
public function abstractMethod();
}
56. <?php
class DropboxFile implements FileInterface
{
public function rename($name)
{
...
}
public function changeOwner($user, $group)
{
throw new BadMethodCallException(
"Not implemented for Dropbox files"
);
}
}
57. <?php
class DropboxFile implements FileInterface
{
public function rename($name)
{
...
}
public function changeOwner($user, $group)
{
throw new BadMethodCallException(
"Not implemented for Dropbox files"
);
}
}
//ask the user to check if $file is an instance of DropboxFile
if (!($file instanceof DropboxFile)) {
$file->changeOwner(...);
}
62. <?php
class DropboxFile implements FileInterface
{
public function rename($name)
{
...
}
}
class LocalFile implements FileWithOwnerInterface
{
public function rename($name)
{
...
}
public function changeOwner($user, $group)
{
...
}
}
63. SOLID PRINCIPLES
NEW HIERARCHY OF FILE CLASSES
DropboxFile.php
extends
LocalFile.php
implements
FileInterface.php
implements
FileWithOwnerInterface.php
70. <?php
class SmtpMassMailer implements MassMailerInterface
{
public function sendMail(
TransportInterface $transport,
MessageInterface $message,
RecipientsInterface $recipients
) {
if (!($transport instanceof SmtpTransport)) {
throw new InvalidArgumentException(
'SmtpMassMailer only works with SMTP'
);
}
}
}
74. <?php
class SmtpMassMailer implements MassMailerInterface
{
public function sendMail(
TransportWithMassMailSupportInterface $transport,
MessageInterface $message,
RecipientsInterface $recipients
) {
…
}
}
75. SOLID PRINCIPLES
A GOOD SUBSTITUTE
▸ Provides an implementation for all the methods of the
base class.
▸ Returns the type of things the base class prescribes.
▸ Doesn’t put extra constraints on arguments for methods.
78. SOLID PRINCIPLES
FIZZ BUZZ ASSIGNMENT
▸ Generate a list of integers, from 1 to n.
▸ Numbers that are divisible by 3 should be replace with
“Fizz”
▸ Numbers that are divisible by 5 should be replaced with
“Buzz”
▸ Numbers that are both divisible by 3 and by 5 should be
replaced with “FizzBuzz”
79. <?php
class FizzBuzz
{
public static function generateList($limit)
{
$list = [];
for ($number = 1; $number <= $limit; $number++) {
$list[] = self::generateElement($number);
}
return $list;
}
private static function generateElement($number)
{
if ($number % 3 === 0 && $number % 5 === 0) {
return 'FizzBuzz';
}
if ($number % 3 === 0) {
return 'Fizz';
}
if ($number % 5 === 0) {
return 'Buzz';
}
return $number;
}
}
90. <?php
use DoctrineDBALConnection;
class Authentication
{
private $connection;
public function __construct(Connection $connection)
{
$this->connection = $connection;
}
public function checkCredentials($username, $password)
{
$user = $this->connection->fetchAssoc(
'SELECT * FROM users WHERE username = ?',
[$username]
);
if ($user === null) {
throw new InvalidCredentialsException(
"User not found."
);
}
//validate password
...
}
}
92. <?php
class Authentication
{
private $userRepository;
public function __construct(UserRepository $userRepository)
{
$this->userRepository = $userRepository;
}
public function checkCredentials($username, $password)
{
$user = $this->userRepository->ofUsername($username);
if ($user === null) {
throw new InvalidCredentialsException(
"User not found."
);
}
//validate password
...
}
}
93. <?php
class UserRepository
{
private $connection;
public function __construct(Connection $connection)
{
$this->connection = $connection;
}
public function ofUsername($username)
{
return $this->connection->fetchAssoc(
'SELECT * FROM users WHERE username = ?',
[$username]
);
}
}
98. SOLID PRINCIPLES
AUTHENTICATION CLASS DEPENDS ON USER REPOSITORY INTERFACE
Authentication.php UserRepositoryInterface.php
TextFileUserRepository.php DoctrineDbalUserRepository.php Connection.php