
O código “divino” é um termo de alto som que pode parecer um cabeçalho amarelo, mas, no entanto, é exatamente esse código que será discutido: em que partes ele consiste e como escrevê-lo. Esta é uma história sobre meus esforços para garantir que as tarefas não retornem com uma revisão de código com a nota: "Tudo o que ele * nya - refaz".
Eu não tenho formação especializada e tive que aprender a programar na prática, através de erros, abrasões e contusões. Trabalhando constantemente para melhorar a qualidade do código escrito, elaborei algumas regras pelas quais ele deveria estar em conformidade. Eu quero compartilhá-los.
O código de DEUS - sigla sigla - código escrito de acordo com os princípios de Grasp, Calisthenics de objetos, Lei de Deméter e Solid. Alguém que todos conhecem, alguém conheceu apenas alguns, mas consideraremos cada componente do acrônimo. Não tenho como objetivo mergulhar em cada grupo de regras em detalhes, pois elas foram abordadas muitas vezes na Internet. Em vez disso, ofereço um aperto de minha própria experiência.
GRASP
Nove modelos para atribuir responsabilidades a classes e objetos. Para facilitar a lembrança, divido-os em dois subgrupos:
- No primeiro subgrupo, podemos distinguir regras que permitem escrever módulos atômicos que são bem testados e modificados. Essas regras não se referem tanto à responsabilidade, mas, digamos, às propriedades dos módulos: acoplamento fraco, forte adesão, polimorfismo, resistência a mudanças. Por mim, substituo essas regras pelo SOLID, mais sobre isso na parte correspondente.
- O segundo subgrupo já possui modelos mais claros que nos informam quem cria os objetos ("criador" - o nome coletivo dos padrões de fábrica), como reduzir a conexão entre os módulos (usando os padrões "controlador" e "intermediário"), a quem delegar responsabilidades individuais (especialista em informações) e o que fazer se eu gostar de DDD e, ao mesmo tempo, baixo acoplamento (pura ficção).
Leia mais
aqui .
Calistenia de objetos
Um conjunto de regras de execução de código muito semelhantes ao código de leis codeStyle. Há também nove deles. Vou falar sobre três que tento seguir no meu trabalho diário (ligeiramente modificado), o resto pode ser lido na
fonte original .
- O comprimento do método não é maior que 15 LOC, o número de métodos em uma classe não é maior que 15, o número de classes em um espaço para nome não é maior que 15. A linha inferior é que longas folhas de código são muito difíceis de ler e entender. Além disso, classes e métodos longos são um sinal de violação do SRP (mais sobre isso abaixo).
- Máximo de um nível de aninhamento por método.
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
}
}
, , , , «» . , . , , , «», , :)