Hướng dẫn php được bao bởi

Xin chào mọi người, đây sẽ là bài viết cuối cùng trong các bài viết về Lập trình hướng đối tượng trong PHP. Ở 2 phần trước mình đã nói tổng quan về lập trình hướng đối tượng trong PHP. Bài viết này mình sẽ nói đến chuẩn PSR và SOLID. Đây là những chuẩn về coding convention trong lập trình PHP. Mong rằng mọi người có thể hiểu và áp dụng chúng vào những dòng code của mình.

PSR - PHP Standard Recommendation

PSR là 1 đặc tả PHP được xuất bản bởi PHP Framework Interop Group. Nó đưa ra các tiêu chuẩn để chuẩn hóa các khái niệm trong lập trình PHP. Tiêu chuẩn này được các lập trình viên, tổ chức chấp nhận sử dụng.

Lý do bạn nên viết code theo chuẩn PSR:

  1. Là tiêu chuẩn được áp dụng vào các dự án lớn hoặc framework PHP
  2. Việc viết code chuẩn giúp không chỉ có bạn mà người đọc code, hay người sẽ tiếp tục tham gia vào dự án, bảo trì có thể hiểu code hơn
  3. Có sự thống nhất trong cách thức viết code, tổ chức các class...
  4. Dễ dàng trong đọc, hiểu code

PSR-2: Coding Style Guide

Tiêu chuẩn này thừa kế và mở rộng của tiêu chuẩn PSR-1. Tiêu chuẩn này là về 1 số quy tắc cú pháp trong code.

  • Code PHẢI tuân thủ PSR-1
  • Code PHẢI sử dụng 4 khoảng trắng thay vì tab để lùi vào
  • Không có giới hạn cứng về độ dài của dòng, giới hạn mềm PHẢI là 120 ký tự, dòng NÊN có 80 ký tự trở xuống
  • PHẢI có 1 dòng trống sau khai báo
    $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(); 
    
    8, và PHẢI có 1 dòng trống sau khổi khai báo
    $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(); 
    
    9
  • Ký tự
    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);
    }
    
    0 mở 1 class PHẢI ở dòng tiếp theo và
    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);
    }
    
    1 đóng class PHẢI ở dòng tiếp theo sau phần nội dung class
  • Ký tự
    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);
    }
    
    0 cho phương thức PHẢI ở dòng tiếp theo và
    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);
    }
    
    1 PHẢI ở dòng tiếp theo sau phần nội dung
  • Các visibility PHẢI được khai báo trên tất cả các thuộc tính và phương thức.
    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);
    }
    
    4 và
    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);
    }
    
    5 phải được khai báo trước visibility,
    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);
    }
    
    6 PHẢI được khai báo sau visibility
  • Các từ khóa của cấu trúc điểu khiển PHẢI có 1 khoảng trắng sau chúng, việc gọi phương thức và hàm thì không phải
  • Ký tự
    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);
    }
    
    0 của các cấu trúc điều khiển PHẢI ở cùng hàng và ký tự
    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);
    }
    
    1 PHẢI ở hàng tiếp theo sau phần nội dung
  • Ký tự
    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);
    }
    
    9 cho các cấu trúc điểu khiển KHÔNG PHẢI có khoảng trắng sau chúng, và ký tự
    class Square {
        public $length;
    
        public function __construct($length)
        {
            $this->length = $length;
        }
    
        public function area()
        {
            return pow($this->length, 2);
        }
    } 
    
    0 KHÔNG PHẢI có khoảng trắng trước nó

PSR-0 và PSR-4: Autoloading Standard

PSR-0

Tiêu chuẩn này đã không còn được chấp nhận từ 21/10/2014 và được chấp nhận thay thế bằng chuẩn PSR-4.

  • Điều kiện đủ của namespace và class phải có cấu trúc như sau:
    class Square {
        public $length;
    
        public function __construct($length)
        {
            $this->length = $length;
        }
    
        public function area()
        {
            return pow($this->length, 2);
        }
    } 
    
    1
  • Mỗi namspace bắt buộ phải có namspace của cấp cao nhất (“Vendor Name”)
  • Mỗi namspace có thể có nhiều namespace con
  • Mỗi dấu phân cách namespace được chuyển đổi thành DIRECTORY_SEPARATOR khi tải từ hệ thống tập tin
  • Mỗi ký tự _ trong tên class được chuyển đổi thành DIRECTORY_SEPARATOR. Ký tự _ không có ý nghĩa đặc biệt trong namespace
  • Một namespace và class tiêu chuẩn cần có hậu tố .php khi tải từ hệ thống tập tin
  • Tên trong Vendor name, namespace, class có thể là tổ hợp của ký tự thường và ký tự hoa

PSR-4

Tiêu chuẩn PSR-4 mô tả các đặc điểm kỹ thuật cho việc tự động tải các class từ đường dẫn tập tin. Nó hoàn toàn tương thích và có thể được sử dụng ngoài bất kỳ mục nào khác của đặc tả tự động tải, bao gồm PSR-0. PSR này cũng mô tả nơi để đặt các tệp sẽ được tự động tri theo đặc điểm kỹ thuật.

  • Thuật ngữ "class" đề cập đến các class, interface, trait và các loại cấu trúc tương tự khác

  • Tên class tiêu chuẩn theo mẫu:

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

    • Tên class tiêu chuẩn PHẢI có namspace cấp cao nhất, có thể hiểu là vendor namespace
    • Tên class tiêu chuẩn CÓ THỂ có 1 hoặc nhiều namespace con
    • Tên class tiêu chuẩn PHẢI có 1 tên class kết thúc
    • Dấu gạch dưới không có ý nghĩa đặc biệt trong bất kỳ phần nào của tên class tiêu chuẩn
    • Ký tự trong tên class tiêu chuẩn CÓ THỂ kết hợp của ký tự thường và ký tự hoa
    • Tất cả các tên class PHẢI được tham chiếu trong trường hợp phù hợp
  • Khi tải 1 tệp tương ứng với tên đẩy đủ của class

    • 1 chuỗi liên tiếp của 1 hoặc nhiều namespace dẫn đầu và namespace con, không bao gồm dấu phân tách namespace dẫn đầu, trong tên lớp đạt đầy đủ điều kiện tương ứng với ít nhất 1 "thư mục cơ sở"
    • Các namespace con liền kề sau "namespace prefix" tương ứng với thư mục con trong "thư mục cơ sở", trong đó bộ phân tách namespace đại diện cho thư mục phân tách. Tên thư mục con PHẢI khớp với trường hợp của các namespace phụ
    • Tên lớp kết thúc tương ứng với tên tệp kết thúc bằng
      class Square {
          public $length;
      
          public function __construct($length)
          {
              $this->length = $length;
          }
      
          public function area()
          {
              return pow($this->length, 2);
          }
      } 
      
      3
  • Việc triển khai trình tự động tải KHÔNG ĐƯỢC ném (throw) các ngoại lệ, KHÔNG ĐƯỢC sinh thêm lỗi ở bất kỳ cấp độ nào và KHÔNG NÊN trả về giá trị

Nguyên tắc thiết kế hướng đối tượng S.O.L.I.D

S.O.L.I.D là 1 từ viết tắt cho 5 nguyên tắc thiết kế hướng đối tượng đầu tiên (OOD) của Robert C. Martin. Những nguyên tắc này khi kết hợp với nhau giúp cho người lập trình dễ dàng phát triển phần mềm cũng như dễ cho việc bảo trì và mở rộng.

Vì mình cũng mới tìm hiểu phẩn này nên kiến thức có thể không chắc chắn ở 1 số phần. Nội dung sau đây mình lấy từ đây. Các bạn có thể tham khảo kỹ hơn.

S - Single reponsibity principle: Nguyên tắc đơn nhiệm

1 lớp nên có 1 và chỉ 1 lý do để thay đổi, có nghĩa là 1 lớp chỉ nên có 1 công việc.

Ví dụ, chúng ta có 1 vài hình và cần tính tổng diện tích các hình.

class Circle {
    public $radius;

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

class Square {
    public $length;

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

Đầu tiên, chúng ta tạo các class hình và có các

class Square {
    public $length;

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

    public function area()
    {
        return pow($this->length, 2);
    }
} 
4 thiết lập các thông số cần thiết. Tiếp theo, chúng ta tạo class
class Square {
    public $length;

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

    public function area()
    {
        return pow($this->length, 2);
    }
} 
5 và viết logic để tính tổng diện tích các hình.

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(),
            ""
        ));
    }
}

Để sử dụng lớp AreaCalculator, chúng ta chỉ đơn giản là khởi tạo lớp và truyền vào 1 mảng các hình và hiển thị đầu ra.

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

$areas = new AreaCalculator($shapes);

echo $areas->output();

Vấn đề với phương thức

class Square {
    public $length;

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

    public function area()
    {
        return pow($this->length, 2);
    }
} 
6 là lớp AreaCalculator xử lý logic để xuất dữ liệu. Vì vậy, nếu người dùng muốn xuất dữ liệu như JSON hay 1 dạng gì khác thì sao?

Tất cả các logic đó sẽ được xử lý bởi lớp Tất cả các logic đó sẽ được xử lý bởi lớp AreaCalculator, đây là không phù hợp với nguyên tắc đơn nhiệm. Lớp AreaCalculator nên chỉ tính tổng diện tích được cung cấp bởi các hình. Nó không nên quan tâm đến việc người dùng muốn trả về JSON hay HTML.

Vì vậy để giải quyết vấn đề chúng ta có thể taọ lớp SumCalculatorOutputter và sử dụng nó để giải quyết bất kỳ vấn để gì về logic mà bạn bạn cần để xử lý cách tính tổng diện tích của tất cả các hình được cung cấp.

Lớp SumCalculatorOutputter sẽ làm việc như sau:

$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(); 

O - Open - closed principle: Nguyên tắc mở - đóng

Các đối tượng hoặc thực thể nên được mở để mở rộng, nhưng đóng để sửa đổi.

Điều này đơn giản có nghĩa là 1 lớp nên dễ dàng để được mở rộng mà không sửa đổi chính lớp đó. Chúng ta xem xét phương thức

class Square {
    public $length;

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

    public function area()
    {
        return pow($this->length, 2);
    }
} 
7 của lớp AreaCalculator.

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);
}

Nếu chúng ta muốn phương thức

class Square {
    public $length;

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

    public function area()
    {
        return pow($this->length, 2);
    }
} 
7 có thể tính tổng diện tích của 1 vài hình nữa thì chúng ta cần thêm các khối
class Square {
    public $length;

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

    public function area()
    {
        return pow($this->length, 2);
    }
} 
9 và nó đi ngược lại nguyên tắc đóng - mở.

1 cách mà chúng ta có thể làm cho phương thức

class Square {
    public $length;

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

    public function area()
    {
        return pow($this->length, 2);
    }
} 
7 tốt hơn là loại bỏ phần logic tính diện tích của mỗi hình ra khỏi phương thức
class Square {
    public $length;

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

    public function area()
    {
        return pow($this->length, 2);
    }
} 
7 và đính kém nó vào lơp của hình.

class Square {
    public $length;

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

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

Bây giờ, để tính tổng của bất kỳ hình nào chúng ta có thể đơn giản phương thức

class Square {
    public $length;

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

    public function area()
    {
        return pow($this->length, 2);
    }
} 
7 như sau:

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

    return array_sum($area);
}

Bây giờ, chúng ta có thể tạo 1 lớp đại diện cho hình khác và chuyển nó vào khi tính tổng mà không ảnh hưởng đến code. Tuy nhiên, bây giờ có 1 vấn đề khác nảy sinh, làm thế nào mà chúng ta biết rằng đối tượng được truyền vào trong AreaCalculator thực sự là 1 hình hoặc nếu hình có 1 phương thức tên là

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

    return array_sum($area);
}
3?

Việc sử dụng

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

    return array_sum($area);
}
4 là 1 phần không thể thiếu của S.O.L.I.D, chúng ta tạo 1
public function sum()
{
    foreach($this->shapes as $shape) {
        $area[] = $shape->area();
    }

    return array_sum($area);
}
4 để mọi hình có thể
public function sum()
{
    foreach($this->shapes as $shape) {
        $area[] = $shape->area();
    }

    return array_sum($area);
}
6:

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);
    }
} 

Phương thức

class Square {
    public $length;

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

    public function area()
    {
        return pow($this->length, 2);
    }
} 
7 của
class Square {
    public $length;

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

    public function area()
    {
        return pow($this->length, 2);
    }
} 
5 có thể kiểm tra xem các hình được cung cấp có thực sự là trường hợp của
public function sum()
{
    foreach($this->shapes as $shape) {
        $area[] = $shape->area();
    }

    return array_sum($area);
}
9 hay không, nếu không thì chúng ta có thể ném 1 ngoại lệ:

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

        throw new AreaCalculatorInvalidShapeException;
    }

    return array_sum($area);
}   

L: Liskov substitution principle - Nguyên tắc thay thế Liskov

Gọi

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);
    }
} 
0 là 1 thuộc tính có thể chứng minh được về các đối tượng của x của kiểu T. Sau đó,
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);
    }
} 
1 phải được chứng minh cho các đối tượng y kiểu S, trong đó S là 1 kiểu con của T.

Tất cả những điểu trên có nghĩa là tất cả các lơp con / lớp dẫn xuất nên được thay thế cho các lớp cơ sở / lớp cha của chúng.

Vẫn với việc sử dụng lớp

class Square {
    public $length;

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

    public function area()
    {
        return pow($this->length, 2);
    }
} 
5, chúng ta có 1 lớp
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);
    }
} 
3 thừa kế lớp
class Square {
    public $length;

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

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

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);
    }
}

Lớp

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);
    }
} 
5:

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(),
            ""
        ));
    }
}
0

Nếu chúng ta thử chạy ví dụ trên như sau:

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(),
            ""
        ));
    }
}
1

Khi chúng ta gọi phương thức

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);
    }
} 
6 trên đối tượng
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);
    }
} 
7 thì sẽ trả về lỗi E_NOTICE thông báo chuyển đổi mảng sang chuỗi.

Để giải quyết nó, thay vì trả về mảng từ phương thức

class Square {
    public $length;

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

    public function area()
    {
        return pow($this->length, 2);
    }
} 
7 của lớp
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);
    }
} 
3, bạn nên đơn giản nó:

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(),
            ""
        ));
    }
}
2

I: Interface segregation principle - Nguyên tắc giao diện phân biệt

1 khách hàng không bao giờ bị buộc phải

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

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

    return array_sum($area);
}
4 mà nó không sử dụng hoắc các khách hàng không nên bị phụ thuộc vào các phương thức mà họ không sử dụng.

Vẫn với ví dụ trên, chúng ta biết rằng chúng ta cũng có hình dạng rắn nên chúng ta có thể muốn tính toán khối lượng của hình khối, chúng ta có thể thêm 1 "hợp đồng - contract" vào

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

    return array_sum($area);
}
9:

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(),
            ""
        ));
    }
}
3

Bất kỳ hình nào chúng ta tạo bắt buộc phải

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

    return array_sum($area);
}
6 phương thức
public function sum() 
{
    foreach($this->shapes as $shape) {
        if(is_a($shape, 'ShapeInterface')) {
            $area[] = $shape->area();
            continue;
        }

        throw new AreaCalculatorInvalidShapeException;
    }

    return array_sum($area);
}   
4, nhưng chúng ta biết rằng hình vuông là hình phẳng và không có khối lượng, vì vậy
public function sum()
{
    foreach($this->shapes as $shape) {
        $area[] = $shape->area();
    }

    return array_sum($area);
}
4 này sẽ bắt buộc lớp
public function sum() 
{
    foreach($this->shapes as $shape) {
        if(is_a($shape, 'ShapeInterface')) {
            $area[] = $shape->area();
            continue;
        }

        throw new AreaCalculatorInvalidShapeException;
    }

    return array_sum($area);
}   
6 thực hiện 1 phương thức mà nó không sử dụng.

Nguyên tắc này không cho phép thực hiện như vậy, thay vào đó bạn có thể tạo 1 giao diện khác gọi là

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

        throw new AreaCalculatorInvalidShapeException;
    }

    return array_sum($area);
}   
7 có hợp đồng là
public function sum() 
{
    foreach($this->shapes as $shape) {
        if(is_a($shape, 'ShapeInterface')) {
            $area[] = $shape->area();
            continue;
        }

        throw new AreaCalculatorInvalidShapeException;
    }

    return array_sum($area);
}   
4 và các hình dạng rắn như hình khối có thể triển khai giao diện này:

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(),
            ""
        ));
    }
}
4

Đây là 1 cách giải quyết tương đối tốt, nhưng đáng buồn là để xem khi loại gợi ý các giao diện này, thay vì sử dụng 1

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

    return array_sum($area);
}
9 hoặc 1
public function sum() 
{
    foreach($this->shapes as $shape) {
        if(is_a($shape, 'ShapeInterface')) {
            $area[] = $shape->area();
            continue;
        }

        throw new AreaCalculatorInvalidShapeException;
    }

    return array_sum($area);
}   
7.

Bạn có thể tạo 1 giao diện khác, ví dụ là

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);
    }
}
1 và
public function sum()
{
    foreach($this->shapes as $shape) {
        $area[] = $shape->area();
    }

    return array_sum($area);
}
6 nó trong cả các lớp hình phẳng và hình khối rắn. Đây là cách mà bạn có thể dễ dàng thấy rằng nó có 1 API duy nhất để quản lý các hình.

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(),
            ""
        ));
    }
}
5

Bây giờ, trong lớp

class Square {
    public $length;

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

    public function area()
    {
        return pow($this->length, 2);
    }
} 
5, chúng ta có thể dễ dàng thay thế việc gọi đến phương thức
public function sum()
{
    foreach($this->shapes as $shape) {
        $area[] = $shape->area();
    }

    return array_sum($area);
}
3 với
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);
    }
}
5 và cũng kiểm tra nếu đối tượng là 1 thể hiện của
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);
    }
}
1 và không phải là
public function sum()
{
    foreach($this->shapes as $shape) {
        $area[] = $shape->area();
    }

    return array_sum($area);
}
9.

D: Dependency inversion principle - Nguyên tắc nghịch đảo phụ thuộc

Các thực thể phải phụ thuộc vào sự trừu tượng hóa không phải là các concretion. Nó nói rằng mô-đun mức cao không được phụ thuộc vào mô-đun mức thấp, nhưng chúng phải phụ thuộc vào sự trừu tượng hóa.

Điều này có vẻ cồng kềnh, nhưng nó thực sự rất dễ hiểu. Nguyên tắc này cho phép tách rời.

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(),
            ""
        ));
    }
}
6

Đầu tiên,

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);
    }
}
8 là mô-đun cấp thấp trong khi
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);
    }
}
9 là mô-đun cấp cao, nhưng theo định nghĩa của D trong S.O.L.I.D nói rằng phụ thuộc vào trừu tượng hóa không trên concretion, đoạn mã trên vi phạm nguyên tắc này đối với lớp
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);
    }
}
9 bị buộc phụ thuộc vào lớp
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);
    }
}
8.

Sau này, nếu bạn muốn thay đổi cơ sở dữ liệu, bạn cũng sẽ phải chỉnh sửa lớp

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);
    }
}
9 và vi phạm nguyên tắc O.

Lớp

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);
    }
}
9 không nên quan tâm đến cơ sở dữ liệu mà ứng dụng đang sử dụng, để giải quyết vấn đề chúng ta "code vào 1 giao diện", vì các mô-đun mức cap và mức thấp nên phụ thuộc vào sự trừu tượng, chúng ta có thể tạo ra 1 giao diện như sau:

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(),
            ""
        ));
    }
}
7

Giao diện này có phương thức

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(),
            ""
        ));
    }
}
04 và lớp
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);
    }
}
8 thực thi giao diện này. Cũng thay vì trực tiếp lớp
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);
    }
}
8 trong
class Square {
    public $length;

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

    public function area()
    {
        return pow($this->length, 2);
    }
} 
4 của
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);
    }
}
9. Thay vào đó, chúng ta gợi ý giao diện và bất kể loại cơ sở dữ liệu nào mà ứng dụng của bạn đang sử dụng, lớp
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);
    }
}
9 có thể dễ dàng kết nối đến cơ sở dữ liệu mà không gặp bất kỳ vấn đề nào và nguyên lý O không bị vi phạm.

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(),
            ""
        ));
    }
}
8

Thông qua đoạn mã ở trên, bây giờ bạn có thể thấy rằng cả mô-đun cấp cao và cấp thấp để phụ thuộc vào sự trừu tượng.

Kết luận

Phần cuối này, bản thân mình tìm hiểu về nguyên tắc SOLID cũng đang vẫn tương đối mơ hồ. Mong rằng, chúng ta có thêm nhiều thời gian để cọ xát với các dự án thực tế để nắm bắt cũng như hiểu rõ thêm. Mình cũng xin cảm ơn nếu bạn nào cũng có lỡ đọc cả 3 bài về Lập trình hướng đối tượng trong PHP của mình. Bài viết còn tương đối thiếu sót do kiến thức của bản thân. Mong rằng những góp ý của các bạn giúp mình có thể tiến bộ hơn (bowbowbow).