Getter và Setter?


203

Tôi không phải là nhà phát triển PHP, vì vậy tôi tự hỏi liệu trong PHP có phổ biến hơn để sử dụng getter / setters rõ ràng, theo kiểu OOP thuần túy, với các trường riêng tư (theo cách tôi thích):

class MyClass {
    private $firstField;
    private $secondField;

    public function getFirstField() {
        return $this->firstField;
    }
    public function setFirstField($x) {
        $this->firstField = $x;
    }
    public function getSecondField() {
        return $this->secondField;
    }
    public function setSecondField($x) {
        $this->secondField = $x;
    }
}

hoặc chỉ các lĩnh vực công cộng:

class MyClass {
    public $firstField;
    public $secondField;
}

Cảm ơn


7
Sau khi thử một số mã từ các câu trả lời, tôi đã sử dụng mã bạn đang sử dụng trong câu hỏi. Thật đáng buồn :-(
sumid 16/2/13

9
PHPstorm ... tạo> getters và setters. == giành chiến thắng
DevDonkey 14/07/2015

@DevDonkey Không phải là một chiến thắng nào cả. Để giữ dữ liệu có cấu trúc, thay vào đó hãy sử dụng mảng. @: Đánh dấu Đây không phải là những gì đối tượng đang hoặc là cho. Getters và setters là xấu xa: yegor256.com/2014/09/16/getters-and-setters-are-evil.html
Kubo2

Câu trả lời:


222

Bạn có thể sử dụng các phương thức ma thuật php __get__set.

<?php
class MyClass {
  private $firstField;
  private $secondField;

  public function __get($property) {
    if (property_exists($this, $property)) {
      return $this->$property;
    }
  }

  public function __set($property, $value) {
    if (property_exists($this, $property)) {
      $this->$property = $value;
    }

    return $this;
  }
}
?>

15
Tôi nghĩ bạn có ý nghĩa __get__set. Có hai dấu gạch dưới, không phải một. Đây là liên kết trực tiếp đến phần bên phải của trang: php.net/manual/en/ Kẻ (+1 để có câu trả lời đúng)
Computerish

28
Lợi ích chống lại publictài sản là gì, nếu không có xác nhận / vệ sinh?
KingCrunch

7
@KingCrunch, đây chỉ là một ví dụ. Một ví dụ rất rất giả cho một tài nguyên mạnh mẽ.
Davis Peixoto

10
Đó không thực sự là setter và getter. Thông thường tôi cần cho mỗi tài sản thực hiện khác nhau của getter!
sumid

79
Xin đừng: Với các phương thức ma thuật, bạn sẽ LO LẮNG hầu hết mọi tính năng liên quan đến chất lượng, trong nhiều IDE (thậm chí vim): tự động hoàn thành, kế thừa PHP rõ ràng, diễn giải PHP nhanh và tạo ra & sản xuất PHPDoc nhanh chóng. xem stackoverflow.com/a/6184893/490589
Ronan

113

Tại sao nên sử dụng getters và setters?

  1. Khả năng mở rộng : Dễ dàng cấu trúc lại một getter hơn là tìm kiếm tất cả các phép gán var trong mã dự án.
  2. Gỡ lỗi : Bạn có thể đặt các điểm dừng tại setters và getters.
  3. Trình dọn dẹp : Các hàm ma thuật không phải là giải pháp tốt để ghi ít hơn, IDE của bạn sẽ không đề xuất mã. Sử dụng tốt hơn các mẫu cho getters viết nhanh.

chuyển nhượng trực tiếp và getters / setters


7
Nếu bạn sử dụng @property, IDE của bạn sẽ đề xuất mã (được thử nghiệm với PhpStorm 7)
Alex2php

41

Google đã xuất bản một hướng dẫn về tối ưu hóa PHP và kết luận là:

Không có getter và setter Tối ưu hóa PHP

Và không, bạn không được sử dụng các phương pháp ma thuật . Đối với PHP, Phương thức ma thuật là xấu xa. Tại sao?

  1. Họ rất khó để gỡ lỗi.
  2. Có một tác động hiệu quả tiêu cực.
  3. Họ yêu cầu viết thêm mã.

PHP không phải là Java, C ++ hoặc C #. PHP là khác nhau và chơi với các vai trò khác nhau.


10
Tôi có xu hướng đồng ý với ý kiến ​​đó; đó $dog->name = 'fido'là tốt hơn so với $dog->setName('fido'). Khi thực sự làm biến đổi một tài sản (ví dụ: $dog->increaseAge(1)tôi có thể xây dựng phương thức xác thực cần thiết và làm biến đổi tài sản đó. Nhưng không phải tất cả các hành động đều thực sự đòi hỏi sự đột biến theo nghĩa đó.
Charlie Schliesser

11
Bài báo không nói " không ", nó nói 'setters và getters ngây thơ'.
Brett Santore


13
Thật an toàn khi giả định rằng một bài viết được viết bởi Google có tiêu đề "mẹo hiệu suất PHP" KHÔNG nhằm mục đích đề xuất phong cách mã hóa tốt, nhưng thực thi mã nhanh. Chương liên quan đến setters và getters được dán nhãn "Tránh viết setters và getters ngây thơ", và ví dụ mã chính xác là: Naive. Nó thiết lập và chỉ là một biến mà không có bất kỳ xác nhận. Loại setter / getter NÀY là vô dụng. Việc xác thực bên trong một setter (chỉ cần sử dụng một gợi ý kiểu cho đối số phương thức) sẽ làm cho setters / getters trở nên hữu ích bởi vì bây giờ mã của bạn BIẾT những gì nó đang xử lý.
Sven

3
điều này giống như nói rằng phong cách nội tuyến là tốt hơn. Tất nhiên hiệu suất là tốt hơn, nhưng nó là mã tốt hơn? Dù sao thì tôi cũng không biết các kỹ sư của Google sử dụng php
Claudiu Creanga

13

Đóng gói là quan trọng trong bất kỳ ngôn ngữ OO, sự phổ biến không có gì để làm với nó. Trong các ngôn ngữ được gõ động, như PHP, nó đặc biệt hữu ích vì có rất ít cách để đảm bảo một thuộc tính thuộc loại cụ thể mà không cần sử dụng setters.

Trong PHP, điều này hoạt động:

class Foo {
   public $bar; // should be an integer
}
$foo = new Foo;
$foo->bar = "string";

Trong Java, nó không:

class Foo {
   public int bar;
}
Foo myFoo = new Foo();
myFoo.bar = "string"; // error

Sử dụng các phương thức ma thuật ( __get__set) cũng hoạt động, nhưng chỉ khi truy cập vào một thuộc tính có tầm nhìn thấp hơn phạm vi hiện tại mới có thể truy cập. Nó có thể dễ dàng khiến bạn đau đầu khi cố gắng gỡ lỗi, nếu nó không được sử dụng đúng cách.


7
Getters và setter không mang lại sự đóng gói. Encapsulation == các đối tượng làm một cái gì đó với dữ liệu riêng của mình thay vì đưa chúng ra bên ngoài. Getters và setters không phải là một công cụ để thực thi kiểu trong các ngôn ngữ được gõ động như PHP.
smentek

14
@smentek: Bạn rõ ràng đang thiếu ít nhất một nửa số đóng gói thực sự là gì.
netcoder

2
Là một bản cập nhật cho bất cứ ai đang tìm kiếm, PHP 7.4 sẽ đi kèm với hỗ trợ thuộc tính được gõ. Vì vậy, bạn có thể khai báo $barnhư một intví dụ đầu tiên: wiki.php.net/rfc/typed_properIES_v2
Kevin

7

Nếu bạn muốn sử dụng hàm __call, bạn có thể sử dụng phương thức này. Nó hoạt động với

  • NHẬN => $this->property()
  • THIẾT LẬP => $this->property($value)
  • NHẬN => $this->getProperty()
  • THIẾT LẬP => $this->setProperty($value)

kalsdas

public function __call($name, $arguments) {

    //Getting and setting with $this->property($optional);

    if (property_exists(get_class($this), $name)) {


        //Always set the value if a parameter is passed
        if (count($arguments) == 1) {
            /* set */
            $this->$name = $arguments[0];
        } else if (count($arguments) > 1) {
            throw new \Exception("Setter for $name only accepts one parameter.");
        }

        //Always return the value (Even on the set)
        return $this->$name;
    }

    //If it doesn't chech if its a normal old type setter ot getter
    //Getting and setting with $this->getProperty($optional);
    //Getting and setting with $this->setProperty($optional);
    $prefix = substr($name, 0, 3);
    $property = strtolower($name[3]) . substr($name, 4);
    switch ($prefix) {
        case 'get':
            return $this->$property;
            break;
        case 'set':
            //Always set the value if a parameter is passed
            if (count($arguments) != 1) {
                throw new \Exception("Setter for $name requires exactly one parameter.");
            }
            $this->$property = $arguments[0];
            //Always return the value (Even on the set)
            return $this->$name;
        default:
            throw new \Exception("Property $name doesn't exist.");
            break;
    }
}

2
@ krzysztof-przygoda: "Phương pháp kỳ diệu" này luôn đi kèm với giá cả. Họ phải sử dụng đệ quy property_exists(get_class($this), $name)và đệ quy chậm. Có một cách y để giảm thiểu điều này bằng bộ đệm, nhưng nó vẫn sẽ chậm hơn so với việc tạo các getters và setters bằng tay. Tôi chỉ viết điều này như là một thay thế. Tôi thực sự không khuyên bạn nên sử dụng "Phương pháp ma thuật". Thời gian thêm của việc tạo các getters và setters thường không đáng kể.
J-Rou

7

Ngoài các câu trả lời tuyệt vời và được tôn trọng ở đây, tôi muốn mở rộng trên PHP không có setters / getters.

PHP không có cú pháp getter và setter . Nó cung cấpcác phương thứcphân lớp hoặc ma thuật để cho phép "kết nối" và ghi đè quá trình tra cứu tài sản, như Dave đã chỉ ra.

Magic cho phép chúng tôi lập trình viên lười biếng làm nhiều hơn với ít mã hơn tại thời điểm mà chúng tôi đang tích cực tham gia vào một dự án và biết nó một cách thân mật, nhưng thường phải trả giá bằng khả năng đọc.

Hiệu năng Mọi chức năng không cần thiết, xuất phát từ việc buộc một kiến ​​trúc mã giống như getter / setter trong PHP, liên quan đến khung ngăn xếp bộ nhớ của chính nó khi gọi và đang lãng phí chu kỳ CPU.

Khả năng đọc: Cơ sở mã phát sinh các dòng mã đầy hơi, tác động đến điều hướng mã vì càng nhiều LỘC càng có nghĩa là cuộn nhiều hơn ,.

Sở thích: Cá nhân, theo nguyên tắc thông thường, tôi coi sự thất bại của phân tích mã tĩnh là một dấu hiệu để tránh đi vào con đường ma thuật miễn là lợi ích lâu dài rõ ràng trốn tránh tôi vào thời điểm đó.

Ngụy biện:

Một lập luận phổ biến là khả năng đọc. Ví dụ $someobject->widthdễ đọc hơn $someobject->width(). Tuy nhiên, không giống như một hành tinh circumferencehoặc width, có thể được coi là static, một thể hiện của một đối tượng, như $someobject, đòi hỏi một hàm chiều rộng, có thể thực hiện phép đo chiều rộng của đối tượng.
Do đó, khả năng đọc tăng chủ yếu là do các lược đồ đặt tên quyết đoán chứ không phải bằng cách ẩn hàm đi mà tạo ra một giá trị thuộc tính nhất định.

__get / __set sử dụng:

  • xác nhận trước và vệ sinh trước các giá trị tài sản

  • chuỗi ví dụ

    "
    some {mathsobj1->generatelatex} multi
    line text {mathsobj1->latexoutput}
    with lots of variables for {mathsobj1->generatelatex}
     some reason
    "

    Trong trường hợp này generatelatexsẽ tuân thủ sơ đồ đặt tên của tên hành động + tên phương thức

  • trường hợp đặc biệt, rõ ràng

    $dnastringobj->homeobox($one_rememberable_parameter)->gattaca->findrelated()
    $dnastringobj->homeobox($one_rememberable_parameter)->gttccaatttga->findrelated()

Lưu ý: PHP đã chọn không thực hiện cú pháp getter / setter. Tôi không khẳng định rằng getters / setter nói chung là xấu.


6
class MyClass {
    private $firstField;
    private $secondField;
    private $thirdField;

    public function __get( $name ) {
        if( method_exists( $this , $method = ( 'get' . ucfirst( $name  ) ) ) )
            return $this->$method();
        else
            throw new Exception( 'Can\'t get property ' . $name );
    }

    public function __set( $name , $value ) {
        if( method_exists( $this , $method = ( 'set' . ucfirst( $name  ) ) ) )
            return $this->$method( $value );
        else
            throw new Exception( 'Can\'t set property ' . $name );
    }

    public function __isset( $name )
    {
        return method_exists( $this , 'get' . ucfirst( $name  ) ) 
            || method_exists( $this , 'set' . ucfirst( $name  ) );
    }

    public function getFirstField() {
        return $this->firstField;
    }

    protected function setFirstField($x) {
        $this->firstField = $x;
    }

    private function getSecondField() {
        return $this->secondField;
    }
}

$obj = new MyClass();

echo $obj->firstField; // works
$obj->firstField = 'value'; // works

echo $obj->getFirstField(); // works
$obj->setFirstField( 'value' ); // not works, method is protected

echo $obj->secondField; // works
echo $obj->getSecondField(); // not works, method is private

$obj->secondField = 'value'; // not works, setter not exists

echo $obj->thirdField; // not works, property not exists

isset( $obj->firstField ); // returns true
isset( $obj->secondField ); // returns true
isset( $obj->thirdField ); // returns false

Sẵn sàng!


Quá nhiều nồi hơi. Hãy tưởng tượng công cụ này có trong mỗi lớp. Tránh IMO
DarkNeuron

PHP không hỗ trợ getters và setters cho cùng một lý do bạn đề cập. Bất kỳ việc thực hiện kiểu này đều ảnh hưởng nghiêm trọng đến hiệu suất của tập lệnh phía máy chủ.
tham gia

Tôi nghĩ rằng điều này đi ngược lại các thuộc tính 'riêng tư'. Bạn đóng gói chúng, nhưng cũng cho phép truy cập trực tiếp.
Koray Küpe 18/03/19

@ KorayKüpe chỉ khi getter được xác định. Tôi sử dụng đóng gói này rất nhiều (với nhiều cải tiến) và nó hoạt động hoàn hảo. Bạn cũng có thể mở rộng lớp và sử dụng nó trong tất cả các mã một cách dễ dàng.
joas

5

Vâng, PHP không có phương pháp kỳ diệu __get, __set, __isset& __unset, mà luôn luôn là một sự khởi đầu. Than ôi (có được không?) Thuộc tính OO không chỉ là phương pháp ma thuật. Vấn đề chính với việc triển khai PHP là các phương thức ma thuật được gọi cho tất cả các thuộc tính không thể truy cập. Điều đó có nghĩa là bạn phải Lặp lại chính mình (ví dụ: bằng cách gọi property_exists ()) trong các phương thức ma thuật khi xác định xem tên có thực sự là một thuộc tính của đối tượng của bạn hay không. Và bạn thực sự không thể giải quyết vấn đề chung này với một lớp cơ sở trừ khi tất cả các lớp của bạn kế thừa từ đó. ClassWithProperIES, vì PHP thiếu nhiều kế thừa.

Ngược lại, các lớp kiểu mới của Python cung cấp cho bạn property(), cho phép bạn xác định rõ ràng tất cả các thuộc tính của mình. C # có cú pháp đặc biệt.

http://en.wikipedia.org/wiki/Property_(programming)


1
Gọi property_exists, class_vars hoặc Array_key_exists (nghĩa là kiểm tra xem thuộc tính có thực sự tồn tại không) chỉ là một bước để tránh lỗi nghiêm trọng trong thời gian chạy. Tôi không chắc chắn nếu không tỏ ra khó chịu cũng giống như việc lặp đi lặp lại trong mã hóa.
Davis Peixoto

1
Đủ công bằng. Nhưng trong Python và C # sự lặp lại này là không cần thiết. Tôi nghĩ đó là một thế mạnh.
Emanuel Landeholm

4

Tôi đã thực hiện một thí nghiệm bằng phương pháp ma thuật __call. Không chắc chắn tôi có nên đăng nó không (vì tất cả các cảnh báo "KHÔNG SỬ DỤNG PHƯƠNG PHÁP TẠO" trong các câu trả lời và nhận xét khác) nhưng tôi sẽ để nó ở đây .. chỉ trong trường hợp ai đó thấy nó hữu ích.


public function __call($_name, $_arguments){
    $action  = substr($_name, 0, 4);
    $varName = substr($_name, 4);

    if (isset($this->{$varName})){
        if ($action === "get_") return $this->{$varName};
        if ($action === "set_") $this->{$varName} = $_arguments[0];
    }
}

Chỉ cần thêm phương thức đó ở trên trong lớp của bạn, bây giờ bạn có thể gõ:

class MyClass{
    private foo = "bar";
    private bom = "bim";
    // ...
    // public function __call(){ ... }
    // ...
}
$C = new MyClass();

// as getter
$C->get_foo(); // return "bar"
$C->get_bom(); // return "bim"

// as setter
$C->set_foo("abc"); // set "abc" as new value of foo
$C->set_bom("zam"); // set "zam" as new value of bom


Bằng cách này, bạn có thể nhận / đặt mọi thứ trong lớp nếu nó tồn tại, nếu bạn chỉ cần một vài yếu tố cụ thể, bạn có thể sử dụng "danh sách trắng" làm bộ lọc.

Thí dụ:

private $callWhiteList = array(
    "foo" => "foo",
    "fee" => "fee",
    // ...
);

public function __call($_name, $_arguments){
    $action  = substr($_name, 0, 4);
    $varName = $this->callWhiteList[substr($_name, 4)];

    if (!is_null($varName) && isset($this->{$varName})){
        if ($action === "get_") return $this->{$varName};
        if ($action === "set_") $this->{$varName} = $_arguments[0];
    }
}

Bây giờ bạn chỉ có thể nhận / đặt "foo" và "phí".
Bạn cũng có thể sử dụng "danh sách trắng" đó để gán tên tùy chỉnh để truy cập vào các lọ của bạn.
Ví dụ,

private $callWhiteList = array(
    "myfoo" => "foo",
    "zim" => "bom",
    // ...
);

Với danh sách đó, bây giờ bạn có thể gõ:

class MyClass{
    private foo = "bar";
    private bom = "bim";
    // ...
    // private $callWhiteList = array( ... )
    // public function __call(){ ... }
    // ...
}
$C = new MyClass();

// as getter
$C->get_myfoo(); // return "bar"
$C->get_zim(); // return "bim"

// as setter
$C->set_myfoo("abc"); // set "abc" as new value of foo
$C->set_zim("zam"); // set "zam" as new value of bom

.
.
.
Đó là tất cả.


Doc: __call () được kích hoạt khi gọi các phương thức không thể truy cập trong ngữ cảnh đối tượng.


Vấn đề với tất cả các giải pháp "ma thuật" này là chúng chỉ đơn giản sử dụng các phương pháp ma thuật này bởi vì chúng ở đó, và chúng rất hữu ích vì vấn đề được hỏi là một cách đơn giản, chung chung. Khi bạn rời khỏi cấp độ của vấn đề chung này, bạn sẽ gặp các yêu cầu cụ thể không thể giải quyết bằng một phương pháp ma thuật đơn giản, nhưng sẽ yêu cầu một phương pháp ma thuật rất phức tạp - hoặc các setters và getters được mã hóa riêng lẻ.
Sven

2

Sau khi đọc những lời khuyên khác, tôi muốn nói rằng:

Theo quy tắc GENERIC , bạn sẽ không luôn xác định setters cho TẤT CẢ các thuộc tính, đặc biệt là "nội bộ" (semaphores, cờ nội bộ ...). Các thuộc tính chỉ đọc sẽ không có setters, rõ ràng, vì vậy một số thuộc tính sẽ chỉ có getters; đó là nơi __get () đến để thu nhỏ mã:

  • định nghĩa một __get () (getters toàn cầu ma thuật) cho tất cả các thuộc tính giống nhau,
  • nhóm chúng trong mảng để:
    • họ sẽ chia sẻ các đặc điểm chung: các giá trị tiền tệ sẽ / có thể được định dạng chính xác, ngày tháng trong một bố cục cụ thể (ISO, US, Intl.), v.v.
    • bản thân mã có thể xác minh rằng chỉ các thuộc tính hiện có và được phép đang được đọc bằng phương thức ma thuật này.
    • Bất cứ khi nào bạn cần tạo một thuộc tính tương tự mới, chỉ cần khai báo nó và thêm tên của nó vào mảng thích hợp và thế là xong. Đó là cách NHANH CHÓNG hơn là xác định một getter mới, có lẽ với một số dòng mã được lặp lại nhiều lần trên toàn bộ mã lớp.

Đúng! chúng ta có thể viết một phương thức riêng để thực hiện điều đó, nhưng sau đó, một lần nữa, chúng ta sẽ có NHIỀU phương thức được khai báo (bộ nhớ ++) mà cuối cùng lại gọi một phương thức khác, luôn luôn giống như vậy. Tại sao không viết một phương pháp SINGLE để cai trị tất cả ...? [Vâng! chơi chữ hoàn toàn có chủ đích! :)]

Magic setters cũng có thể đáp ứng CHỈ cho các thuộc tính cụ thể, vì vậy tất cả các thuộc tính loại ngày có thể được sàng lọc dựa trên các giá trị không hợp lệ trong một phương thức. Nếu thuộc tính loại ngày được liệt kê trong một mảng, setters của chúng có thể được xác định dễ dàng. Chỉ là một ví dụ, tất nhiên. Có quá nhiều tình huống.

Về khả năng đọc ... Chà ... Đó là một cuộc tranh luận khác: Tôi không muốn bị ràng buộc với việc sử dụng IDE (thực tế, tôi không sử dụng chúng, họ có xu hướng nói với tôi (và ép buộc tôi) làm thế nào để viết ... và tôi có sở thích của tôi về mã hóa "vẻ đẹp"). Tôi có xu hướng nhất quán về cách đặt tên, vì vậy sử dụng ctags và một vài công cụ hỗ trợ khác là đủ cho tôi ... Dù sao: một khi tất cả các setters và getters ma thuật này được thực hiện, tôi viết các setters khác quá cụ thể hoặc "đặc biệt" để được khái quát trong phương thức __set (). Và nó bao gồm tất cả những gì tôi cần về việc nhận và thiết lập các thuộc tính. Tất nhiên: không phải lúc nào cũng có một điểm chung, hoặc có một vài thuộc tính không đáng để rắc rối khi mã hóa một phương thức ma thuật, và sau đó vẫn là cặp setter / getter truyền thống tốt cũ.

Ngôn ngữ lập trình chỉ là: ngôn ngữ nhân tạo của con người. Vì vậy, mỗi người trong số họ có ngữ điệu hoặc giọng nói, cú pháp và hương vị riêng, vì vậy tôi sẽ không giả vờ viết mã Ruby hoặc Python bằng cách sử dụng cùng một "dấu" so với Java hoặc C #, tôi cũng không viết JavaScript hoặc PHP để giống Perl hoặc SQL ... Sử dụng chúng theo cách chúng được sử dụng.


1

Nói chung, cách đầu tiên là phổ biến hơn vì những người có kiến ​​thức lập trình trước có thể dễ dàng chuyển sang PHP và hoàn thành công việc theo hướng đối tượng. Cách đầu tiên là phổ quát hơn. Lời khuyên của tôi sẽ là gắn bó với những gì đã thử và đúng trên nhiều ngôn ngữ. Sau đó, khi và nếu bạn sử dụng ngôn ngữ khác, bạn sẽ sẵn sàng để hoàn thành một cái gì đó ( thay vì dành thời gian để phát minh lại bánh xe ).


0

Có nhiều cách để tạo mã nguồn trong một quy ước netbeans. Cái này đẹp đấy. Nó làm cho suy nghĩ dễ dàng hơn như vậy === SAI. Chỉ cần sử dụng truyền thống, đặc biệt nếu bạn không chắc chắn một trong những thuộc tính nên được gói gọn và cái nào không. Tôi biết, đó là một boi .... pla ... mã, nhưng để gỡ lỗi và nhiều người khác nghĩ rằng đó là cách tốt hơn, rõ ràng hơn. Đừng dành nhiều thời gian với nghệ thuật làm thế nào để tạo ra getters và setters đơn giản. Bạn không thể thực hiện quá một số mẫu thiết kế như quy tắc demeter, v.v., nếu bạn sử dụng phép thuật. Trong tình huống cụ thể, bạn có thể sử dụng magic_calls hoặc cho các giải pháp nhỏ, nhanh và rõ ràng. Chắc chắn bạn cũng có thể tạo ra các giải pháp cho thiết kế theo cách này, nhưng tại sao làm cho bạn sống khó khăn hơn.


0

Xác thực + Định dạng / Giá trị phái sinh

Setters cho phép bạn xác thực dữ liệu và getters cho phép bạn định dạng hoặc lấy dữ liệu. Các đối tượng cho phép bạn đóng gói dữ liệu và mã xác thực và định dạng của nó thành một gói gọn gàng khuyến khích DRY.

Ví dụ, hãy xem xét lớp đơn giản sau có chứa ngày sinh.

class BirthDate {

    private $birth_date;

    public function getBirthDate($format='Y-m-d') {
        //format $birth_date ...
        //$birth_date = ...
        return $birth_date;
    }

    public function setBirthDate($birth_date) {                   
        //if($birth_date is not valid) throw an exception ...          
        $this->birth_date = $birth_date;
    }

    public function getAge() {
        //calculate age ...
        return $age;
    }

    public function getDaysUntilBirthday() {
        //calculate days until birth days
        return $days;
    }
}

Bạn sẽ muốn xác thực rằng giá trị được đặt là

  • Một ngày hợp lệ
  • Không trong tương lai

Và bạn không muốn thực hiện xác nhận này trên tất cả các ứng dụng của bạn (hoặc trên nhiều ứng dụng cho vấn đề đó). Thay vào đó, việc biến biến thành viên được bảo vệ hoặc riêng tư dễ dàng hơn (để biến setter thành điểm truy cập duy nhất) và xác thực trong setter bởi vì sau đó bạn sẽ biết rằng đối tượng chứa ngày sinh hợp lệ cho dù là phần nào của ứng dụng đối tượng đến từ và nếu bạn muốn thêm xác nhận thì bạn có thể thêm nó vào một nơi duy nhất.

Bạn có thể muốn thêm nhiều trình định dạng hoạt động trên cùng một biến thành viên getAge(), getDaysUntilBirthday()và bạn có thể muốn thực thi một định dạng có thể định cấu hình getBirthDate()tùy thuộc vào miền địa phương. Do đó, tôi thích liên tục truy cập các giá trị thông qua getters thay vì trộn $date->getAge()với $date->birth_date.

getters và setters cũng hữu ích khi bạn mở rộng các đối tượng. Ví dụ: giả sử ứng dụng của bạn cần cho phép ngày sinh hơn 150 năm ở một số nơi nhưng không phải ở những nơi khác. Một cách để giải quyết vấn đề mà không lặp lại bất kỳ mã nào là mở rộng BirthDateđối tượng và đặt xác nhận bổ sung trong setter.

class LivingBirthDate extends BirthDate {

    public function setBirthDate($birth_date) {
        //if $birth_date is greater than 150 years throw an exception
        //else pass to parent's setter
        return parent::setBirthDate($birth_date);
    }
}

Tuy nhiên, nhiều lần bạn cần xác thực các thuộc tính với nhau (và không chỉ riêng lẻ). Tôi nghĩ rằng cho phép "setters" có nghĩa là bạn cũng không nắm bắt được bối cảnh. Xác nhận nên được thực hiện trên cơ sở theo ngữ cảnh. Ngoài ra, bạn sẽ buộc phải kiểm tra một số cờ "isValid" trên mọi phương thức bạn có (và thực hiện xác nhận nếu nó sai). Điều đó đang được nói, setters cung cấp một cách hữu ích để gõ gợi ý, chẳng hạn như khi bạn có một tài sản mà bạn muốn trở thành Đối tượng Giá trị (ví dụ: Tiền).
prograhammer

Tôi không hiểu ý của bạn khi bị buộc phải kiểm tra cờ "isValid"? Nếu cách duy nhất để đặt giá trị là thông qua setters thực hiện xác thực, thì bạn biết dữ liệu hợp lệ bởi thực tế là nó đã được đặt thành công. Nếu bạn cần xác thực các thuộc tính cùng nhau, bạn có thể viết một phương thức xác thực phổ biến mà setters của các thuộc tính đó gọi.
FuzzyTree

Nói rằng bạn có một lớp nhân viên với setHiredsetHireDate. Sẽ không hợp lệ khi để ai đó đặt ngày thuê mà không đặt nhân viên là người được thuê. Nhưng không có cách nào bạn thực thi điều này. Nếu bạn thực thi nó trong một trong những setters này, hơn là bạn đang buộc thứ tự "cài đặt", và điều đó đòi hỏi phải đọc nhiều mã hơn từ nhà phát triển để biết. Sau đó, khi bạn thực hiện một phương thức như $employee->promote($newPosition);bạn phải kiểm tra một cờ để xem xác thực đã được thực hiện hay giả sử nó chưa được thực hiện và thực hiện lại (dự phòng).
prograhammer

Thay vào đó, nắm bắt các tương tác. Có lẽ $employee->updateWorkStatus($hired, $hireDate);hoặc nếu nâng cao hơn $employee->adminUpdate(\Employee\AdminUpdateDTO $dto);. Bây giờ bạn có thể xác nhận trong ngữ cảnh bạn cần và quyết định xem có cần xác thực thêm không.
prograhammer

updateWorkStatus về cơ bản là một hàm setter đặt 2 thay vì 1 giá trị nhưng khái niệm là như nhau. Đó là một cách để làm điều đó nhưng bạn cũng có thể đặt xác thực theo ngữ cảnh trong một phương thức phổ biến chỉ chạy khi tất cả các thuộc tính cần xác thực cùng nhau được đặt, tức là phần xác thực theo ngữ cảnh sẽ được gọi bởi cả setHiredDate và setHired nhưng chỉ chạy nếu cả hai được thuê và ngay lập tức được thuêDate là đúng.
FuzzyTree

0

Bài đăng này không cụ thể về __get__setthay vào __callđó là cùng một ý tưởng ngoại trừ việc gọi phương thức. Theo quy định, tôi tránh xa mọi loại phương thức ma thuật cho phép quá tải vì những lý do được nêu trong các nhận xét và bài đăng TUY NHIÊN , gần đây tôi đã sử dụng API của bên thứ 3 mà tôi sử dụng sử dụng DỊCH VỤ và SUB-DỊCH VỤ :

http://3rdparty.api.com?service=APIService.doActionOne&apikey=12341234

Phần quan trọng của điều này là API này có mọi thứ giống nhau ngoại trừ hành động phụ, trong trường hợp này doActionOne. Ý tưởng là nhà phát triển (bản thân tôi và những người khác sử dụng lớp này) có thể gọi dịch vụ phụ theo tên trái ngược với một cái gì đó như:

$myClass->doAction(array('service'=>'doActionOne','args'=>$args));

Tôi có thể làm thay thế:

 $myClass->doActionOne($args);

Để mã hóa cứng, đây sẽ chỉ là một sự trùng lặp (ví dụ này rất giống với mã):

public function doActionOne($array)
    {
        $this->args     =   $array;
        $name           =   __FUNCTION__;
        $this->response =   $this->executeCoreCall("APIService.{$name}");
    }

public function doActionTwo($array)
    {
        $this->args     =   $array;
        $name           =   __FUNCTION__;
        $this->response =   $this->executeCoreCall("APIService.{$name}");
    }

public function doActionThree($array)
    {
        $this->args     =   $array;
        $name           =   __FUNCTION__;
        $this->response =   $this->executeCoreCall("APIService.{$name}");
    }

protected function executeCoreCall($service)
    {
        $cURL = new \cURL();
        return $cURL->('http://3rdparty.api.com?service='.$service.'&apikey='.$this->api.'&'.http_build_query($this->args))
                    ->getResponse();
    }

Nhưng với phương pháp kỳ diệu của __call()tôi có thể truy cập tất cả các dịch vụ bằng các phương thức động:

public function __call($name, $arguments)
    {
        $this->args     =   $arguments;
        $this->response =   $this->executeCoreCall("APIService.{$name}");   
        return $this;
    }

Lợi ích của việc kêu gọi trả lại dữ liệu động này là nếu nhà cung cấp thêm dịch vụ phụ khác, tôi không phải thêm phương thức khác vào lớp hoặc tạo lớp mở rộng, v.v. Tôi không chắc liệu điều này có hữu ích với bất cứ ai, nhưng tôi figured tôi sẽ hiển thị một ví dụ nơi __set, __get, __call, vv có thể là một lựa chọn để xem xét kể từ khi chức năng chính là sự trở lại của dữ liệu.


BIÊN TẬP:

Thật trùng hợp, tôi đã thấy điều này một vài ngày sau khi đăng tải trong đó nêu chính xác kịch bản của tôi. Nó không phải là API mà tôi đã đề cập nhưng ứng dụng của các phương thức này giống hệt nhau:

Tôi có đang sử dụng api đúng không?


-2

Cập nhật: Đừng sử dụng câu trả lời này vì đây là mã rất ngu ngốc mà tôi tìm thấy khi tôi tìm hiểu. Chỉ cần sử dụng getter và setter đơn giản, nó sẽ tốt hơn nhiều.


Tôi thường sử dụng tên biến đó làm tên hàm và thêm tham số tùy chọn cho hàm đó để khi tham số tùy chọn đó được điền bởi người gọi, sau đó đặt nó vào thuộc tính và trả về $ đối tượng này (chuỗi) và sau đó khi tham số tùy chọn đó không được chỉ định bởi người gọi, tôi chỉ trả lại tài sản cho người gọi.

Ví dụ của tôi:

class Model
{
     private $propOne;
     private $propTwo;

     public function propOne($propVal = '')
     {
          if ($propVal === '') {
              return $this->propOne;
          } else {
              $this->propOne = $propVal;
              return $this;
          }
     }

     public function propTwo($propVal = '')
     {
          if ($propVal === '') {
              return $this->propTwo;
          } else {
              $this->propTwo = $propVal;
              return $this;
          }
     }
}

Bây giờ câu hỏi vẫn còn: Làm thế nào để bạn đặt một thuộc tính cho chuỗi trống? Và làm thế nào để bạn phát hiện ra rằng việc đặt một thuộc tính thành chuỗi rỗng thực sự thất bại và hoạt động như một getter? Chỉ cần nghĩ về các biểu mẫu HTML gửi các trường trống dưới dạng chuỗi ... Và không: Sử dụng một giá trị khác như NULL làm giá trị mặc định không giải quyết được vấn đề.
Sven
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.