Hai lý do chính chống lại việc sử dụng các phương thức tĩnh là:
- mã bằng các phương thức tĩnh rất khó kiểm tra
- mã sử dụng các phương thức tĩnh khó mở rộng
Có một cuộc gọi phương thức tĩnh bên trong một số phương thức khác thực sự tồi tệ hơn việc nhập một biến toàn cục. Trong PHP, các lớp là các ký hiệu toàn cục, vì vậy mỗi khi bạn gọi một phương thức tĩnh, bạn dựa vào một ký hiệu toàn cục (tên lớp). Đây là một trường hợp khi toàn cầu là xấu xa. Tôi gặp vấn đề với cách tiếp cận này với một số thành phần của Zend Framework. Có các lớp sử dụng các cuộc gọi phương thức tĩnh (nhà máy) để xây dựng các đối tượng. Tôi không thể cung cấp một nhà máy khác cho trường hợp đó để lấy lại một đối tượng tùy chỉnh. Giải pháp cho vấn đề này là chỉ sử dụng các thể hiện và phương pháp instace và thực thi các singletons và tương tự khi bắt đầu chương trình.
Miško Hevery , người làm việc như một Huấn luyện viên Agile tại Google, có một lý thuyết thú vị, hay nói đúng hơn là, chúng ta nên tách thời gian tạo đối tượng khỏi thời gian chúng ta sử dụng đối tượng. Vì vậy, vòng đời của một chương trình được chia làm hai. Phần đầu tiên ( main()
phương thức giả sử), sẽ xử lý tất cả các hệ thống dây đối tượng trong ứng dụng của bạn và phần thực hiện công việc thực tế.
Vì vậy, thay vì có:
class HttpClient
{
public function request()
{
return HttpResponse::build();
}
}
Chúng ta nên làm:
class HttpClient
{
private $httpResponseFactory;
public function __construct($httpResponseFactory)
{
$this->httpResponseFactory = $httpResponseFactory;
}
public function request()
{
return $this->httpResponseFactory->build();
}
}
Và sau đó, trong trang chỉ mục / trang chính, chúng tôi sẽ làm (đây là bước nối dây đối tượng hoặc thời gian để tạo biểu đồ các trường hợp sẽ được chương trình sử dụng):
$httpResponseFactory = new HttpResponseFactory;
$httpClient = new HttpClient($httpResponseFactory);
$httpResponse = $httpClient->request();
Ý tưởng chính là tách các phần phụ thuộc ra khỏi các lớp của bạn. Bằng cách này, mã có thể mở rộng hơn nhiều và, phần quan trọng nhất đối với tôi, có thể kiểm chứng được. Tại sao nó quan trọng hơn để được kiểm tra? Bởi vì tôi không phải lúc nào cũng viết mã thư viện, nên khả năng mở rộng không quan trọng lắm, nhưng khả năng kiểm tra rất quan trọng khi tôi thực hiện tái cấu trúc. Dù sao, mã có thể kiểm tra thường mang lại mã mở rộng, vì vậy nó không thực sự là một tình huống hoặc.
Miško Hevery cũng phân biệt rõ ràng giữa singletons và Singletons (có hoặc không có chữ S viết hoa). Sự khác biệt rất đơn giản. Singletons với chữ "s" chữ thường được thi hành bằng cách nối dây trong chỉ mục / chính. Bạn khởi tạo một đối tượng của một lớp không triển khai mẫu Singleton và lưu ý rằng bạn chỉ chuyển thể hiện đó cho bất kỳ trường hợp nào khác cần nó. Mặt khác, Singleton, với chữ "S" viết hoa là một triển khai của mẫu (chống) cổ điển. Về cơ bản là một sự ngụy trang toàn cầu không được sử dụng nhiều trong thế giới PHP. Tôi đã không nhìn thấy một cho đến thời điểm này. Nếu bạn muốn một kết nối DB duy nhất được sử dụng bởi tất cả các lớp của bạn thì tốt hơn là làm như thế này:
$db = new DbConnection;
$users = new UserCollection($db);
$posts = new PostCollection($db);
$comments = new CommentsCollection($db);
Bằng cách làm ở trên, rõ ràng rằng chúng tôi có một người độc thân và chúng tôi cũng có một cách hay để tiêm giả hoặc sơ khai trong các bài kiểm tra của chúng tôi. Thật đáng ngạc nhiên khi các bài kiểm tra đơn vị dẫn đến một thiết kế tốt hơn. Nhưng nó rất có ý nghĩa khi bạn nghĩ rằng các bài kiểm tra buộc bạn phải suy nghĩ về cách bạn sử dụng mã đó.
/**
* An example of a test using PHPUnit. The point is to see how easy it is to
* pass the UserCollection constructor an alternative implementation of
* DbCollection.
*/
class UserCollection extends PHPUnit_Framework_TestCase
{
public function testGetAllComments()
{
$mockedMethods = array('query');
$dbMock = $this->getMock('DbConnection', $mockedMethods);
$dbMock->expects($this->any())
->method('query')
->will($this->returnValue(array('John', 'George')));
$userCollection = new UserCollection($dbMock);
$allUsers = $userCollection->getAll();
$this->assertEquals(array('John', 'George'), $allUsers);
}
}
Tình huống duy nhất mà tôi sử dụng (và tôi đã sử dụng chúng để bắt chước đối tượng nguyên mẫu JavaScript trong PHP 5.3) là khi tôi biết rằng trường tương ứng sẽ có cùng thể hiện giá trị chéo. Tại thời điểm đó, bạn có thể sử dụng một thuộc tính tĩnh và có thể là một cặp phương thức getter / setter tĩnh. Dù sao, đừng quên thêm khả năng ghi đè thành viên tĩnh bằng một thành viên thể hiện. Ví dụ, Zend Framework đã sử dụng một thuộc tính tĩnh để chỉ định tên của lớp bộ điều hợp DB được sử dụng trong các trường hợp của Zend_Db_Table
. Đã được một lúc kể từ khi tôi sử dụng chúng vì vậy nó có thể không còn phù hợp nữa, nhưng đó là cách tôi nhớ nó.
Các phương thức tĩnh không xử lý các thuộc tính tĩnh phải là các hàm. PHP có các chức năng và chúng ta nên sử dụng chúng.