SOLID: PHP principles of object-oriented design of the five benchmarks

SOLID is the first five initials object-oriented design (OOD) guidelines abbreviation, the guidelines proposed by Robert C. Martin, he was better known name is Uncle Bob .

These guidelines make development of scalable, maintainable software easier. Also makes the code more compact, easily reconstituted. Agile development and is also part of the adaptive software development.

SOLID means:

Extended out acronyms may seem complicated, in fact, they are easy to understand.

  • S - single responsibility principle
  • O - Open Closed Principle
  • L - Richter substitution principle
  • The I - Interface Segregation Principle
  • D - Dependency Inversion Principle

Let's look at each principle, to understand why SOLID can help us become better developers.

Single Responsibility Principle


Abbreviation SRP , the principle reads:

A class has one and only one factor in making change, meaning that a class should only have a single responsibility.

For example, suppose we have some graphics, and you want to calculate the total area of ​​these graphics. Yes, it is very simple, right?

class Circle {
    public $radius;

    public function construct($radius) {
        $this->radius = $radius;
    }
}

class Square {
    public $length;

    public function construct($length) {
        $this->length = $length;
    }
}

First, we create graphics class, the class constructor to initialize the necessary parameters. Next, create AreaCalculator class, and then write the specified pattern calculating the total area of the logic code.

class AreaCalculator {

    protected $shapes;

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

    public function sum() {
        // logic to sum the areas
    }

    public function output() {
        return implode('', array(
            "",
            "Sum of the areas of provided shapes: ",
            $this->sum(),
            ""
        ));
    }
}

AreaCalculator use, we simply instantiate the class and pass an array of graphics, content in the bottom of the page display output.

$shapes = array(
    new Circle(2),
    new Square(5),
    new Square(6)
);

$areas = new AreaCalculator($shapes);

echo $areas->output();

Problems that the output method, AreaCalculator processed data output logic. Therefore, if the user wants the data to json or other output formats it?

All logic by AreaCalculator class treatment, which is precisely the violation of the principle of single responsibility ( SRP ); AreaCalculator class should only be responsible for calculation of the total area of graphics, it should not be concerned about is the user wants to json or HTML format data.

Therefore, to solve this problem, you can create a SumCalculatorOutputter class, and use it to display logic processing required to handle all the graphics of the total area of how to display.

SumCalculatorOutputter work categories are as follows:

$shapes = array(
    new Circle(2),
    new Square(5),
    new Square(6)
);

$areas = new AreaCalculator($shapes);
$output = new SumCalculatorOutputter($areas);

echo $output->JSON();
echo $output->HAML();
echo $output->HTML();
echo $output->JADE();

Now, no matter what format you want to output data to the user, by SumCalculatorOutputter class treatment.

Open Closed Principle


Objects and entities should be open for extension, but closed for modification.

Simply put, a class should not have to modify their own can easily extend its functionality. Let's look at AreaCalculator class, especially the sum method.

public function sum() {
    foreach($this->shapes as $shape) {
        if(is_a($shape, 'Square')) {
            $area[] = pow($shape->length, 2);
        } else if(is_a($shape, 'Circle')) {
            $area[] = pi() * pow($shape->radius, 2);
        }
    }

    return array_sum($area);
}

If we want to use the sum method can calculate the area more graphic, we have to add more IF / the else , but contrary to the principle of opening and closing.

Let the sum process becomes better way is to calculate the area of each shape code logic out of the sum method, which is shaped into various classes:

class Square {
    public $length;

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

    public function area() {
        return pow($this->length, 2);
    }
}

The same operation should be used to process the Circle class, add a class in the area method. Now, calculate the area of any shape and should be as simple as the following:

public function sum() {
    foreach($this->shapes as $shape) {
        $area[] = $shape->area();
    }

    return array_sum($area);
}

Next we can create another shape class and pass it when calculating the sum without destroying our code. But now there is another problem, how can we know the incoming AreaCalculator the object is actually a shape, or the shape of the object has an area method?

Interface coding practice SOLID part, for example the following example we create an interface class, the shape of each class will implement this interface class:

interface ShapeInterface {
    public function area();
}

class Circle implements ShapeInterface {
    public $radius;

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

    public function area() {
        return pi() * pow($this->radius, 2);
    }
}

In our AreaCalculator the sum method, we can examine the examples provided by the shape of the class whether ShapeInterface achieve, otherwise we will throw an exception:

public function sum() {
    foreach($this->shapes as $shape) {
        if(is_a($shape, 'ShapeInterface')) {
            $area[] = $shape->area();
            continue;
        }

        throw new AreaCalculatorInvalidShapeException;
    }

    return array_sum($area);
}

Richter substitution principle


If the object type for each of T1 o1, o2 T2 has an object type, such that all defined P T1 program when all the generations are replaced objects o1 o2, the behavior of the program P has not changed, then the type T2 is a subtype of type T1.

Phrase definition means that: each subclass or derived class can replace the base class without problems / parent class.

Still use AreaCalculator class, suppose we have a VolumeCalculator class that inherits AreaCalculator categories:

class VolumeCalculator extends AreaCalulator {
    public function construct($shapes = array()) {
        parent::construct($shapes);
    }

    public function sum() {
        // logic to calculate the volumes and then return and array of output
        return array($summedData);
    }
}

SumCalculatorOutputter categories:

class SumCalculatorOutputter {
    protected $calculator;

    public function __constructor(AreaCalculator $calculator) {
        $this->calculator = $calculator;
    }

    public function JSON() {
        $data = array(
            'sum' => $this->calculator->sum();
        );

        return json_encode($data);
    }

    public function HTML() {
        return implode('', array(
            '',
            'Sum of the areas of provided shapes: ',
            $this->calculator->sum(),
            ''
        ));
    }
}

If we run an example like this:

$areas = new AreaCalculator($shapes);
$volumes = new VolumeCalculator($solidShapes);

$output = new SumCalculatorOutputter($areas);
$output2 = new SumCalculatorOutputter($volumes);

The program will not go wrong, but when we use $ output2 object to call HTML when the method, we received a E_NOTICE error, suggesting that we be treated as an array of error string used.

To fix this problem, simply:

public function sum() {
    // logic to calculate the volumes and then return and array of output
    return $summedData;
}

Rather than VolumeCalculator class sum method returns an array.

$ summedData is a floating point, double precision floating point or integer.

Interface Segregation Principle


Consumer (client) should not rely on the use of the interface is not enforced, the method should not rely on or not in use.

Continued use of the above shapes known example has a solid block, if we need to calculate the volume of the shape, we can ShapeInterface add one method:

interface ShapeInterface {
    public function area();
    public function volume();
}

Any time you create must implement shape volume method, but the "plane" is no volume, implementation of this interface will be forced to make "plane" class to implement their own less than a method.

ISP principle not allowed to do so, so we should have to create another volume method SolidShapeInterface interfaces to replace this way, this cube-like solid body can implement this interface up:

interface ShapeInterface {
    public function area();
}

interface SolidShapeInterface {
    public function volume();
}

class Cuboid implements ShapeInterface, SolidShapeInterface {
    public function area() {
        //计算长方体的表面积
    }

    public function volume() {
        // 计算长方体的体积
    }
}

This is a better way, but be careful not only when prompted to type a prompt ShapeInterface or SolidShapeInterface .

You can create other interfaces, such as ManageShapeInterface , and implement it in the plane and the cube of class, so you can easily see that there is a form for managing API . example:

interface ManageShapeInterface {
    public function calculate();
}

class Square implements ShapeInterface, ManageShapeInterface {
    public function area() { /Do stuff here/ }

    public function calculate() {
        return $this->area();
    }
}

class Cuboid implements ShapeInterface, SolidShapeInterface, ManageShapeInterface {
    public function area() { /Do stuff here/ }
    public function volume() { /Do stuff here/ }

    public function calculate() {
        return $this->area() + $this->volume();
    }
}

Now in AreaCalculator class, we can easily use calculate alternative to the area called method, and check whether the object is ManageShapeInterface instance, instead of ShapeInterface .

Dependency Inversion Principle


Last, but not least:

Entity must rely on the abstract rather than concrete implementations. That high-level modules should not depend on low-level modules, they should rely on abstract.

This may sound makes big head, but it is easy to understand. This principle can be well decoupled, for example seems to be the best way to explain this principle:

class PasswordReminder {
    private $dbConnection;

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

First MySQLConnection low level modules, PasswordReminder high-grade module, but according SOLID in D explained: depend on abstractions without depending on the implementation, the above code snippet contrary to this principle, because PasswordReminder class is forced to rely on MySQLConnection class.

Later, if you want to modify the database driver, you have to modify PasswordReminder class, and therefore contrary to the opening and closing principle (Open-use Close Principle) .

This PasswordReminder class should not be concerned about what your application uses a database, in order to further address this issue, we "the interface for writing code", due to the high-grade and low-grade module should depend on abstractions, we can create an interface:

interface DBConnectionInterface {
    public function connect();
}

This interface has a method of connecting to the database, MySQLConnection class that implements this interface, in PasswordReminder construction methods do not directly type constraint to MySQLConnection class, but the class is set to the interface, so no matter what your type of database applications, PasswordReminder class can be connected to the database without any problems, and without departing from the shutter principle .

class MySQLConnection implements DBConnectionInterface {
    public function connect() {
        return "Database connection";
    }
}

class PasswordReminder {
    private $dbConnection;

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

From the above piece of code, you can now see the high-grade and low-grade modules are dependent on the abstract.

to sum up


To be honest, SOLID initially seem difficult to grasp, but as long as the constant use and compliance with its principles, it will become part of you, to make your code easily extend, modify, test, even though the reconstruction is not prone to problems.

Guess you like

Origin www.cnblogs.com/Cecil_1995/p/10950617.html