2011-01-05 25 views
5

Viết đầu tiên thực PHP lớp của tôi và tình cờ gặp một vấn đề, sau đây là phương pháp __construct tôi:Hợp lệ hóa __construct, đảm bảo vars của của đúng loại

public function __construct($foo,$bar) { 
     $this->foo = $foo; 
     $this->bar = $bar; 
} 

Cả $foo$bar được yêu cầu, nếu không có họ, các phương pháp sẽ thất bại và cung cấp cho các lỗi php. OK khi chúng không được xác định khi đối tượng được khởi tạo:

$var = new Class(); 

Vì điều này sẽ gây ra lỗi (ví dụ: Lớp yêu cầu 2 thông số không được đặt). Nhưng nếu chúng được đặt nhưng không phải là loại dữ liệu chính xác như vậy:

$var = new Class('33','ddd'); 

Phương pháp của tôi vẫn sẽ thử và chạy sai biến, sau đó in ra lỗi.

Thực tiễn tốt nhất để xác thực những điều này là gì? Trong cấu trúc hoặc trong mỗi phương pháp?

Giải pháp của tôi mà tôi đang sử dụng ngay bây giờ làm việc, nhưng tôi không chắc chắn nếu đó là cách 'đúng':

//$foo needs to be a string with letters only 
//$bar needs to be an integer 
public function __construct($foo,$bar) { 
     $this->foo = $foo; 
     $this->bar = $bar; 
     if(!is_numeric($bar)){ 
     //print error msg 
     } 
     elseif(other validation case) 
     etc... 
} 

Các khái niệm về lập trình OO là khá mới mẻ với tôi, vì vậy liên kết bất kỳ tài liệu tham chiếu nào bạn đã sử dụng sẽ được đánh giá cao.

+1

bạn không nên "thông báo lỗi print" trong một nhà xây dựng, bạn nên ném một ngoại lệ (như trong @ Gordon của câu trả lời dưới đây). Mục đích của nhà xây dựng là có một đối tượng hợp lệ sau khi nó được gọi. Bởi chỉ cần in một thông báo lỗi thay vì một ngoại lệ, bạn sẽ rất có thể kết thúc với một đối tượng không hợp lệ sẽ vít lên toàn bộ chương trình của bạn. – netcoder

+0

Không, bạn không nên sử dụng ngoại lệ. Sử dụng 'assert()' để kiểm tra các kiểu biến. Đối số không hợp lệ là vấn đề phát triển quan trọng nhất. Đây là bạn muốn bắt những vấn đề như vậy. Các ngoại lệ nên được sử dụng khi các đối số không hợp lệ được dự kiến ​​trong thời gian chạy và nếu bạn có thể triển khai logic bắt logic hợp lý để thử lại với các đối số thay thế. Ví dụ. [Assert vs Exceptions] (http://stackoverflow.com/questions/117171/design-by-contract-tests-by-assert-or-by-exception/117247#117247) nhưng cũng thấy các bản sao .. – mario

+0

@mario: Tôi đồng ý rằng 'khẳng định' có thể được sử dụng để kiểm tra các loại nội bộ. Nhưng không phải trong một nhà xây dựng. Một đối số xấu được truyền cho một hàm tạo và nó là một ngoại lệ, một ngoại lệ là một tình huống đặc biệt mà chương trình không thể tiếp tục được nữa. Nếu một hàm tạo có các đối số sai được truyền cho nó, thì nó sẽ không tiếp tục. Nếu không, như tôi đã nói, bạn kết thúc với một đối tượng không hợp lệ và một chương trình hoạt động mà lấy đối tượng là hợp lệ, và tiếp tục đi cho đến khi một số trục vít lớn xảy ra (rác được chèn liên tục trong DB của bạn cho mỗi ví dụ). – netcoder

Trả lời

18

tôi có lẽ sẽ làm một cái gì đó như thế này để ngăn chặn lộn xộn bên trong ctor và cho phép các lớp để thiết lập chúng giá trị trong nội bộ:

class MyClass … 

    protected $_foo; 

    /** 
    * @param String $foo String with letters only 
    * @param Integer $bar Any Integer 
    * @return void 
    * @throws InvalidArgumentException when $foo is not letters only 
    * @throws InvalidArgumentException when $bar is not an Integer 
    */ 
    public function __construct($foo, $bar) 
    { 
     $this->_setFoo($foo); 
     $this->_setBar($bar) 
    } 

    /** 
    * @param String $foo String with letters only 
    * @return void 
    * @throws InvalidArgumentException when String is not letters only 
    */ 
    protected function _setFoo($foo) 
    { 
     if (FALSE === $this->_consistsOfLettersOnly($foo)) { 
      throw new InvalidArgumentException(
       '$foo should consists of letters only' 
      ); 
     } 
     $this->_foo = $foo; 
    } 

    … 

này có lợi thế thêm rằng nếu bạn cần phải công khai lộ setters sau đó, bạn chỉ cần thay đổi từ khóa hiển thị.

Việc xác thực thành phương pháp riêng của nó là không cần thiết, nhưng tôi nghĩ nó làm cho mã dễ đọc hơn. Nếu bạn tìm thấy một thuộc tính khác trong lớp đó cần xác nhận giống nhau, bạn cũng có thể sử dụng lại nó dễ dàng hơn.

+0

+1 cho InvalidArgumentException. – netcoder

+2

Cảm ơn bạn đã giải thích và cập nhật câu trả lời của bạn. – Daniel

+0

Tuyệt vời Gordon, cảm ơn! – Jimbo

2

Xác thực các loại trong hàm tạo (theo ví dụ của bạn) có vẻ là lựa chọn hợp lý đối với tôi, mặc dù hơi lạ nếu đối số là tùy chọn. Có lẽ bạn nên thiết lập giá trị mặc định an toàn nếu đây là trường hợp qua ...

public function __construct($foo=null, $bar=null)

Điều đó nói rằng, bạn có thể sử dụng type hinting cho các đối số trong PHP, nhưng tôi tin rằng điều này chỉ làm cho các mảng và các lớp học. (Không ngạc nhiên, vì không có thứ gì như một số nguyên hay chuỗi ký tự trong PHP.)

Như những người khác đã nói, bạn cũng nên đảm bảo rằng bạn xác thực trong bất kỳ trình cài đặt nào bạn có hoặc (tốt hơn) các setters từ bên trong constructor để đảm bảo không có sự trùng lặp mã.

+0

Tôi thấy, nếu chúng không được đưa ra mặc định trong __contstruct, chúng được phân loại là tùy chọn? – Daniel

+0

@Daniel - Không, nó chỉ có nghĩa là chúng đã được đặt thành mặc định "đã biết". Như vậy, bạn có thể chỉ cần kiểm tra đối với null trong mã xác nhận của trình thiết lập của bạn. –

0

Nếu cần $ foo và $ bar, hãy xác thực chúng trong __construct là nơi tốt nhất để làm như vậy (nếu không: sử dụng trình đặt và xác thực ở đó). Nếu bạn làm điều đó trong mọi phương pháp, bạn sẽ chỉ kết thúc với mã bị trùng lặp. Hãy tưởng tượng nếu một kiểu dữ liệu thay đổi, bạn phải thay đổi mọi phương thức ...

Nếu bạn muốn thực sự cầu kỳ, bạn có thể gọi is_numeric trước khi gán thanh $ đến $ this-> thanh này không cần thiết việc kiểm tra giá trị số không thành công.

+0

Không thực sự - nếu có mã trùng lặp trong quá trình xác thực, thì bạn có thể tạo một phương thức riêng khác cho điều đó, có thể được sử dụng bởi những người định cư cần nó. – xil3

0

Cách tốt nhất là sử dụng getter và setter - ví dụ:

private $_foo; 
private $_bar; 

public function __construct($foo,$bar) { 
    $this->setFoo($foo); 
    $this->setBar($bar); 
} 

public function setFoo($foo) { 
    // validate here 

    $this->_foo = $foo; 
} 

public function getFoo() { 
    return $this->_foo; 
} 

... 

sạch hơn nhiều theo cách này ...

+0

Phương pháp đó bắt nguồn từ Java. Nhưng không phải để thực thi các loại hoặc ràng buộc giá trị. [Tại sao phương pháp getter và setter là xấu] (http://www.javaworld.com/javaworld/jw-09-2003/jw-0905-toolbox.html) – mario

0

vấn đề của bạn không bắt nguồn từ thực tế là bạn đang viết đối tượng định hướng mã - đó là vì PHP là loosely typed language. Điều này có nghĩa là các biến có thể chứa bất kỳ loại dữ liệu nào, từ int đến Object. Bạn có thể thực thi một số loại loại đúc như vậy:

$foo = (int)$foo; 
$bar = (str)$bar; 
0

Lập một chút về câu trả lời @ xil3 của tôi muốn nói tất cả Dan đang tìm kiếm/nhu cầu là:

private $_foo; 
private $_bar; 

public function __construct($foo,$bar) { 
    if(is_int($foo)) { 
    $this->setFoo($foo); 
    } 
    if(is_string($bar)) { 
    $this->setBar($bar); 
    } 
} 

public function setFoo(int $foo) { 
    $this->_foo = $foo; 
} 

public function setBar(string $bar) { 
    $this->_foo = $bar; 
} 

... 

Cho phép đối tượng của mình để được instantless instantless mà không gây tử vong, với các ràng buộc kiểu trên các setters.

0

Bạn không nên làm gì ngoài việc đặt giá trị trong hàm tạo. Nếu bạn cần phải xác nhận thông số nhà xây dựng như trong setters (có thể là tư nhân), hoặc sử dụng các tham số kiểu đặc biệt, vì vậy PHP sẽ xác nhận kiểu cho bạn, ví dụ:

__construct(SomeType $foo, AnotherType $bar); 

Ví dụ bạn có thể sử dụng SPL kiểu dữ liệu:

__construct(SplInt $integer, SplString $string); 

Read more about SPL data types

Các vấn đề liên quan