12

私は数年間 PHP でプログラミングを行っており、過去にはアプリケーション内でデータを処理するために独自の方法を採用していました。

私は過去に独自の MVC を構築しており、php 内の OOP について十分に理解していますが、実装には深刻な作業が必要であることを知っています。

過去に、モデルとデータベース テーブルの間に is-a 関係を使用したことがあります。いくつかの調査を行った結果、これが実際には最善の方法ではないことがわかりました。私が理解している限り、基礎となるデータベース (または使用されるストレージ メカニズム) をあまり気にせず、アクションとデータのみを気にするモデルを作成する必要があります。

このことから、たとえば Person のモデルを作成できることを確立しました。この person オブジェクトは、配列に保持された Person オブジェクトでもある Children (人間の子供) を持つことができます (addPerson メソッドと removePerson メソッドを使用して、Person オブジェクトを受け入れます)。 .

次に、特定の 'id' を持つ Person を取得したり、Person を保存したりするために使用できる PersonMapper を作成できます。

次に、ルックアップ テーブルでリレーションシップ データをルックアップし、要求された Person に関連付けられた子オブジェクトを作成し (存在する場合)、save コマンドでルックアップ テーブルにデータを保存することもできます。

これは今、私の知識の限界を押し広げています.....

さまざまなレベルと、それらのレベル内のさまざまな部屋を持つ建物をモデル化したい場合はどうすればよいでしょうか? それらの部屋にアイテムを配置したい場合はどうすればよいですか?

建物、レベル、部屋、アイテムのクラスを作成しますか

以下の構造で。

建物は 1 つまたは複数のレベル オブジェクトを配列に保持できます レベル オブジェクトは配列に保持された 1 つまたは複数の部屋オブジェクトを保持できます 部屋は配列に保持された 1 つまたは複数のアイテム オブジェクトを保持できます

各クラスのマッパーと、子マッパーを使用してより高いレベルのマッパーを使用して配列を設定します (最上位オブジェクトの要求に応じて、または要求に応じて遅延ロードします)。

これは、一方向ではありますが、さまざまなオブジェクトを密結合しているように見えます (つまり、フロアは建物内にある必要はありませんが、建物にはレベルがあります)。

これは物事を進める正しい方法ですか?

ビュー内で、レベルを選択するオプションを使用して建物を表示し、次に部屋などを選択するオプションを使用してレベルを表示したいと考えていますが、建物内のアイテムの構造のようなツリーを表示したい場合もあります。彼らがいるレベルと部屋。

これが理にかなっていることを願っています。oop の一般的な概念が物事を分離することであると思われる場合、オブジェクトを相互にネストするという概念に苦労しています。

誰かが助けることができれば、それは本当に役に立ちます。

4

3 に答える 3

8

次のようにオブジェクトを整理するとします。 ここに画像の説明を入力してください

建物オブジェクト全体(レベル、部屋、アイテムを含む)を初期化するには、ジョブを実行するためのdbレイヤークラスを提供する必要があります。建物のツリービューに必要なものをすべて取得する1つの方法は、次のとおりです。

見やすくするためにブラウザをズームします

より良いビューのためにズーム

ビルドは、initializeByIdメソッドの引数として提供されたマッパーに応じて、適切なデータで自身を初期化します。このアプローチは、レベルと部屋を初期化するときにも機能します。(注:建物全体を初期化するときにこれらのinitializeByIdメソッドを再利用すると、多くのdbクエリが発生するため、結果のインデックス作成のトリックとSQL IN演算子を少し使用しました)

class RoomMapper implements RoomMapperInterface {

    public function fetchByLevelIds(array $levelIds) {
        foreach ($levelIds as $levelId) {
            $indexedRooms[$levelId] = array();
        }

        //SELECT FROM room WHERE level_id IN (comma separated $levelIds)
        // ...
        //$roomsData = fetchAll();

        foreach ($roomsData as $roomData) {
            $indexedRooms[$roomData['level_id']][] = $roomData;
        }

        return $indexedRooms;
    }

}

ここで、このデータベーススキーマがあるとしましょう

ここに画像の説明を入力してください

そして最後にいくつかのコード。

建物

class Building implements BuildingInterface {

    /**
     * @var int
     */
    private $id;

    /**
     * @var string
     */
    private $name;

    /**
     * @var LevelInterface[]
     */
    private $levels = array();

    private function setData(array $data) {
        $this->id = $data['id'];
        $this->name = $data['name'];
    }

    public function __construct(array $data = NULL) {
        if (NULL !== $data) {
            $this->setData($data);
        }
    }

    public function addLevel(LevelInterface $level) {
        $this->levels[$level->getId()] = $level;
    }

    /**
     * Initializes building data from the database. 
     * If all mappers are provided all data about levels, rooms and items 
     * will be initialized
     * 
     * @param BuildingMapperInterface $buildingMapper
     * @param LevelMapperInterface $levelMapper
     * @param RoomMapperInterface $roomMapper
     * @param ItemMapperInterface $itemMapper
     */
    public function initializeById(BuildingMapperInterface $buildingMapper, 
            LevelMapperInterface $levelMapper = NULL, 
            RoomMapperInterface $roomMapper = NULL, 
            ItemMapperInterface $itemMapper = NULL) {

        $buildingData = $buildingMapper->fetchById($this->id);

        $this->setData($buildingData);

        if (NULL !== $levelMapper) {
            //level mapper provided, fetching bulding levels data
            $levelsData = $levelMapper->fetchByBuildingId($this->id);

            //indexing levels by id
            foreach ($levelsData as $levelData) {
                $levels[$levelData['id']] = new Level($levelData);
            }

            //fetching room data for each level in the building
            if (NULL !== $roomMapper) {
                $levelIds = array_keys($levels);

                if (!empty($levelIds)) {
                    /**
                     * mapper will return an array level rooms 
                     * indexed by levelId
                     * array($levelId => array($room1Data, $room2Data, ...))
                     */
                    $indexedRooms = $roomMapper->fetchByLevelIds($levelIds);

                    $rooms = array();

                    foreach ($indexedRooms as $levelId => $levelRooms) {
                        //looping through rooms, key is level id
                        foreach ($levelRooms as $levelRoomData) {
                            $newRoom = new Room($levelRoomData);

                            //parent level easy to find
                            $levels[$levelId]->addRoom($newRoom);

                            //keeping track of all the rooms fetched 
                            //for easier association if item mapper provided
                            $rooms[$newRoom->getId()] = $newRoom;
                        }
                    }

                    if (NULL !== $itemMapper) {
                        $roomIds = array_keys($rooms);
                        $indexedItems = $itemMapper->fetchByRoomIds($roomIds);

                        foreach ($indexedItems as $roomId => $roomItems) {
                            foreach ($roomItems as $roomItemData) {
                                $newItem = new Item($roomItemData);
                                $rooms[$roomId]->addItem($newItem);
                            }
                        }
                    }
                }
            }

            $this->levels = $levels;
        }
    }

}

レベル

class Level implements LevelInterface {

    private $id;
    private $buildingId;
    private $number;

    /**
     * @var RoomInterface[]
     */
    private $rooms;

    private function setData(array $data) {
        $this->id = $data['id'];
        $this->buildingId = $data['building_id'];
        $this->number = $data['number'];
    }

    public function __construct(array $data = NULL) {
        if (NULL !== $data) {
            $this->setData($data);
        }
    }

    public function getId() {
        return $this->id;
    }

    public function addRoom(RoomInterface $room) {
        $this->rooms[$room->getId()] = $room;
    }

}

部屋

class Room implements RoomInterface {

    private $id;
    private $levelId;
    private $number;

    /**
     * Items in this room
     * @var ItemInterface[]
     */
    private $items;

    private function setData(array $roomData) {
        $this->id = $roomData['id'];
        $this->levelId = $roomData['level_id'];
        $this->number = $roomData['number'];
    }

    private function getData() {
        return array(
            'level_id' => $this->levelId,
            'number' => $this->number
        );
    }

    public function __construct(array $data = NULL) {
        if (NULL !== $data) {
            $this->setData($data);
        }
    }

    public function getId() {
        return $this->id;
    }

    public function addItem(ItemInterface $item) {
        $this->items[$item->getId()] = $item;
    }

    /**
     * Saves room in the databse, will do an update if room has an id
     * @param RoomMapperInterface $roomMapper
     */
    public function save(RoomMapperInterface $roomMapper) {
        if (NULL === $this->id) {
            //insert
            $roomMapper->insert($this->getData());
        } else {
            //update
            $where['id'] = $this->id;
            $roomMapper->update($this->getData(), $where);
        }
    }

}

アイテム

class Item implements ItemInterface {

    private $id;
    private $roomId;
    private $name;

    private function setData(array $data) {
        $this->id = $data['id'];
        $this->roomId = $data['room_id'];
        $this->name = $data['name'];
    }

    public function __construct(array $data = NULL) {
        if (NULL !== $data) {
            $this->setData($data);
        }
    }

    /**
     * Returns room id (needed for indexing)
     * @return int
     */
    public function getId() {
        return $this->id;
    }

}
于 2013-02-11T04:18:53.027 に答える
3

これは今私の知識に限界を押し広げています.....

あなたが説明した建物/レベル/部屋/アイテムの構造は私には完全にうまく聞こえます。ドメイン駆動設計とは、ドメインを理解し、概念をオブジェクトとしてモデル化することです。必要なものを簡単な言葉で説明できれば、タスクはすでに完了しています。ドメインを設計するときは、他のすべて(永続性など)を画像から除外してください。そうすれば、物事を追跡するのがはるかに簡単になります。

これは、一方向ではありますが、異なるオブジェクトを緊密に結合しているようです

それについては何も悪いことはありません。現実世界の建物には床や部屋などがあり、この事実を単純にモデル化しています。

子マッパーを使用した、より高いレベルのマッパーを持つ各クラスのマッパー

DDDの用語では、これらの「マッパー」は「リポジトリ」と呼ばれます。また、オブジェクトがその中のすべてのフロア/部屋/アイテムを所有していて、建物なしでそれ自体Buildingをロードすることが意味をなさない場合、オブジェクトは「集合体」と見なされる可能性があります。Roomその場合、必要なBuildingRepositoryのは建物ツリー全体をロードできるものだけです。最新のORMライブラリを使用する場合は、すべてのマッピング作業(子オブジェクトのロードを含む)が自動的に実行されます。

于 2012-09-12T02:34:37.750 に答える
0

あなたの質問を正しく理解できれば、あなたの主な問題は、抽象クラスを適切に使用していないことです。基本的に、建物、レベル、部屋などごとに異なるクラスを用意する必要があります。たとえば、抽象クラス Building 、Building によって拡張される抽象クラス Levels などを用意する必要があります。正確に何を持ちたいかによって異なりますが、ツリーの建物 - >レベル - >部屋があるようですが、各建物にはレベルオブジェクトの配列があり、各レベルには建物オブジェクトの親があるため、二重にリンクされたリストに似ています。多くの人がインターフェイスを無視しているため、インターフェイスも使用する必要があります。インターフェイスは将来、あなたとチームに大いに役立ちます。

より一般的な方法でモデルを構築することに関して、私の意見では、それを行うための最良の方法は、使用するデータベースまたは他のストアメソッドの各タイプに対して同じメソッドを実装するクラスを持つことです。たとえば、mongo データベースと mysql データベースがあり、これらのそれぞれにクラスがあり、追加、削除、更新、プッシュなどのメソッドがあります。これを行う最善の方法は、メソッドを格納するインターフェイス データベースを用意することです。これにより、mysql メソッドが定義されていない場所で mongo メソッドを使用することはなくなります。共通メソッドがある場合は、それらの抽象クラスを定義することもできます。これが役立つことを願っています、乾杯!

于 2012-09-11T19:38:17.723 に答える