web-dev-qa-db-ja.com

PHPの値オブジェクトと連想配列

(この質問では、コンテキストとしてPHPを使用していますが、PHPのみに限定されていません。たとえば、ハッシュが組み込まれている言語も関連します)

この例(PHP)を見てみましょう:

function makeAFredUsingAssoc()
{
    return array(
        'id'=>1337,
        'height'=>137,
        'name'=>"Green Fred");
}

対:

class Fred
{
    public $id;
    public $height;
    public $name;

    public function __construct($id, $height, $name)
    {
        $this->id = $id;
        $this->height = $height;
        $this->name = $name;
    }
}

function makeAFredUsingValueObject()
{
    return new Fred(1337, 137, "Green Fred");
}

方法#1はもちろん簡潔ですが、次のようなエラーが発生しやすい場合があります。

$myFred = makeAFredUsingAssoc();
return $myFred['naem']; // notice teh typo here

もちろん、$myFred->naemも同様にエラーにつながりますが、これは事実です。しかし、正式なクラスを持つことは私にとってより厳格に感じますが、私はそれを本当に正当化することはできません。

各アプローチを使用することの長所/短所は何であり、人々はいつどのアプローチを使用する必要がありますか?

38
kizzx2

表面的には、2つのアプローチは同等です。ただし、クラスを使用すると、標準のOOの利点のほとんどが得られます:カプセル化、継承など。

また、次の例も見てください。

$arr['naem'] = 'John';

は完全に有効であり、見つけるのが難しいバグである可能性があります。

一方、

$class->setNaem('John');

動作しません。

28
Zack Marrapese

このような単純なクラス:

_class PersonalData {
    protected $firstname;
    protected $lastname;

    // Getters/setters here
}
_

アレイに比べて利点はほとんどありません。

  1. いくつかのタイプミスをする可能性はありません。 _$data['firtsname'] = 'Chris';_は機能しますが、$data->setFirtsname('Chris');はエラーをスローします。
  2. タイプヒント:PHP配列にはすべて(何も含まない)を含めることができますが、明確に定義されたクラスには指定されたデータのみが含まれます。

    _public function doSth(array $personalData) {
        $this->doSthElse($personalData['firstname']); // What if "firstname" index doesn't exist?
    }
    
    
    public function doSth(PersonalData $personalData) {
        // I am guaranteed that following method exists. 
        // In worst case it will return NULL or some default value
        $this->doSthElse($personalData->getFirstname());
    }
    _
  3. 検証やロギングなどの設定/取得操作の前に、いくつかのコードを追加できます。

    _public function setFirstname($firstname) {
        if (/* doesn't match "firstname" regular expression */) {
            throw new InvalidArgumentException('blah blah blah');
        }
    
    
    _
    _if (/* in debbug mode */) {
        log('Firstname set to: ' . $firstname);
    }
    
    
    $this->firstname = $firstname;
    _ </ code>
     
    } 
    
  4. OOP継承、ポリモーフィズム、型ヒント、カプセル化など)のすべての利点を使用できます...
  5. 前述のように、すべての「構造体」は、CountableSerializable、またはIteratorインターフェイスの実装を提供する基本クラスから継承できるため、構造体はforeachループなど。
  6. IDEのサポート。

唯一の欠点はスピードのようです。アレイの作成と操作が高速になります。ただし、多くの場合、CPU時間はプログラマー時間よりもはるかに安いことは誰もが知っています。 ;)

29
Crozin

しばらく考えた後、これが私の答えです。

値オブジェクトを優先する配列よりも重要なことは明快さです。

この関数を考えてみましょう:

// Yes, you can specify parameter types in PHP
function MagicFunction(Fred $fred)
{
    // ...
}

versus

function MagicFunction(array $fred)
{
}

意図はより明確です。関数の作成者は、自分の要件を強制できます。

さらに重要なことに、ユーザーとして、私は簡単に調べることができます有効なフレッドを構成するもの。開く必要があるだけですFred.phpそしてその内部を発見してください。

発信者と着信者の間には契約があります。値オブジェクトを使用すると、このコントラクトは構文チェック済みコードとして記述できます。

class Fred
{
    public $name;
    // ...
}

配列を使用した場合、ユーザーがコメントまたはドキュメントを読むことを期待できます。

// IMPORTANT! You need to specify 'name' and 'age'
function MagicFunction(array $fred)
{
}
8
kizzx2

ユースケースに応じて、またはのいずれかを使用する場合があります。このクラスの利点は、タイプのように使用でき、メソッドまたは任意のイントロスペクションメソッドでタイプヒントを使用できることです。クエリなどからランダムなデータセットを渡したいだけの場合は、配列を使用する可能性があります。ですから、モデルでFredが特別な意味を持っている限り、クラスを使用すると思います。

補足:
ValueObjectsは不変であることになっています。少なくとも、ドメインドリブンデザインにおけるエリックエヴァンの定義を参照している場合は。 FowlerのPoEAでは、ValueObjectは必ずしも不変である必要はありませんが(推奨されていますが)、アイデンティティを持つべきではありません。これは、明らかにFredの場合です。

6
Gordon

この質問をあなたに提起させてください:

_$myFred['naem']_のようなタイプミスを作成することと_$myFred->naem_のようなタイプミスを作成することの違いは何ですか?同じ問題が両方の場合にまだ存在し、両方ともエラーになります。

私はプログラムするときに[〜#〜] kiss [〜#〜](シンプルに保つ、愚か)を使用したい.

  • メソッドからクエリのサブセットを返すだけの場合は、配列を返すだけです。
  • クラスの1つにpublic/private/static/protected変数としてデータを保存する場合は、stdClassとして保存するのが最適です。 。
  • これを後で別のクラスメソッドに渡す場合は、Fredクラスの厳密な型指定、つまりpublic function acceptsClass(Fred $fredObj)を使用することをお勧めします。

戻り値として使用する場合は、配列ではなく標準クラスを簡単に作成できます。この場合、厳密な入力についてはあまり気にする必要はありません。

_$class = new stdClass();
$class->param = 'value';
$class->param2 = 'value2';
return $class;
_
3
Corey Ballou

ハッシュの長所:設計時に不明な名前と値の組み合わせを処理できます。

2
Veger

戻り値がアプリケーションのエンティティを表す場合は、オブジェクトを使用する必要があります。これがOOPの目的です。関連のない値のグループを返すだけの場合は、それほど明確ではありません。ただし、それがパブリックAPIの一部である場合は、宣言されたクラスが最善の方法です。

1
Will Vousden

適切な値オブジェクトの利点は、実際に無効なオブジェクトを作成する方法や、存在するオブジェクトを変更する方法(整合性と「不変性」)がないことです。ゲッターとタイプヒントパラメータだけで、コンパイル可能なコードでそれを台無しにする方法はありません。これは、順応性のある配列で明らかに簡単に行うことができます。

または、パブリックコンストラクタで検証して例外をスローすることもできますが、これにより、より穏やかなファクトリメソッドが提供されます。

class Color
{
    public static function create($name, $rgb) {
        // validate both
        if ($bothValid) {
            return new self($name, $rgb);
        } else {
            return false;
        }
    }
    public function getName() { return $this->_name; }
    public function getRgb() { return $this->_rgb; }

    protected function __construct($name, $rgb)
    {
        $this->_name = $name;
        $this->_rgb = $rgb;
    }
    protected $_name;
    protected $_rgb;
}
1
Steve Clay

私はOOP言語を10年以上使用してきました。オブジェクトがどのように機能するかを理解すれば、それが好きになります。継承、ポリモーフィズム、カプセル化、オーバーロードがOOPの主な利点です。一方、 PHP PHPはフル機能のオブジェクト指向言語ではないことを考慮する必要があります。たとえば、メソッドのオーバーロードやコンストラクターのオーバーロードを使用することはできません(簡単です) )。

PHP=の連想配列は非常に優れた機能ですが、phpエンタープライズアプリケーションに害を及ぼすと思います。コードを記述する場合、クリーンで保守可能なアプリケーションを取得する必要があります。

もう1つは、連想配列で失うと思うのは、インテリセンスを使用できないということです。

したがって、よりクリーンで保守しやすいコードを記述したい場合は、提供されたときにOOP機能を使用する必要があると思います。

1
Sanosay

正直なところ、私は両方が好きです。

  • ハッシュ配列はオブジェクトを作成するよりもはるかに高速で、時間はお金です!
  • しかし、JSONはハッシュ配列を好みません(これはOOP OCD)に少し似ているようです)。
  • 複数の人が参加するプロジェクトの場合は、明確に定義されたクラスの方がよいでしょう。
  • ハッシュ配列は、すべてのシナリオで確実にするのは難しいですが、より多くのCPU時間とメモリを必要とする場合があります(オブジェクトには事前定義された量があります)。

しかし、本当にひどいのは、どれを使いすぎるかを考えることです。私が言ったように、JSONはハッシュが好きではありません。おっと、配列を使用しました。今、数千行のコードを変更する必要があります。

私はそれが好きではありませんが、クラスがより安全な方法のようです。

1
Josh

2番目の例のように、ハードコードされたプロパティを使用することを好みます。期待されるクラス構造(およびクラスで可能なすべてのプロパティ)をより明確に定義しているように感じます。結局のところ、常に同じキー名を使用することを忘れないようにするという最初の例とは対照的です。 2つ目では、いつでも戻ってクラスを確認し、ファイルの先頭を確認するだけでプロパティを把握できます。

あなたはあなたが2番目のもので何か間違ったことをしていることを知っているでしょう-あなたがecho $this->doesntExistエラーが発生しますが、echo array['doesntExist']あなたはしません。

0
Parrots