
“Divine” code is a loud term that may seem like a yellow heading, but nevertheless, it is about such code that we are talking about: what parts it consists of and how to write it. This is a story about my efforts to make sure that the tasks did not come back with a code review with the note: “All heh * nya - remake”.
I have no specialized education, and I had to learn programming in practice through mistakes, abrasions and bruises. Constantly working to improve the quality of the written code, I worked out some rules that it must comply with. I want to share them.
GOD'S code - an acronym of acronyms - a code written in accordance with the principles of Grasp, Object calisthenics, Demeter's law and Solid. Someone they know everything, someone met only a few, but we will consider every component of the acronym. I do not aim to immerse myself in every group of rules in detail, since they have already been covered many times on the Internet. Instead, I suggest squeezing from my own experience.
GRASP
Nine templates for assigning responsibilities to classes and objects. For convenience of memorization, I divide them into two subgroups:
- In the first subgroup, you can select the rules that allow you to write atomic modules that are well tested and changed. These rules are not so much about responsibility, but, let's say, about the properties of modules: weak connectivity, strong adhesion, polymorphism, resistance to change. For myself, I overlap these rules with SOLID, more about this in the relevant part.
- The second subgroup is already clearer patterns that tell us who creates objects (“creator” is a collective name for factory patterns), how to reduce connectivity between modules (using “controller” and “intermediary” patterns), whom to delegate separate duties (information expert) and what to do if I love DDD and at the same time low coupling (pure fiction).
You can
read more here .
Object Calichenica
A set of code design rules very similar to codeStyle. They are also nine. I will talk about the three that I try to observe in my daily work (slightly modified), the rest can be read in the
original source .
- The length of the method is no more than 15 LOC, the number of methods in a class is no more than 15, the number of classes in one namespace is no more than 15. The bottom line is that it is very difficult to read and understand long sheets of code. In addition, long classes and methods are a signal for violation of SRP (see below).
- Maximum one level per method.
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
}
}
, , , , «» . , . , , , «», , :)