web-dev-qa-db-ja.com

コード臭のインスタンス

instanceofの使用はコードのにおいかもしれませんが、私は次のコードの前で問題ないようです。そのような場合にはinstanceofを使用しないでください。使用するパターンは何ですか?

_<?php

interface Account {}

class PrivateAccount implements Account {}

class PublicAccount implements Account {}

class User {
    private $publicAccounts;
    private $privateAccounts;

    public function hasAccount(Account $account) {
        $haystack = [];

        if($account instanceof PrivateAccount) {
            $haystack = $this->privateAccounts;
        } else if($account instanceof PublicAccount) {
            $haystack = $this->publicAccounts;
        }

        foreach($haystack as $someAccount) {
            if($account->getId() == $someAccount->getId()) {
                return true;
            }
        }
        return false;
    }
}
_

具体的には、_$privateAccounts_および_$publicAccounts_は、ORMによってリレーショナルデータベースからレイジーロードされるオブジェクトであるため、getId()の呼び出しはコストがかかります(呼び出しごとにデータベースリクエストが発生します) )。 PublicAccountとPrivateAccountはデータベースの2つのテーブルです。

$haystack = array_merge($privateAccounts, $publicAccounts)を使用すると、instanceofの使用が削除されますが、パフォーマンスが低下します。

2
Thibaud

ええ、オブジェクトのタイプをチェックするコードのにおい。ポリモーフィズムの要点は、型を知っている必要はないということです。

あなたの場合、アカウントの他の派生クラスを渡すとコードが壊れます。

パフォーマンスの面では、遅延ロードされたリストの検索はどちらの方法でも遅くなります。たとえば、すべてのユーザーがすべてプライベートアカウントしか持っていない場合があるため、一般的なケースではこれをプライベートアカウントとパブリックアカウントに分割しても実際には効果がありません。

データベースにFindAccountByUserIdAndAccountIdロジックを実行させるか、ユーザーオブジェクトのaccountIdのリストをキャッシュして、このパフォーマンスの問題を削除します。

4
Ewan

私はここでの回答に反対し、この場合instanceofは大丈夫であると主張します。

PHPのことを1秒間忘れてみましょう。これがTypeScriptに必要なものです。


type PublicAccount = {
 type: "public";
 publicAccounts: List<any>; // IDK the type
}

type PrivateAccount = {
 type: "private";
 privateAccounts: List<any>; // IDK the type
}

type Account = PublicAccount | PrivateAccount;

const hasAccount = (account: Account) => {
 switch(account.type) {
   case "public":
     // type-safe
     return account.publicAccounts.size > 0; /** Some logic **/
   case "private":
     // type-safe
     return account.privateAccounts.size > 0; /** Some logic **/
   default: 
     return false;
 } 

}

これはPHPに実装された単なる 代数データ型の合計 であると主張します。これは、TypeScriptとしてこの高度な型付きキー/値構造を持たないためです。

TypeScriptでは問題ないので、なぜPHPの同じコード(意味論的)が問題になるのでしょうか。

あなたが何をしているかを知っている限り、あなたは大丈夫でなければなりません。

0
andras

instanceofを使用する代わりに、アカウントにメソッドisPrivate()を実装できます。これは、PrivateAccountでは_return true;_、PublicAccountでは_return false;_として実装されます。

もう1つのオプションは、二重ディスパッチを使用することです。

ユーザーはメソッドhasPrivateAccount()hasPublicAccount()を実装し、アカウントサブクラスはこれらのメソッドの1つを使用してisAccountOf()を実装し、ユーザーのhasAccount()isAccountOf($this)

0

アカウントが2つの異なるテーブルにあるのはなぜですか?それらのすべてではないにしても、共通の列は多くありませんか?共通の列を共通のテーブルに配置してから、その違いを補助テーブルに結合することはできませんか?参照整合性が必要な場合、補助テーブルの1つを使用したり、2列の制約(2番目はaccountTypeID)を使用したりできませんか?またはマテリアライズされたインデックス付きビューを使用しますか?

なぜこのコードは別のことをする必要があるのか​​疑問に思います。 privateAccount.accountsとpublicAccount.accountsで十分ではありませんか?どのようにしてpublicAccount.privateAccountsを作成できますか?

0
ErikE