Giter Site home page Giter Site logo

Comments (18)

dmytro-y-dev avatar dmytro-y-dev commented on May 12, 2024 2

Honestly, the good example is a debug hell. Imagine that these 3 functions are in different files, it won't be easy for you to jump between them. The provided "bad" code is easier to extend and understand than good one, though it is also broken.

bad code

function emailClients($clients) {
    foreach ($clients as $client) {
        $clientRecord = $db->find($client);

        if ($clientRecord->isActive()) {
            email($client);
        }
    }
}

vs

good code

so called "Baklav code" in analogy to spaghetti code (https://www.johndcook.com/blog/2009/07/27/baklav-code/)

function emailClients($clients) {
    $activeClients = activeClients($clients);
    array_walk($activeClients, 'email');
}

function activeClients($clients) {
    return array_filter($clients, 'isClientActive');
}

function isClientActive($client) {
    $clientRecord = $db->find($client);
    return $clientRecord->isActive();
}

In a Good example, we will need to pass the dependency through 3 functions.
This is very bad.

emailClients tries to be a good shim, but in the end it wires dependencies too hard by providing a name of global function to array_walk. Also, it has 2 responsibilities : 1. Filter active clients 2. Email active clients. First responsibility should be moved away.

from clean-code-php.

SahinU88 avatar SahinU88 commented on May 12, 2024 2

Just curious, wouldn't it be better to call the function emailActiveClients?
In the end, if I see the function emailClients I wouldn't assume right away, that there is some filtering going on.

And the $db instance could be a class which is accessible in a global way, like some Frameworks do: DB::find( $client ) which would resolve the issue of passing an extra argument.

from clean-code-php.

dmytro-y-dev avatar dmytro-y-dev commented on May 12, 2024 2

@SahinU88
Actually, if you will through popular Laravel libraries (e.g. Sluggable trait https://github.com/cviebrock/eloquent-sluggable 3.1k stars, https://github.com/cviebrock/eloquent-sluggable/blob/master/src/Services/SlugService.php), you will see something like next:

class A {
/** @var Model */
private $model;

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

public function all() {
  return $this->model->all();
}
}

...

$mymodel = new Client();
$a = new A($mymodel);

You can avoid using Client from facade if you need to generalize behavior. Also, if you use PHPStorm, it will show you warnings about usage of Client methods from facade. It is ok to use Client from facade in controllers/commands (i.e. on top-top level of execution) or for fast prototyping, but when you go deeper into classes that do real job, you should really use interfaces and Model to make your code more reusable and testable.

from clean-code-php.

TomasVotruba avatar TomasVotruba commented on May 12, 2024 1

Just curious, wouldn't it be better to call the function emailActiveClients?

I think that's good idea

from clean-code-php.

dmytro-y-dev avatar dmytro-y-dev commented on May 12, 2024 1

@peter-gribanov

Don't use a Singleton pattern

This requires as well some explanation in Readme.md why it is antipattern. There are several problems with it when it is used improperly. See https://stackoverflow.com/a/138012/2678487 vs https://jorudolph.wordpress.com/2009/11/22/singleton-considerations/, but I would better refer to real literature like GoF's book (sorry, can't find it on the Internet).

Valid case for singleton that I can think of is DB connection pools. Also, common ORM implementations (Hibernate, Doctrine) have significant global state, as they keep $connection resource and tables of changed entities. This is not such a big deal when you have single-threaded application, but what if you have 10-100 threads that use same global DB instance in parallel with modifying operations? You need really solid code to handle connection and ops in a thread safe manner, as you likely will reuse same DB connection between threads for performance reasons.

In general singletons are easy to abuse, but in some cases it is a useful pattern.

@SahinU88

Just curious, wouldn't it be better to call the function emailActiveClients?

Absolutely, it would. M.b. emailOnlyActiveClients, because it receives array of ids of any clients, so name would assume that only active clients from provided list will be emailed. However, emailActiveClients is also good name and it is shorter. emailClients is too generic.

from clean-code-php.

TomasVotruba avatar TomasVotruba commented on May 12, 2024

Thanks for reporting
Could you send PR to fix that please?

I think the 2nd case looks to simplest to explain the example.

from clean-code-php.

MacDada avatar MacDada commented on May 12, 2024

@TomasVotruba Which fix version? ;p

from clean-code-php.

MacDada avatar MacDada commented on May 12, 2024

Mind that the "good" example must be fixed as well.

from clean-code-php.

peter-gribanov avatar peter-gribanov commented on May 12, 2024

@TomasVotruba It's necessary to change the whole example entirely.
In a Good example, we will need to pass the dependency through 3 functions.
This is very bad.

from clean-code-php.

TomasVotruba avatar TomasVotruba commented on May 12, 2024

This issue is about fixing broken code only. For changing the code entirely, please open new issue/PR to prevent mixing many things at once.

from clean-code-php.

peter-gribanov avatar peter-gribanov commented on May 12, 2024

@TomasVotruba issue already exists #38

from clean-code-php.

TomasVotruba avatar TomasVotruba commented on May 12, 2024

Great

from clean-code-php.

dmytro-y-dev avatar dmytro-y-dev commented on May 12, 2024

(are you good?)

function emailClients($clients, Mailer $mailer, Database $db) {
    $activeClients = activeClients($clients, $db);
    
    array_walk($activeClients, function($client) {
        $mailer->email($client);
    });
}

function activeClients($clients, Database $db) {
    return array_filter($clients, function($client) use ($db) {
        return isClientActive($client->isActive(), $db);
    });
}

function isClientActive($client, Database $db) {
    $clientRecord = $db->find($client);
    return $clientRecord->isActive();
}

function main()
{
    $clients = [1, 2, 3];
    $mailer = MailerFactory::createNiceMailer(); // Or reach me out via Dependency Injection Container
    $db = DatabaseFactory::createNiceDb();

    emailClients($clients, $mailer, $db);
}

vs (are you bad?)

function emailClients($clients, Mailer $mailer, Database $db) {
    foreach ($clients as $client) {
        $clientRecord = $db->find($client);

        if ($clientRecord->isActive()) {
            $mailer->email($client);
        }
    }
}

function main()
{
    $clients = [1, 2, 3];
    $mailer = MailerFactory::createNiceMailer(); // Or reach me out via Dependency Injection Container
    $db = DatabaseFactory::createNiceDb();

    emailClients($clients, $mailer, $db);
}

vs (are you mad?)

function filterActiveClients($clients_ids, Database $db) {
    // Please, don't reimplement something that 99% of ORMs can do
    // See https://en.wikipedia.org/wiki/Inner-platform_effect and know your tools.

    return $db
        ->find(Client::class)
        ->where()
            ->field('active', true)
            ->and()->field('id', 'in', $clients_ids)
        ->endwhere()
        ->all()
    ;
}

function emailClients($clients, Mailer $mailer) {
    foreach ($clients as $client) {
        $mailer->email($client);
    }
}

function main()
{
    $clients_ids = [1, 2, 3];
    $mailer = MailerFactory::createNiceMailer(); // Or reach me out via Dependency Injection Container
    $db = DatabaseFactory::createNiceDb();

    $clients = filterActiveClients($clients_ids, $db);
    emailClients($clients, $mailer);
}

from clean-code-php.

MacDada avatar MacDada commented on May 12, 2024

Just curious, wouldn't it be better to call the function emailActiveClients?
In the end, if I see the function emailClients I wouldn't assume right away, that there is some filtering going on.

@SahinU88 Yep.

And the $db instance could be a class which is accessible in a global way, like some Frameworks do: DB::find( $client ) which would resolve the issue of passing an extra argument.

@SahinU88 Out of the frying pan into the fire…

from clean-code-php.

peter-gribanov avatar peter-gribanov commented on May 12, 2024

And the $db instance could be a class which is accessible in a global way, like some Frameworks do: DB::find( $client ) which would resolve the issue of passing an extra argument.

@SahinU88 Don't use a Singleton pattern

from clean-code-php.

SahinU88 avatar SahinU88 commented on May 12, 2024

@MacDada / @peter-gribanov
The singleton-pattern could also be a service container which is also used in frameworks.
For example in Laravel, you would use something like:

use App\Client; // client is the Eloquent-Model-Repo
...

$clients = Client::all();

I know the example is a little far fetched, but I hope you get the idea.

from clean-code-php.

peter-gribanov avatar peter-gribanov commented on May 12, 2024

@metamaker I agree. Description Singleton incomplete.
This section was created chaotically. It needs to be improved.

You can make PR and expand the description.

PS: Singleton was added to GoF and only later was recognized as an anti-pattern.

from clean-code-php.

TomasVotruba avatar TomasVotruba commented on May 12, 2024

Surpassed by #38

from clean-code-php.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.