Getter and Setter?

203

Nie jestem programistą PHP, więc zastanawiam się, czy w PHP bardziej popularne jest używanie jawnego gettera / settera, w czystym stylu OOP, z polami prywatnymi (tak jak lubię):

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

lub po prostu pola publiczne:

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

Dzięki

znak
źródło
7
Po wypróbowaniu kodu z odpowiedzi użyłem kodu, którego używasz w pytaniu. Jak smutne :-(
sumid
9
PHPstorm ... generuje> pobierające i ustawiające. == wygrana
DevDonkey
@DevDonkey W ogóle nie jest to wygrana. Do przechowywania danych strukturalnych użyj tablic. @: Mark Nie do tego służą obiekty. Getters i setery są złe: yegor256.com/2014/09/16/getters-and-setters-are-evil.html
Kubo2

Odpowiedzi:

222

Możesz użyć magicznych metod php __get i __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;
  }
}
?>
Davis Peixoto
źródło
15
Myślę, że masz na myśli __geti __set. Istnieją dwa podkreślenia, a nie jedno. Oto bezpośredni link do prawej części strony: php.net/manual/en/… (+1 za poprawną odpowiedź)
Computerish
28
Jaka jest korzyść z publicwłaściwości, jeśli nie ma walidacji / warunków sanitarnych?
KingCrunch
7
@KingCrunch, to tylko przykład. Bardzo, bardzo fałszywy przykład potężnego zasobu.
Davis Peixoto,
10
To nie jest tak naprawdę seter i getter. Zazwyczaj potrzebuję dla każdej właściwości innej implementacji gettera!
sumid
79
Proszę nie: Za pomocą magicznych metod UTRACISZ prawie wszystkie funkcje związane z jakością, w wielu IDE (nawet vim): automatyczne uzupełnianie, jawne dziedziczenie PHP, szybka interpretacja PHP i przydatne generowanie i wysyłanie PHPDoc. por. stackoverflow.com/a/6184893/490589
Ronan
113

Dlaczego warto korzystać z getterów i seterów?

  1. Skalowalność : łatwiej jest refaktorować getter niż przeszukiwać wszystkie przypisania var w kodzie projektu.
  2. Debugowanie : Możesz ustawić punkty przerwania na ustawiających i pobierających.
  3. Cleaner : Funkcje magiczne nie są dobrym rozwiązaniem do pisania mniej, twoje IDE nie sugeruje kodu. Lepiej używaj szablonów do szybkiego pisania.

bezpośrednie przypisanie i pobierający / ustawiający

Wiliam
źródło
7
Jeśli użyjesz @property, twoje IDE zasugeruje kod (przetestowany z PhpStorm 7)
Alex2php
41

Google opublikował już przewodnik na temat optymalizacji PHP, a wniosek był następujący:

Bez gettera i setera Optymalizacja PHP

I nie, nie wolno ci używać magicznych metod . Dla PHP metoda magiczna jest zła. Czemu?

  1. Trudno je debugować.
  2. Istnieje negatywny wpływ na wydajność.
  3. Wymagają napisania więcej kodu.

PHP nie jest Java, C ++ ani C #. PHP jest inny i gra różnymi rolami.

magallanes
źródło
10
Zwykle zgadzam się z tym pomysłem; to $dog->name = 'fido'jest lepsze niż $dog->setName('fido'). Podczas faktycznej mutacji właściwości (np. $dog->increaseAge(1)Potrafię opracować metodę, która dokonuje niezbędnej walidacji i mutuje tę właściwość. Ale nie wszystkie działania naprawdę wymagają mutacji w tym sensie
Charlie Schliesser,
11
Artykuł nie mówi „ nie ”, mówi „naiwni setery i gettersy”.
Brett Santore,
13
Można bezpiecznie założyć, że artykuł napisany przez Google z nagłówkiem „Wskazówki dotyczące wydajności PHP” NIE ma sugerować dobrego stylu kodowania, ale szybkiego wykonania kodu. Rozdział poświęcony ustawiającym i pobierającym jest oznaczony etykietą „Unikaj pisania naiwnych ustawiających i pobierających”, a przykładowy kod jest dokładnie taki: Naiwny. Ustawia i otrzymuje tylko zmienną bez sprawdzania poprawności. Taki rodzaj setera / gettera jest bezużyteczny. Wykonanie sprawdzania poprawności wewnątrz settera (wystarczy użyć podpowiedzi typu dla argumentu metody) sprawi, że settery / getters będą przydatne, ponieważ teraz twój kod WIE, z czym ma do czynienia.
Sven
3
to tak, jakby powiedzieć, że stylizacja liniowa jest lepsza. oczywiście wydajność jest lepsza, ale czy to lepszy kod? I tak nie wiedziałem, że inżynierowie Google używają php
Claudiu Creanga
13

Kapsułkowanie jest ważne w każdym języku OO, popularność nie ma z tym nic wspólnego. W dynamicznie wpisywanych językach, takich jak PHP, jest to szczególnie przydatne, ponieważ istnieje niewiele sposobów, aby upewnić się, że właściwość jest określonego typu bez użycia seterów.

W PHP działa to:

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

W Javie nie:

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

Korzystanie z magicznych metod ( __geti __set) również działa, ale tylko podczas uzyskiwania dostępu do właściwości, która ma mniejszą widoczność niż obecny zakres może uzyskać. Może z łatwością wywoływać bóle głowy podczas próby debugowania, jeśli nie jest właściwie używany.

netcoder
źródło
7
Getters i Setter nie powodują enkapsulacji. Encapsulation == obiekty robią coś z własnymi danymi zamiast dawać je na zewnątrz. Gettery i settery nie są narzędziem do wymuszania pisania w dynamicznie pisanych językach, takich jak PHP.
smentek
14
@smentek: Najwyraźniej brakuje ci co najmniej połowy tego, czym tak naprawdę jest enkapsulacja.
netcoder
2
Jako aktualizację tego dla każdego, kto szuka, PHP 7.4 będzie zawierać obsługę właściwości maszynopisanych. Możesz więc zadeklarować $barjako intw pierwszym przykładzie: wiki.php.net/rfc/typed_properties_v2
Kevin
7

Jeśli wolisz korzystać z funkcji __call, możesz użyć tej metody. Działa z

  • GET => $this->property()
  • SET => $this->property($value)
  • GET => $this->getProperty()
  • SET => $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;
    }
}
J-Rou
źródło
2
@ krzysztof-przygoda: „Magiczne metody” zawsze mają swoją cenę. Muszą używać rekurencji, property_exists(get_class($this), $name)a rekurencja jest powolna. Istnieje sposób na złagodzenie tego za pomocą buforowania, ale nadal będzie on wolniejszy niż ręczne tworzenie getterów i seterów. Napisałem to tylko jako alternatywę. Właściwie nie polecam używania „Magicznych metod”. Dodatkowy czas tworzenia getterów i seterów jest zwykle nieistotny.
J-Rou,
7

Oprócz świetnych i szanowanych odpowiedzi tutaj chciałbym rozwinąć PHP bez żadnych setterów / getterów.

PHP nie ma składni gettera i settera . Zapewnia podklasowe lub magiczne metody umożliwiające „zaczepienie” i przesłonięcie procesu wyszukiwania właściwości, jak zauważył Dave .

Magic pozwala nam leniwym programistom robić więcej przy mniejszym kodzie w czasie, gdy jesteśmy aktywnie zaangażowani w projekt i znamy go dokładnie, ale zwykle kosztem czytelności.

Wydajność Każda niepotrzebna funkcja, która wynika z wymuszenia architektury kodu przypominającej getter / setter w PHP, obejmuje własną ramkę stosu pamięci podczas wywoływania i marnuje cykle procesora.

Czytelność: podstawa kodu powoduje wzdęcie linii kodu, co wpływa na nawigację po kodzie, ponieważ więcej LOC oznacza więcej przewijania.

Preferencje: Osobiście, zgodnie z moją ogólną zasadą, traktuję niepowodzenie analizy kodu statycznego jako znak, aby uniknąć pójścia magiczną drogą, o ile w tym czasie wymykają mi się oczywiste długoterminowe korzyści.

Błędy:

Częstym argumentem jest czytelność. Na przykład $someobject->widthjest to łatwiejsze do odczytania niż $someobject->width(). Jednak w odróżnieniu od planety circumferencelub width, jak można to założyć static, instancji obiektu, takiej jak $someobject, która wymaga funkcji szerokości, prawdopodobnie mierzy szerokość instancji obiektu.
Dlatego czytelność zwiększa się głównie z powodu asertywnych schematów nazewnictwa, a nie przez ukrycie funkcji, która generuje daną wartość właściwości.

__get / __set używa:

  • wstępna walidacja i wstępna dezynfekcja wartości nieruchomości

  • struny np

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

    W takim przypadku generatelatexzastosowałby się schemat nazewnictwa akcji nazwa + nazwa metody

  • specjalne, oczywiste przypadki

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

Uwaga: PHP zdecydowało się nie implementować składni getter / setter. Nie twierdzę, że osoby pobierające / ustawiające są ogólnie złe.

Lorenz Lo Sauer
źródło
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

Gotowy!

joas
źródło
Za dużo płyty kotłowej. Wyobraź sobie, że te rzeczy są w każdej klasie. Unikaj IMO
DarkNeuron
PHP nie obsługuje programów pobierających i ustawiających z tych samych powodów, o których wspomniałeś. Każda implementacja tego typu ma poważny wpływ na wydajność skryptu po stronie serwera.
joas
Myślę, że jest to sprzeczne z „prywatnymi” właściwościami. Hermetyzujesz je, ale również pozwalasz na bezpośredni dostęp.
Koray Küpe 18.03.19
@ KorayKüpe tylko wtedy, gdy zdefiniowano moduł pobierający. Często używam tego kapsułkowania (z wieloma ulepszeniami) i działa ono doskonale. Możesz także z łatwością rozszerzyć klasę i używać jej w całym kodzie.
joas,
5

Cóż, PHP ma magicznych sposobów __get, __set, __isseti __unset, co jest zawsze początek. Niestety właściwe (rozumiesz?) Właściwości OO to coś więcej niż metody magiczne. Główny problem z implementacją PHP polega na tym, że wywoływane są magiczne metody dla wszystkich niedostępnych właściwości. Co oznacza, że ​​musisz powtórzyć się (np. Wywołując property_exists ()) w magicznych metodach podczas określania, czy imię jest faktycznie własnością twojego obiektu. I tak naprawdę nie możesz rozwiązać tego ogólnego problemu z klasą podstawową, chyba że wszystkie twoje klasy dziedziczą np. ClassWithProperties, ponieważ PHP nie ma wielokrotnego dziedziczenia.

W przeciwieństwie do tego, nowe klasy stylu Pythona dają ci property()możliwość jawnego zdefiniowania wszystkich swoich właściwości. C # ma specjalną składnię.

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

Emanuel Landeholm
źródło
1
Wywołanie property_exists, class_vars lub array_key_exists (tj. Sprawdzenie, czy właściwość naprawdę istnieje) jest tylko krokiem, aby uniknąć krytycznego błędu w czasie wykonywania. Nie jestem pewien, czy brak dyscypliny jest tym samym, co powtarzalność kodowania.
Davis Peixoto,
1
Słusznie. Ale w Python i C # to powtórzenie nie jest potrzebne. Myślę, że to siła.
Emanuel Landeholm
4

Zrobiłem eksperyment przy użyciu magicznej metody __call. Nie jestem pewien, czy powinienem to opublikować (z powodu wszystkich ostrzeżeń „NIE UŻYWAJ METOD MAGICZNYCH” w innych odpowiedziach i komentarzach), ale zostawię to tutaj… na wypadek, gdyby ktoś uznał to za przydatne.


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

Po prostu dodaj tę metodę powyżej w swojej klasie, teraz możesz wpisać:

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


W ten sposób możesz pobrać / ustawić wszystko w swojej klasie, jeśli istnieje, więc jeśli potrzebujesz go tylko dla kilku konkretnych elementów, możesz użyć „białej listy” jako filtra.

Przykład:

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

Teraz możesz tylko ustawić / ustawić „foo” i „opłata”.
Możesz także użyć tej „białej listy”, aby przypisać niestandardowe nazwy dostępu do swoich zmiennych.
Na przykład,

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

Na tej liście możesz teraz wpisać:

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

.
.
.
To wszystko.


Doc: __call () jest wyzwalany podczas wywoływania niedostępnych metod w kontekście obiektowym.

Sabaz
źródło
Problem z tymi wszystkimi „magicznymi” rozwiązaniami polega na tym, że po prostu używają tych magicznych metod, ponieważ istnieją i są one użyteczne, ponieważ zadany problem jest prosty, ogólny. Kiedy opuścisz ten ogólny poziom problemu, napotkasz określone wymagania, których nie da się rozwiązać za pomocą prostej magicznej metody, ale wymagałoby to bardzo złożonej magicznej metody - lub pojedynczych, zakodowanych seterów i getterów.
Sven
2

Po przeczytaniu innych rad mam skłonność powiedzieć, że:

Jako GENERIC reguły nie zawsze określają ustawiające dla WSZYSTKICH właściwości, szczególnie te „wewnętrzne” (semafory, wewnętrzne flagi ...). Oczywiście właściwości tylko do odczytu nie będą miały ustawiających, więc niektóre właściwości będą miały tylko ustawiające; tam właśnie przychodzi __get (), aby zmniejszyć kod:

  • zdefiniuj __get () (magiczne globalne gettery) dla wszystkich podobnych właściwości,
  • zgrupuj je w tablice, aby:
    • będą miały wspólne cechy: wartości pieniężne będą / mogą pojawiać się odpowiednio sformatowane, daty w określonym układzie (ISO, USA, międzynarodowe) itp.
    • sam kod może zweryfikować, że tylko istniejące i dozwolone właściwości są odczytywane przy użyciu tej magicznej metody.
    • ilekroć potrzebujesz utworzyć nową podobną właściwość, po prostu ją zadeklaruj i dodaj jej nazwę do odpowiedniej tablicy i gotowe. Jest to o wiele SZYBCIEJ niż definiowanie nowego programu pobierającego, być może z kilkoma wierszami kodu powtarzanymi wielokrotnie w całym kodzie klasy.

Tak! moglibyśmy napisać prywatną metodę, aby to zrobić, ale wtedy znowu będziemy WIELU metod zadeklarowanych (pamięć ++), które w końcu wywołują inną, zawsze tę samą, metodę. Dlaczego nie napisać POJEDYNCZEJ metody, by rządzić nimi wszystkimi ...? [tak! kalambur absolutnie przeznaczony! :)]

Ustawiacze magii mogą również reagować TYLKO na określone właściwości, więc wszystkie właściwości typu daty mogą być sprawdzane pod kątem nieprawidłowych wartości tylko w jednej metodzie. Jeśli właściwości typu daty były wymienione w tablicy, ich ustawiacze można łatwo zdefiniować. Oczywiście tylko przykład. jest zbyt wiele sytuacji.

O czytelności ... Cóż ... To kolejna debata: Nie lubię być związana z użyciem IDE (w rzeczywistości nie używam ich, zwykle mówią mi (i zmuszają ) jak pisz ... a ja lubię kodować „piękno”). Zwykle jestem konsekwentny w nazywaniu, więc używanie tagów i kilku innych pomocy jest dla mnie wystarczające ... W każdym razie: kiedy wszystkie te magiczne setery i gettery są skończone, piszę inne sety, które są zbyt specyficzne lub „specjalne”, aby być uogólnione w metodzie __set (). I to wszystko, czego potrzebuję, aby uzyskać i ustawić właściwości. Oczywiście: nie zawsze istnieje wspólna płaszczyzna lub istnieje kilka takich właściwości, które nie są warte kłopotu z kodowaniem magicznej metody, a ponadto istnieje stara, dobra, tradycyjna para setter / getter.

Języki programowania są po prostu: sztuczne języki ludzkie. Każdy z nich ma swoją intonację lub akcent, składnię i smak, więc nie będę udawał, że piszę kod Ruby lub Python przy użyciu tego samego „akcentu” niż Java lub C #, ani nie napisałbym JavaScript ani PHP, aby przypominać Perl lub SQL ... Używaj ich tak, jak powinny.

Manuel
źródło
1

Ogólnie rzecz biorąc, pierwszy sposób jest ogólnie bardziej popularny, ponieważ osoby posiadające wcześniejszą wiedzę programistyczną mogą łatwo przejść na PHP i wykonywać pracę w sposób obiektowy. Pierwszy sposób jest bardziej uniwersalny. Radzę trzymać się tego, co sprawdzone i sprawdzone w wielu językach. Następnie, gdy używasz innego języka, będziesz gotowy, aby coś osiągnąć ( zamiast spędzać czas na nowo wymyślając koło ).

Anthony Rutledge
źródło
0

Istnieje wiele sposobów tworzenia kodu źródłowego w konwencji netbeans. To jest miłe. Ułatwia to myślenie === FAŁSZ. Po prostu skorzystaj z tradycji, zwłaszcza jeśli nie masz pewności, która z właściwości powinna zostać zamknięta, a która nie. Wiem, to jest kod boi .... pla ..., ale do debugowania działa i wiele innych uważa, że ​​jest to lepszy, wyraźniejszy sposób. Nie spędzaj zbyt wiele czasu z tysiącami sztuk, jak robić proste gettery i setery. Nie możesz zaimplementować niektórych wzorców projektowych, takich jak zasada demeter i tak dalej, jeśli używasz magii. W konkretnej sytuacji możesz użyć magic_calls lub małych, szybkich i przejrzystych rozwiązań. Na pewno możesz w ten sposób tworzyć rozwiązania dla wzorników projektowych, ale po co utrudniać ci życie.

Dennis Komnenovic
źródło
0

Sprawdzanie poprawności + formatowanie / wyprowadzanie wartości

Settery umożliwiają sprawdzanie poprawności danych, a gettery umożliwiają formatowanie lub uzyskiwanie danych. Obiekty umożliwiają enkapsulację danych oraz ich walidację i formatowanie kodu w zgrabny pakiet, który zachęca do OSUSZANIA.

Rozważmy na przykład następującą prostą klasę, która zawiera datę urodzenia.

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ędziesz chciał sprawdzić, czy ustawiana jest wartość

  • Prawidłowa data
  • Nie w przyszłości

I nie chcesz przeprowadzać tej weryfikacji w całej aplikacji (lub w wielu aplikacjach). Zamiast tego łatwiej jest ustawić zmienną członkowską jako chronioną lub prywatną (aby setter był jedynym punktem dostępu) i sprawdzić poprawność w seterze, ponieważ wtedy będziesz wiedział, że obiekt zawiera prawidłową datę urodzenia bez względu na to, która część aplikacja, z której pochodzi obiekt, a jeśli chcesz dodać więcej walidacji, możesz dodać ją w jednym miejscu.

Może chcesz dodać wiele formatek, które działają na tej samej zmiennej, tj członek getAge()i getDaysUntilBirthday(), a może chcesz wymusić konfigurowalny format, w getBirthDate()zależności od kraju. Dlatego wolę konsekwentnie uzyskiwać dostęp do wartości za pośrednictwem metod pobierających zamiast mieszać się $date->getAge()z $date->birth_date.

metody pobierające i ustawiające są również przydatne podczas rozszerzania obiektów. Załóżmy na przykład, że Twoja aplikacja musiała pozwalać na ponad 150-letnie daty urodzenia w niektórych miejscach, ale w innych nie. Jednym ze sposobów rozwiązania problemu bez powtarzania jakiegokolwiek kodu byłoby rozszerzenie BirthDateobiektu i umieszczenie dodatkowej weryfikacji w seterze.

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);
    }
}
FuzzyTree
źródło
Jednak wiele razy musisz zweryfikować właściwości razem (a nie tylko indywidualnie). Myślę, że zezwolenie na „seterów” oznacza, że ​​również nie przechwytujesz kontekstu. Walidacja powinna odbywać się na podstawie kontekstu. Ponadto będziesz zmuszony sprawdzić flagę „isValid” na każdej posiadanej metodzie (i przeprowadzić weryfikację, jeśli jest fałszywa). To powiedziawszy, setery zapewniają użyteczny sposób na pisanie podpowiedzi, na przykład gdy masz właściwość, którą chcesz być Obiektem Wartości (np. Pieniądze).
prograhammer
Nie rozumiem, co masz na myśli przez zmuszanie do sprawdzania flagi „isValid”? Jeśli jedynym sposobem na ustawienie wartości są setery, które wykonują sprawdzanie poprawności, to wiesz, że dane są poprawne dzięki temu, że zostały pomyślnie ustawione. Jeśli chcesz zweryfikować właściwości razem, możesz napisać wspólną metodę sprawdzania poprawności, którą wywołują ustawiacze tych właściwości.
FuzzyTree,
Załóżmy, że masz klasę pracowników z setHiredi setHireDate. Nie jest dozwolone, aby ktoś ustalał datę zatrudnienia bez ustawiania pracownika jako zatrudnionego. Ale nie ma mowy, żebyś to egzekwował. Jeśli wymusisz to w jednym z tych ustawień, wówczas wymuszasz kolejność „ustawiania”, a to wymaga więcej odczytu kodu od programisty. Następnie, gdy przejdziesz do wykonania metody takiej jak $employee->promote($newPosition);musisz sprawdzić flagę, aby sprawdzić, czy sprawdzanie poprawności zostało wykonane, lub załóż, że nie zostało to zrobione, i zrób to ponownie (nadmiarowe).
prograhammer,
Zamiast tego uchwyć interakcje. Być może $employee->updateWorkStatus($hired, $hireDate);lub bardziej zaawansowany $employee->adminUpdate(\Employee\AdminUpdateDTO $dto);. Teraz możesz sprawdzić poprawność w odpowiednim kontekście i zdecydować, czy w ogóle potrzebna jest dodatkowa weryfikacja.
prograhammer
updateWorkStatus jest zasadniczo funkcją ustawiającą, która ustawia 2 zamiast 1 wartości, ale koncepcja jest taka sama. Jest to jeden ze sposobów, aby to zrobić, ale można również umieścić sprawdzanie kontekstu we wspólnej metodzie, która jest uruchamiana tylko wtedy, gdy wszystkie właściwości, które muszą być sprawdzane razem, są ustawione, tj. Kontekstowa część sprawdzania poprawności byłaby wywoływana zarówno przez setHiredDate, jak i setHired, ale działałaby tylko jeśli zarówno isset hired, jak i isset hiredDate były prawdziwe.
FuzzyTree,
0

Ten post nie jest konkretnie o __geta __setraczej __callktóry jest taki sam pomysł z wyjątkiem metody powołania. Z reguły trzymam się z daleka od wszelkiego rodzaju magicznych metod, które pozwalają na przeładowanie z powodów opisanych w komentarzach i postach, JEDNAK ostatnio natknąłem się na interfejs API innej firmy, którego używam, który korzysta z usługi SERVICE i SUB-SERVICE, na przykład :

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

Ważną częścią tego jest to, że ten interfejs API ma w tym przypadku wszystko to samo oprócz poddziałania doActionOne. Chodzi o to, że programista (ja i ​​inni korzystający z tej klasy) może wywołać pod-usługę z nazwy, w przeciwieństwie do czegoś takiego:

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

Mógłbym zamiast tego:

 $myClass->doActionOne($args);

W przypadku kodu twardego byłoby to po prostu dużo powielania (ten przykład bardzo luźno przypomina kod):

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

Ale dzięki magicznej metodzie __call()jestem w stanie uzyskać dostęp do wszystkich usług za pomocą metod dynamicznych:

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

Zaletą tego dynamicznego wezwania do zwrotu danych jest to, że jeśli dostawca doda inną pod-usługę, nie muszę dodawać innej metody do klasy ani tworzyć rozszerzonej klasy itp. Nie jestem pewien, czy jest to przydatne ktoś, ale pomyślałem, że pokażę przykład, w którym __set, __get, __call, itd. może być opcja do rozważenia, ponieważ podstawową funkcją jest powrót danych.


EDYTOWAĆ:

Przypadkowo zobaczyłem to kilka dni po opublikowaniu, które dokładnie opisuje mój scenariusz. Nie chodzi o API, o którym mówiłem, ale zastosowanie metod jest identyczne:

Czy używam poprawnie interfejsu API?

Rasclatt
źródło
-2

Aktualizacja: nie używaj tej odpowiedzi, ponieważ jest to bardzo głupi kod, który znalazłem podczas nauki. Wystarczy użyć zwykłego gettera i setera, jest o wiele lepszy.


Zwykle używam tej nazwy zmiennej jako nazwy funkcji i dodam opcjonalny parametr do tej funkcji, więc gdy ten opcjonalny parametr zostanie wypełniony przez obiekt wywołujący, następnie ustaw go na właściwość i zwróć $ ten obiekt (łańcuch), a następnie, gdy ten opcjonalny parametr nie zostanie określony przez dzwoniącym, właśnie zwracam właściwość dzwoniącemu.

Mój przykład:

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;
          }
     }
}
ajiyakin
źródło
Pozostaje pytanie: jak ustawić właściwość na pusty ciąg? A jak wykryjesz, że ustawienie właściwości pustego łańcucha faktycznie nie powiodło się i działało jako getter? Pomyśl tylko o formularzach HTML wysyłających puste pola jako ciągi ... I nie: Użycie innej wartości, takiej jak NULL jako wartości domyślnej, nie rozwiąże problemu.
Sven