
Le code «divin» est un terme très sonore qui peut sembler jaune, mais néanmoins c'est précisément ce code qui sera discuté: de quelles parties il se compose et comment l'écrire. Ceci est une histoire sur mes efforts pour garantir que les tâches ne reviennent pas avec une révision du code avec la note: "All he * nya - redo".
Je n'ai pas de formation spécialisée, et j'ai dû apprendre la programmation en pratique, à travers des erreurs, des écorchures et des ecchymoses. Travaillant constamment à améliorer la qualité du code écrit, j'ai élaboré quelques règles auxquelles il devrait se conformer. Je veux les partager.
Code de DIEU - acronyme acronyme - code écrit conformément aux principes de Grasp, Object calisthenics, Demeter's law et Solid. Quelqu'un qu'ils connaissent tous, quelqu'un n'en a rencontré que quelques-uns, mais nous considérerons chaque composante de l'acronyme. Je ne me fixe pas pour objectif de me plonger dans chaque groupe de règles en détail, car elles ont été reprises à plusieurs reprises sur Internet. Au lieu de cela, je propose une compression de ma propre expérience.
GRASP
Neuf modèles pour attribuer des responsabilités aux classes et aux objets. Pour faciliter la mémorisation, je les divise en deux sous-groupes:
- Dans le premier sous-groupe, nous pouvons distinguer des règles qui vous permettent d'écrire des modules atomiques bien testés et modifiés. Ces règles ne concernent pas tant la responsabilité, mais, disons, les propriétés des modules: couplage faible, forte adhérence, polymorphisme, résistance aux changements. Pour ma part, je remplace ces règles par SOLID, plus à ce sujet dans la partie correspondante.
- Le deuxième sous-groupe est déjà des modèles plus clairs qui nous disent qui crée les objets («créateur» - le nom collectif pour les modèles d'usine), comment réduire la connexion entre les modules (en utilisant les modèles «contrôleur» et «intermédiaire»), à qui déléguer responsabilités individuelles (expert en information) et que faire si j'aime DDD et en même temps faible couplage (pure fiction).
Lisez plus
ici .
La gymnastique objet
Un ensemble de règles d'exécution de code très similaires au code de lois codeStyle. Il y en a aussi neuf. J'en parlerai trois que j'essaie de suivre dans mon travail quotidien (légèrement modifié), le reste peut être lu dans la
source originale .
- La longueur de la méthode ne dépasse pas 15 LOC, le nombre de méthodes dans la classe ne dépasse pas 15, le nombre de classes dans un espace de noms ne dépasse pas 15. L'essentiel est que les longues feuilles de code sont très difficiles à lire et à comprendre. De plus, les classes et méthodes longues sont un signal de violation de SRP (voir ci-dessous).
- Maximum un niveau d'imbrication par méthode.
public function processItems(array items)
{
// 0
foreach (items as item) {
// 1
for (i = 0; i < 5; i++) {
// 2
… process item 5 times …
}
}
}
item
.
public function processItems(array items)
{
// 0
foreach (items as item) {
// 1
this.processItem(item);
}
}
public function processItem(Item item)
{
// 0
for (i = 0; i < 5; i++) {
// 1
… process item 5 times …
}
}
-, — , , . else
, .
public function processSomeDto(SomeDtoClass dto)
{
if (predicat) {
throw new Exception(‘predicat is failed’);
} else {
return this.dtoProcessor.process(dto);
}
}
:
public function processSomeDto(SomeDtoClass dto)
{
if (predicat) {
throw new Exception(‘predicat is failed’);
}
return this.dtoProcessor.process(dto);
}
, .
GRASP’a. , .

: B, . . :
- .
- , .
- .
- , .
. , .
this.objectB.objectC.getSomeStuff()
, , .
. -, . :
public function methodA()
{
spawnedObject = this.factory.spawn();
spawnedObject.performSomeStuff();
}
:
public function methodA()
{
this.factory.spawn().performSomeStuff();
}
, - .
public function methodA()
{
this.processor.process(this.factory.spawn());
}
: DTO/Entity. .
public function methodA(SomeDtoClass dto)
{
dto.getAddress().getCity();
}
, , . , , , ,
getCity DTO Address
dto
.
SOLID
SRP, OCP, LSP, ISP, DIP — , .
SRP — . — , . High Cohesion GRASP’a.
: , — - (MVC). - - , SRP.
public function indexAction(RequestInterface request): ResponseInterface
{
requestDto = this.requestTransformer.transform(request);
responseDto = this.requestProcessor.process(requestDto);
return this.responseTransformer.transform(responseDto);
}
- , , — . , , , , .
OCP — -. , , .
- , if/switch. , . . — . , .
resolver, .
final lass Resolver implements ResolverInterface
{
private mapping;
public function Resolver(array mapping)
{
this.mapping = mapping;
}
public function resolve(Item item)
{
return this.mapping[item.getType()].perform(item);
}
}
, . : , final, abstract, .
LSP — . , .
:
- ( ).
- ( , , , ).
- ( ).
- , ( , , , ).
class ParentClass
{
public function someMethod(string param1)
{
// some logic
}
}
class ChildClass extends ParentClass
{
public function someMethod(string param1, string param2)
{
if (param1 == '') {
throw new ExtraException();
}
// some logic
}
}
someMethod
ChildClass
(
param2
),
param1
, . ,
ParentClass
ChildClass
.
ISP — . , . , , , , — , .
interface DuckInterface
{
public function swim(): void;
public function fly(): void;
}
class MallardDuck implements DuckInterface
{
public function swim(): void
{
// some logic
}
public function fly(): void
{
// some logic
}
}
class RubberDuck implements DuckInterface
{
public function swim(): void
{
// some logic
}
public function fly(): void
{
// can't fly :(
}
}
RubberDuck
DuckInterface
. , , ,
DuckInterface
FlyableInterface
SwimableInterface
, .
DIP — . , , ( , ).
new
.
class DIPViolation
{
public function badMethod()
{
someService = new SomeService(445, 'second params');
// operations with someService
}
}
- , . . :
class DIP
{
private $service;
public function DIP(SomeServiceInterface $someService)
{
$this->someService = $someService;
}
public function goodMethod()
{
// operations with someService
}
}
, , , , «» . , . , , , «», , :)