Use case of the SRP concept in a real application

5

I am studying some OOP concepts and I see a lot about the SRP issue, "separate actions into classes etc". at the level of tests, I decided to create a small system to register users in order to test the knowledge, only that I came across some questions

  

1 - Should I separate the validation logic and the part of CRUD into classes   separated?

     

2 - Is it the responsibility of the user to validate himself?

     

2.1 - Is the User class responsible for "accumulating" validation errors?

     

3 - Is the user responsible for saving himself?

     

4 - What would this example look like with thousands of attributes? use a loop   in setter methods?

What I concluded, based on the studies:

  • I want to use 3 classes, User, UserValidator and UserCRUD, because in my understanding they are different "actions"
  • The user class would be the most "abstract", relating validation and integration with the database.
  • Should I create a second class called UserValidator that would be responsible for the validation rules and could be coupled to some validation library? (Respect / rakit)

  • And finally, I would create a last class called UserCRUD, which would inherit the methods of another class called "crudModel", responsible for database integration

Code sample:

class User {

    private $name;
    private $age;

    function __construct () {

        $this->userValidator = new userValidator;
        $this->userCRUD = new userCRUD;
    }

    function setName($name) {

        if($this->userValidator->validateName($name))
            $this->name = $name;
    }

    function setAge($age) {

        if($this->userValidator->validateAge($age))
            $this->age = $age;
    }

    function hasErrors () {
        return $this->userValidator->hasErrors();
    }

    function Save () {

        $user_data['name'] = $this->name;
        $user_data['age'] = $this->age;

        return $this->userCRUD->create($user_data);
        // retorna true para salvo e falso para erro
    }

}

-

    class UserValidator {

    private $error_bag;

    public function validateName ($name) {

        if(strlen($name) > 5)
            return 1;
        else 
            $this->setValidationError('name', 'Nome deve ser maior que 5');
    }

    public function validateAge ($age) {

        if(is_numeric($age))
            return 1;
        else 
            $this->setValidationError('age', 'Idade inválida');
    }

    private function setValidationError ($error, $error_msg) {
        $this->error_bag[$error] = $error_msg;
    }

    private function getValidationErrors() {
        return $this->error_bag;
    }

    public function hasErrors () {

        if( empty($this->getValidationErrors()) )
            return false;
        else
            return $this->getValidationErrors();
    }
}

Code call:

    $User = new User;
    $User->setName('Fulano de Tal');
    $User->setAge(50);

    if($User->hasErrors()) {
        // exibe os erros na tela
        print_r($User->hasErrors());

    } else {
        // salve o usuario
        $saveUser = $User->Save();
        var_dump($saveUser);
    }
    
asked by anonymous 30.09.2018 / 05:28

1 answer

4

In search of the SRP (Single Responsibility Principle)

Follow the answers to your questions.

1 - Should I separate the validation logic and the part of CRUD into separate classes?

Yes. It is better to separate the validation of the part that does the operations of CRUD, since they are different operations.

2 - Is it the user's responsibility to validate himself?

Not necessarily. You can create a component to validate a user, as this would go against SRP.

2.1 - Is the User class responsible for "accumulating" validation errors?

Complementing the previous answer: the component that does validation would be a more appropriate place for this information.

3 - Is the user responsible for saving himself?

It's better to create a class to save a user, because everything gets more separate. When a user saves himself the SRP is being violated, because the object has two responsibilities: to contain the user information and persist it in the database. This type approach is called Active Record and it ends up mixing things up, so it's best to avoid it in order to follow the SRP.

4 - What would this example look like with thousands of attributes? to use a loop in setters methods?

From what I understand, your question on this issue is in the case of a lot of data. If your User object has multiple data, you can pass an array with the data to avoid a huge amount of getters and setters. This question is answered in more detail here.

Your User class example

I noticed that your code contains some dependencies on the User object:

function __construct () {

    $this->userValidator = new userValidator;
    $this->userCRUD      = new userCRUD;

}

This implementation could improve if you pass the User object to the objects and do not receive them, for example:

class UserValidator
{

    public function validate(User $user)
    {
        // Pegar os dados do objeto user e validá-los aqui
    }

}

class UserCrud
{

    public function save(User $user)
    {
         // Salva e edita o usuário
    }

    public function delete(User $user)
    {
         // Deleta o usuário
    }

}

Still on the UserCrud class, you said:

  

Finally, I would create a last class called UserCRUD, which would inherit methods from another class called "crudModel", which is responsible for integrating with the database

I would advise you to inject the crudModel class instead of making UserCrud inherit from it, since overused inheritance can generate some confusing hierarchies. In this case, the composition could be an alternative and instead of inheriting you could do the following:

class UserCrud
{

    private $crudModel;

    public function __construct(crudModel $crudModel)
    {
          $this->crudModel = $crudModel;
    }

}
    
30.09.2018 / 17:39