web-dev-qa-db-ja.com

ゲッターとセッター?

私はPHP開発者ではないので、PHPでは、純粋なOOPスタイルで、明示的なgetter/settersを使う方が人気があるかどうか疑問に思います。プライベートフィールドを使って(私が好きな方法):

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

または単にパブリックフィールド:

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

ありがとう

194
Mark

あなたは php magic methods__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;
  }
}
?>
208
Dave

なぜゲッターとセッターを使うのですか?

  1. スケーラビリティ:プロジェクトコード内のすべてのvar割り当てを検索するよりも簡単にゲッターをリファクタリングできます。
  2. デバッグ:セッターとゲッターにブレークポイントを置くことができます。
  3. Cleaner:マジック関数は書くことを減らすための良い解決策ではありません、あなたのIDEはコードを示唆しません。速記型ゲッター用のテンプレートの使用方法を改善しました。

direct assignment and getters/setters

107
Wiliam

Googleは既にPHPの最適化に関するガイドを公開していましたが、結論は次のとおりです。

ゲッターとセッターなしPHPの最適化

そしていいえ、あなたはしなければなりません魔法の方法を使用しない。 PHPの場合、Magic Methodは悪です。どうして?

  1. それらはデバッグするのが難しいです。
  2. パフォーマンスに悪影響があります。
  3. もっとコードを書く必要があります。

PHPはJava、C++、C#のいずれでもありません。 PHPは異なり、さまざまな役割を果たします。

36
magallanes

カプセル化はあらゆるOO言語において重要であり、人気はそれとは関係がありません。 PHPのような動的型付け言語では、セッターを使用せずにプロパティが特定の型であることを確認する方法がほとんどないため、これは特に便利です。

PHPでは、これは動作します。

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

Javaでは、それはしません:

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

マジックメソッド(__getおよび__set)を使用しても機能しますが、現在のスコープよりも低い可視性を持つプロパティにアクセスする場合に限ります。正しく使用されていないと、デバッグしようとしたときに頭痛がしやすくなります。

12
netcoder

ここですでに偉大で尊敬されている答えに加えて、私はPHPを拡張したいと思います。

PHPにはゲッターとセッターの構文はありませんDaveで指摘されているように、サブクラス化された、またはマジックメソッドを使用して「フック」し、プロパティ検索プロセスをオーバーライドすることができます。

Magic私たち怠惰なプログラマー 私たちが積極的にプロジェクトに従事し、知っているときに少ないコードでより多くのことができるようにしますそれは密接に、しかし通常読みやすさを犠牲にしてです。

パフォーマンスPHPでgetter/setter風のコードアーキテクチャを強制することから生じる不要な関数はすべて、呼び出し時に独自のメモリスタックフレームを含みます。 CPUサイクルを無駄にする。

読みやすさ:コードベースはコード行の肥大化を招きます。これはLOCが多いほどスクロールが多くなるため、コードナビゲーションに影響を与えます。

好み:個人的には、私の経験則として、明らかな限り魔法の道をたどらないようにするための記号として静的コード分析の失敗を取ります。長期的な利益はその時私を避けます。

誤り:

よくある議論は読みやすさです。例えば$someobject->width$someobject->width()より読みやすいです。ただし、circumferenceと見なすことができる惑星のwidthまたはstaticとは異なり、width関数を必要とする$someobjectなどのオブジェクトのインスタンスは、おそらくオブジェクトのインスタンス幅の測定を行います。
したがって、読みやすさは、主に積極的な命名規則のためであり、与えられた特性値を出力する関数を隠すことによってではない。

__ get/__setは次のものを使用します。

  • プロパティ値の事前検証および事前衛生化

  • 文字列.

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

    この場合、generatelatexは、actionname + methodnameの命名規則に従います。

  • 特別な、明白なケース

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

注:PHPは、getter/setter構文を実装しないことを選択しました。ゲッター/セッターが一般的に悪いとは言っていません。

7
Lorenz Lo Sauer

__call関数を使用したい場合は、このメソッドを使用できます。それは働く

  • GET => $this->property()
  • SET => $this->property($value)
  • GET => $this->getProperty()
  • SET => $this->setProperty($value)

カルスダス

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

準備完了

6
joas

さて、PHPはマジックメソッド__get__set__isset__unsetを持っていますが、これは常に始まりです。残念ながら(それを入手しますか?)OO propertiesは魔法の方法以上のものです。 PHPの実装に関する主な問題は、魔法のメソッドがすべてのアクセスできないプロパティに対して呼び出されることです。つまり、nameが実際にオブジェクトのプロパティであるかどうかを判断するときには、魔法のメソッドで(例えばproperty_exists()を呼び出して)自分自身を繰り返す必要があります。そして、すべてのクラスがieを継承しない限り、この一般的な問題を基本クラスで解決することはできません。 ClassWithProperties、PHPは多重継承を欠いています。

対照的に、Pythonの新しいスタイルクラスはproperty()を提供します。これにより、すべてのプロパティを明示的に定義できます。 C#には特別な構文があります。

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

5

マジックメソッド__callを使って実験しました。私がそれを投稿すべきかどうかはわからないが(他の答えやコメントの中のすべての "DO NOT MAGIC METHODS"警告のため)、私はそれをここに残しておきます。


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

クラスに上記のメソッドを追加するだけで、次のように入力できます。

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


こうすれば、存在する場合はクラス内のすべてのものを取得/設定することができます。少数の特定の要素にのみ必要な場合は、「ホワイトリスト」をフィルタとして使用できます。

例:

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

これで、 "foo"と "fee"だけを取得/設定できます。
あなたは自分のvarにアクセスするためのカスタム名を割り当てるためにその "ホワイトリスト"を使うこともできます。
例えば、

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

そのリストであなたは今タイプすることができます:

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


.
.
それで全部です。


Doc:__ call() は、オブジェクトコンテキストでアクセスできないメソッドを呼び出すと発生します。

3
Sabaz

他のアドバイスを読んだ後、私はそれを言う傾向があります:

ジェネリックルールとして、ALLプロパティ、特に「内部」プロパティ(セマフォ、内部フラグ...)のセッターを常に定義するとは限りません。 読み取り専用プロパティにはセッターが含まれないため、一部のプロパティにはゲッターのみが含まれます。そこで__get()がコードを縮小します。

  • 同様のすべてのプロパティに対して__get()(魔法のグローバルゲッター)を定義し、
  • それらを配列にグループ化します:
    • それらは共通の特性を共有します:金銭的価値は適切にフォーマットされた/来るかもしれません、特定のレイアウトの日付(ISO、米国、国際)など。
    • コード自体は、この魔法の方法を使用して、既存の許可されたプロパティのみが読み取られていることを確認できます。
    • 新しい同様のプロパティを作成する必要があるときはいつでも、それを宣言してその名前を適切な配列に追加すれば完了です。その方法新しいゲッターを定義するよりも高速、おそらくコードのいくつかの行がクラスコード全体で何度も繰り返されます。

はい!それを行うプライベートメソッドを記述することもできますが、その場合も、常に同じメソッドを呼び出す別の多くのメソッド(++ memory)が宣言されます。なぜSINGLEメソッドをそれらすべてを支配する...? [うん!絶対に意図したしゃれ! :)]

マジックセッターは特定のプロパティにのみ応答できるため、1つのメソッドのみですべての日付型プロパティを無効な値に対してスクリーニングできます。日付型プロパティが配列にリストされている場合、それらのセッターを簡単に定義できます。もちろん、ほんの一例です。状況が多すぎます。

readability...について...それは別の議論です:IDEの使用に縛られるのは好きではありません(実際、私はそれらを使用しません。彼らは私に(そしてforce私)書き方を教えてくれます... 「美」のコーディング)。私は命名について一貫している傾向があるので、ctagsと他のいくつかの補助具を使用するだけで十分です...とにかく:このマジックセッターとゲッターがすべて完了したら、私はあまりにも具体的または「特別な」他のセッターを書きます__set()メソッドで一般化されます。そして、プロパティの取得と設定に必要なすべてをカバーしています。もちろん、常に共通の基盤があるわけではありません。または、魔法のメソッドをコーディングするのに苦労する価値のないプロパティがいくつかあります。そして、古い伝統的なセッター/ゲッターのペアがまだあります。

プログラミング言語とは、人間の人工言語です。したがって、それぞれに独自のイントネーションまたはアクセント、構文、フレーバーがあるため、Rubyと同じ「アクセント」を使用してPythonまたはJavaコードを書くふりはしません。 C#も、PerlやSQLに似たJavaScriptやPHPを書くこともありません...本来の使用方法で使用してください。

2
Manuel

一般的に言って、最初の方法は全体的に人気があります。これは、プログラミングの知識がある人は簡単にPHPに移行してオブジェクト指向のやり方で作業を進めることができるからです。最初の方法はもっと普遍的です。私の忠告は、多くの言語で試行された事実に固執することです。それで、いつ、あなたが他の言語を使うのであれば、何かを成し遂げる準備ができているでしょう(車輪の再発明に時間を費やす代わりに)。

1

値の検証+フォーマット/導出

セッターを使用するとデータを検証でき、ゲッターを使用するとデータをフォーマットまたは導出できます。オブジェクトを使用すると、データとその検証コードおよび書式設定コードをDRYを促進するきちんとしたパッケージにカプセル化できます。

たとえば、生年月日を含む次の単純なクラスを考えてみましょう。

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

設定されている値が

  • 有効な日付
  • 将来的ではない

そして、あなたはこの検証をあなたのアプリケーション全体(あるいはそのことに関して複数のアプリケーション)に渡ってやりたくないでしょう。代わりに、メンバー変数を保護または非公開にし(セッターを唯一のアクセスポイントにするため)、セッターで検証するほうが簡単です。なぜなら、オブジェクトのどの部分に関係なく、オブジェクトに有効な生年月日が含まれるからです。オブジェクトが由来するアプリケーションで、さらに検証を追加したい場合は、単一の場所に追加できます。

getAge()getDaysUntilBirthday()という同じメンバ変数を操作する複数のフォーマッタを追加したい場合や、ロケールに応じてgetBirthDate()で設定可能なフォーマットを強制したい場合があります。そのため、$date->getAge()$date->birth_dateを混在させるのではなく、一貫してゲッターを介して値にアクセスすることを好みます。

ゲッターとセッターは、オブジェクトを拡張するときにも役立ちます。たとえば、アプリケーションで、ある場所で150年以上の生年月日を許可し、他の場所では許可しないようにする必要があるとします。何もコードを繰り返さずに問題を解決する1つの方法は、BirthDateオブジェクトを拡張し、追加の検証を設定メソッドに追加することです。

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

NetBeans規約でソースコードを作成する方法はたくさんあります。これはいいね。それはそのように簡単に考えるようになります=== FALSE。特に、どのプロパティをカプセル化するのか、どのプロパティをカプセル化しないのかわからない場合は、従来の方法を使用してください。私は知っています、それはboi .... pla ...コードですが、デバッグ作業や他の多くのためにそれがより良い、明確な方法であると思います。単純なゲッターやセッターを作る方法をたくさんの芸術に費やしてはいけません。魔法を使うなら、demeter-ruleなどのようなデザインパターンも実装することはできません。特定の状況では、magic_callsを使用することも、小規模で迅速かつ明確な解決策を使用することもできます。もちろん、このようにしてデザインパターンの解決策を作ることもできますが、なぜあなたはより困難な生活を送るのか。

0

この投稿は特に__get__setについてではなく、むしろメソッド呼び出しを除いて同じ考えです__callです。原則として、私はコメントや投稿で概説された理由でオーバーロードを可能にするどんな種類のマジックメソッドからも離れていますしかしながら、私は最近走りました私が使用しているサードパーティのAPIには、SERVICEとSUB-SERVICEを使用しています。

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

この重要な部分は、このAPIがサブアクションを除いてすべて同じ、この場合はdoActionOneを持つことです。そのアイデアは、開発者(私自身と他の人がこのクラスを使用している)がサブサービスを名前で呼び出すことができるというものです。

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

私は代わりにすることができます:

 $myClass->doActionOne($args);

ハードコーディングすると、これは多くの重複になります(この例非常には、コードと大まかに似ています)。

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

しかし__call()のマジックメソッドで私は動的メソッドですべてのサービスにアクセスすることができます:

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

データの返却を動的に呼び出すことの利点は、ベンダーが別のサブサービスを追加しても、クラスに別のメソッドを追加したり、拡張クラスを作成したりする必要がないことです。だれでも、私は__set__get__callなどが考慮のためのオプションであるかもしれない例を示すだろうと考えました。主な機能はデータの戻りです。


編集:

偶然にも、私は投稿の数日後にこれを見ましたが、それは私のシナリオを正確に概説しています。これは私が言及していたAPIではありませんが、メソッドの適用は同じです。

私は正しくapiを使っていますか?

0
Rasclatt