web-dev-qa-db-ja.com

クラスの設計-メソッドは他のメソッドを呼び出す必要がありますか?

私はクーポン用の非常に基本的なカスタムクラスを作成しており、クラスの基本的なレイアウトを考え出しました。これは、一般的にはベストプラクティスであると理解している多数の小さなメソッドで構成されています。

私が確信していないのは、クラスの流れをどのように決定するかです。現在、コードは次のようになっています。

public function isCouponValid($coupon) {
    if (strlen($coupon) != 8) return false;

    $query = $this->db->prepare('SELECT coupon_code FROM coupons WHERE coupon_code=:coupon');
    try ($query->execute(array(':coupon' => $coupon))) {
        while ($row = $query->fetch()) {
            return true;
        }   
    } catch (PDOException $e) {
        $log->addError('Could not execute SQL query: '.$query.' '.$e->getMessage());
    }

    return false;
}

public function isCouponUsed($coupon) {
    if (self::isCouponValid($coupon)) {
        $query = $this->db->prepare('SELECT coupon_used FROM coupons WHERE coupon_code=:coupon');
        try ($query->execute(array(':coupon' => $coupon))) {
            while ($row = $query->fetch()) {
                return ($row['coupon_used'] == '1') ? true : false;
            }   
        } catch (PDOException $e) {
            $log->addError('Could not execute SQL query: '.$query.' '.$e->getMessage());
        }
    }

    return false;
}

public function setCouponUsed($coupon) {
    if (self::isCouponValid($coupon) && !self::isCouponUsed($coupon)) {
        etc...
    }
}

基本的に、これらのチェックは現在クラスに統合されています。isCouponUsed()は、最初に有効であることを確認するためにisCouponValid()を呼び出します。これは良いアイデアですか、それともクラスの外でisCouponValid()を使用してから、その値をisCouponUsed()に渡すのがよいでしょうか。最初に有効ですか?

3
Roy

これはカプセル化に帰着すると思います。

クーポンをチェックするパブリックメソッドを提供する必要がある場合、APIのユーザーがisCouponValidを呼び出す前にisCouponUsedを呼び出すことを保証できないため、追加のチェックを追加する必要があります。しかし、これらの状況は常に疑わしいものであり、通常、設計が TellDon'tAsk などの原則に従わないことを示唆しています。

しかし-そして私はこれがここに当てはまると信じています-検証がオブジェクトの動作の一部である場合、たとえばsetCouponメソッドが示唆しているようであれば、それらをオブジェクトのプライベートメソッド(実装の詳細)としてカプセル化できます。カプセル化されるとすぐに、オブジェクトは実行順序を完全に制御できるようになります。そのため、isCouponUsedメソッドのifステートメントにすでに実装されているなど、クーポンが実際に有効な場合にのみsetCouponを呼び出すように選択できます。

3
MichelHenrich

基本的に私はこれらのチェックを現在クラスに統合しています-isCouponUsed()はisCouponValid()を呼び出して、それが最初に有効であることをチェックします。これは良い考えですか、それともクラスの外でisCouponValid()を使用してから、その値をisCouponUsed()に渡す方がよいので、isCouponUsed()はクーポンの検証にのみ関係し、そのコードかどうかはチェックしません最初に有効ですか?

他のメソッドを呼び出してそのジョブを実行するメソッドは問題なく、オブジェクト指向設計にうまく適合します。オブジェクトは、システムで動作を実行する人と考えてください。個人が行う必要のある作業が少ないほど、システムはよりスムーズに機能します。

コールセンターを呼び出して新しい電話を注文するとします。 「オブジェクト」に直面している顧客と話しますが、その「オブジェクト」は、「顧客に新しい電話を提供する」という動作を実行するために、システム内部の他のオブジェクトのホスト全体にクエリを実行する場合があります。外部の顧客として、あなたはそれを見たり気にしたりしませんが、電話の男も在庫をチェックし、倉庫に走って電話を取り、小包し、投稿しなければならない場合、コールセンターは一部になります等.

同様に、クーポンが使用されているかどうかを確認する前に、クーポンが有効かどうかを最初に確認する必要がある場合は問題ありません。クーポンオブジェクトに「あなたは有効です」というメッセージを送信した外部オブジェクトは、このアクションを実行するために何をするかは関係ありませんが、動作が明確な多数の小さなオブジェクトへの責任を分離することは良い設計です(同じコールセンターで電話に応答する人があなたに新しい電話を手に入れることに関係するすべての行動を実行しない方法、それはばかげているでしょう)

メソッドをクーポンオブジェクトに移動すると、コードの意味が理解できる場合があります。クーポン自体を検証できず、それが使用されたかどうかを追跡できないのはなぜですか。オブジェクトのパブリックメソッドの数を大幅に減らすことができます。

1
Cormac Mulhall

他のクラスの動作を駆動するクラスについて心配する必要があります。これは、ドット演算子に注意することを意味します(必ずしも常に回避する必要はありません)。

あなたがピザ配達人で、私にピザを配達するために私の家に来たとしたら、あなたは私の財布に手を伸ばして私のお金を受け取るつもりですか?私のお金が冷蔵庫にある場合はどうなりますか?これは高結合と呼ばれます。

これは、追加機能をプログラムするために、PizzaGuyクラスが常にPizzaOrdererの実装について知っている必要があることを意味します。 PizzaGuyオブジェクトは、PizzaOrdererにお金を要求することをお勧めします。ピザ男は、次のようなPizzaOrderクラスの異なるインスタンスに再利用できます。

SearchesCouchForMoneyPizzaOrderer 
PayingWithDebitForATenDollarOrderGuy

...

これはデメテルの法則と呼ばれ、結合を減らしてコードを再利用できるようにするための重要なルールです。

1
davidahines