Art


1. Verwenden Sie weniger Vererbung und mehr Kombination

2. Vermeiden Sie kohärente Schnittstellen

3. Es wird empfohlen, final zu verwenden Klassen

1. Verwenden Sie weniger Vererbung und mehr Einsatz von Komposition

Wie die Viererbande bereits in Design Patterns sagte, haben wir Die Zusammensetzung sollte nach Möglichkeit der Vererbung vorgezogen werden. Die Verwendung sowohl der Vererbung als auch der Zusammensetzung bietet viele Vorteile. Der Hauptpunkt dieser Richtlinie besteht darin, dass Sie, wenn Sie die Vererbung instinktiv nutzen, darüber nachdenken sollten, ob die Komposition Ihre Bedürfnisse besser abbilden könnte. In manchen Fällen trifft das zu.

Als nächstes denken Sie vielleicht: „Wann sollte ich also die Vererbung nutzen?“ Die Antwort hängt von Ihrer Frage ab, aber hier sind einige Hinweise darauf, wann Vererbung besser ist als Zusammensetzung:

Ihre Vererbung drückt eine „ist ein“-Beziehung statt „hat eine“-Beziehung aus (Mensch->Tier, Benutzer-》) Benutzerdetails)

Sie können den Code der Basisklasse wiederverwenden (Menschen können wie Tiere sein (ähnliche Bewegung)

Sie möchten globale Änderungen an allen abgeleiteten Klassen vornehmen, indem Sie die Basisklasse ändern (den Energieverbrauch von Tieren ändern, wenn sie sich bewegen)

Schlecht:

 class Employee
{
    private $name;
    private $email;
 
    public function __construct(string $name, string $email)
    {
        $this->name = $name;
        $this->email = $email;
    }
 
    // ...
}
 
 
// 不好,因为 Employees "有" taxdata
// 而 EmployeeTaxData 不是 Employee 类型的
 
 
class EmployeeTaxData extends Employee
{
    private $ssn;
    private $salary;
    
    public function __construct(string $name, string $email, string $ssn, string $salary)
    {
        parent::__construct($name, $email);
 
        $this->ssn = $ssn;
        $this->salary = $salary;
    }
 
    // ...
}

Gut:

class EmployeeTaxData
{
    private $ssn;
    private $salary;
 
    public function __construct(string $ssn, string $salary)
    {
        $this->ssn = $ssn;
        $this->salary = $salary;
    }
 
    // ...
}
 
class Employee
{
    private $name;
    private $email;
    private $taxData;
 
    public function __construct(string $name, string $email)
    {
        $this->name = $name;
        $this->email = $email;
    }
 
    public function setTaxData(string $ssn, string $salary)
    {
        $this->taxData = new EmployeeTaxData($ssn, $salary);
    }
 
    // ...
}

2. Vermeiden Sie kohärente Schnittstellen

Kohärente Schnittstellen Fluent interface ist ein API-Entwurfsmuster, das die Lesbarkeit von Code in der objektorientierten Programmierung verbessern soll. Es basiert auf Methodenketten Method chaining

, in denen Kontextcode vorhanden ist Die Komplexität kann reduziert werden, wie z. B. PHPUnit Mock Builder und Doctrine Query Builder. Mehr Situationen führen zu höheren Kosten:

Obwohl es einige geben kann Kontexte, häufig Builder-Objekte, bei denen dieses Muster die Ausführlichkeit des Codes verringert (zum Beispiel der PHPUnit Mock Builder oder der Doctrine Query Builder), häufiger ist es mit einigen Kosten verbunden:

Unterbricht die Objektkapselung

2. Bricht das Dekorationsmuster

3. In der Testkomponente ist das nicht einfachmock

Es ist nicht einfach, die diffzu lesen, die zur Einreichung führt

5. Um mehr zu erfahren, lesen Sie bitte Warum kohärente Schnittstellen Are Bad, geschrieben von Marco Pivetta.

Schlecht:

class Car
{
    private $make = 'Honda';
    private $model = 'Accord';
    private $color = 'white';
 
    public function setMake(string $make): self
    {
        $this->make = $make;
 
        // NOTE: Returning this for chaining
        return $this;
    }
 
    public function setModel(string $model): self
    {
        $this->model = $model;
 
        // NOTE: Returning this for chaining
        return $this;
    }
 
    public function setColor(string $color): self
    {
        $this->color = $color;
 
        // NOTE: Returning this for chaining
        return $this;
    }
 
    public function dump(): void
    {
        var_dump($this->make, $this->model, $this->color);
    }
}
 
$car = (new Car())
  ->setColor('pink')
  ->setMake('Ford')
  ->setModel('F-150')
  ->dump();

Gut:

 class Car
{
    private $make = 'Honda';
    private $model = 'Accord';
    private $color = 'white';
 
    public function setMake(string $make): void
    {
        $this->make = $make;
    }
 
    public function setModel(string $model): void
    {
        $this->model = $model;
    }
 
    public function setColor(string $color): void
    {
        $this->color = $color;
    }
 
    public function dump(): void
    {
        var_dump($this->make, $this->model, $this->color);
    }
}
 
$car = new Car();
$car->setColor('pink');
$car->setMake('Ford');
$car->setModel('F-150');
$car->dump();

3. Es wird empfohlen final verwenden Verwenden Sie das Schlüsselwort

, wann immer die Klasse

final verfügbar ist:

1. Verhindern Sie eine unkontrollierte Vererbungskette

2. Ermutigen Sie die Kombination.

3 . Fördern Sie das Einzelverantwortungsmodell.

4. Ermutigen Sie Entwickler, Ihre öffentlichen Methoden zu verwenden, anstatt Klassen zu erben, um Zugriff auf geschützte Methoden zu erhalten.

5. Machen Sie es möglich, den Code zu ändern, ohne die Anwendungen zu beschädigen, die Ihre Klassen verwenden.

Die einzige Voraussetzung ist, dass Ihre Klasse eine Schnittstelle implementiert und keine anderen öffentlichen Methoden definiert sind.

Weitere Informationen finden Sie im Blogbeitrag zu diesem Thema von Marco Pivetta (Ocramius).

Schlecht:

final class Car
{
    private $color;
    
    public function __construct($color)
    {
        $this->color = $color;
    }
    
    /**
     * @return string The color of the vehicle
     */
    public function getColor()
    {
        return $this->color;
    }
}

Gut:

 interface Vehicle
{
    /**
     * @return string The color of the vehicle
     */
    public function getColor();
}
 
final class Car implements Vehicle
{
    private $color;
    
    public function __construct($color)
    {
        $this->color = $color;
    }
    
    /**
     * {@inheritdoc}
     */
    public function getColor()
    {
        return $this->color;
    }
}