mirror of
https://github.com/codeguy/php-the-right-way.git
synced 2025-08-09 23:46:37 +02:00
Merge pull request #1 from skyzyx/20150315-solid
Expanded and clarified the language around SOLID principles.
This commit is contained in:
@@ -15,47 +15,66 @@ separate from our objects. In terms of Dependency Injection, this means loosenin
|
||||
instantiating them elsewhere in the system.
|
||||
|
||||
For years, PHP frameworks have been achieving Inversion of Control, however, the question became, which part of control
|
||||
are you inverting, and where to? For example, MVC frameworks would generally provide a super object or base controller
|
||||
are we inverting, and where to? For example, MVC frameworks would generally provide a super object or base controller
|
||||
that other controllers must extend to gain access to its dependencies. This **is** Inversion of Control, however,
|
||||
instead of loosening dependencies, this method simply moved them.
|
||||
|
||||
Dependency Injection allows us to more elegantly solve this problem by only injecting the dependencies we need, when we
|
||||
need them, without the need for any hard coded dependencies at all.
|
||||
|
||||
### SOLID
|
||||
### S.O.L.I.D.
|
||||
|
||||
#### Single responsibility principle
|
||||
#### Single Responsibility Principle
|
||||
|
||||
It's very helpful principle because it improve code reusability and
|
||||
states that every class should have responsibility over a single part of the functionality provided by the software.
|
||||
Your class should do just one thing and no more. And you can use it in any place of program without changing it.
|
||||
The Single Responsibility Principle is about actors and high-level architecture. It states that “A class should have
|
||||
only one reason to change.” This means that every class should _only_ have responsibility over a single part of the
|
||||
functionality provided by the software. The largest benefit of this approach is that it enables improved code
|
||||
_reusability_. By designing our class to do just one thing, we can use (or re-use) it in any other program without
|
||||
changing it.
|
||||
|
||||
#### Open/closed principle
|
||||
#### Open/Closed Principle
|
||||
|
||||
States that classes should be open for extension, but closed for modification.
|
||||
it's very important and helpful especially in production,
|
||||
because it provides ability to change program behaviour without changing source code.
|
||||
The Open/Closed Principle is about class design and feature extensions. It states that “Software entities (classes,
|
||||
modules, functions, etc.) should be open for extension, but closed for modification.” This means that we should design
|
||||
our modules, classes and functions in a way that when a new functionality is needed, we should not modify our existing
|
||||
code but rather write new code that will be used by existing code. Practically speaking, this means that we should write
|
||||
classes that implement and adhere to _interfaces_, then type-hint against those interfaces instead of specific classes.
|
||||
|
||||
#### Liskov substitution principle
|
||||
The largest benefit of this approach is that we can very easily extend our code with support for something new without
|
||||
having to modify existing code, meaning that we can reduce QA time, and the risk for negative impact to the application
|
||||
is substantially reduced. We can deploy new code, faster, and with more confidence.
|
||||
|
||||
This principle extends previous principle, and states
|
||||
if S is a subtype of T, then objects of type T in a program may be replaced with objects of type S
|
||||
without altering any class of the program.
|
||||
That's amazing principle provides ability to build very flexible and easily configurable programs
|
||||
because when you change one object to another you don't need to change anything in your program.
|
||||
#### Liskov Substitution Principle
|
||||
|
||||
#### Interface segregation principle
|
||||
The Liskov Substitution Principle is about subtyping and inheritance. It states that “Child classes should never break
|
||||
the parent class’ type definitions.” Or, in Robert C. Martin’s words, “Subtypes must be substitutable for their base
|
||||
types.”
|
||||
|
||||
Splits interfaces which are very large into smaller and more specific ones.
|
||||
It provides to work in each time only with methods that interesting for client,
|
||||
and facilitate conceptual explanation of the code.
|
||||
For example, if we have a `FileInterface` interface which defines an `embed()` method, and we have `Audio` and `Video`
|
||||
classes which both implement the `embed()` method, then we can expect that the usage of the `embed()` method will always
|
||||
do the thing that we intend. If we later create a `PDF` class or a `Gist` class which implement the `FileInterface`
|
||||
interface, we will already know and understand what the `embed()` method will do. The largest benefit of this approach
|
||||
is that we have the ability to build flexible and easily-configurable programs, because when we change one object of a
|
||||
type (e.g., `FileInterface`) to another we don't need to change anything else in our program.
|
||||
|
||||
#### Interface Segregation Principle
|
||||
|
||||
The Interface Segregation Principle (ISP) is about _business-logic-to-clients_ communication. It states that “No client
|
||||
should be forced to depend on methods it does not use.” This means that instead of having a single monolithic interface
|
||||
that all conforming classes need to implement, we should instead provide a set of smaller, concept-specific interfaces
|
||||
that a conforming class implements one or more of.
|
||||
|
||||
For example, a `Car` or `Bus` class would be interested in a `steeringWheel()` method, but a `Motorcycle` or `Tricycle`
|
||||
class would not. Conversely, a `Motorcycle` or `Tricycle` class would be interested in a `handlebars()` method, but a
|
||||
`Car` or `Bus` class would not. There is no need to have all of these types of vehicles implement support for both
|
||||
`steeringWheel()` as well as `handlebars()`, so we should break-apart the source interface.
|
||||
|
||||
#### Dependency Inversion Principle
|
||||
|
||||
Dependency Inversion Principle set of object oriented design principles that states one
|
||||
should *"Depend on Abstractions. Do not depend on concretions."*. Put simply, this means our dependencies should be
|
||||
interfaces/contracts or abstract classes rather than concrete implementations. We can easily refactor the above example
|
||||
to follow this principle.
|
||||
The Dependency Inversion Principle is about removing hard-links between discrete classes so that new functionality can
|
||||
be leveraged by passing a different class. It states that one should *"Depend on Abstractions. Do not depend on
|
||||
concretions."*. Put simply, this means our dependencies should be interfaces/contracts or abstract classes rather than
|
||||
concrete implementations. We can easily refactor the above example to follow this principle.
|
||||
|
||||
{% highlight php %}
|
||||
<?php
|
||||
@@ -78,7 +97,7 @@ class MysqlAdapter implements AdapterInterface {}
|
||||
|
||||
There are several benefits to the `Database` class now depending on an interface rather than a concretion.
|
||||
|
||||
Consider that you are working in a team and the adapter is being worked on by a colleague. In our first example, we
|
||||
Consider that we are working in a team and the adapter is being worked on by a colleague. In our first example, we
|
||||
would have to wait for said colleague to finish the adapter before we could properly mock it for our unit tests. Now
|
||||
that the dependency is an interface/contract we can happily mock that interface knowing that our colleague will build
|
||||
the adapter based on that contract.
|
||||
|
Reference in New Issue
Block a user