我用PHP编程已经有几年了,过去也曾采用过自己的方法来处理应用程序中的数据。
我在过去构建过自己的MVC,并且对php中的OOP有一个合理的理解,但我知道我的实现还需要一些认真的工作。
在过去,我在模型和数据库表之间使用is-a关系。在做了一些研究之后,我现在知道这并不是最好的前进道路。据我所知,我应该创建不真正关心底层数据库(或任何要使用的存储机制)的模型,而只关心它们的操作和数据。
由此,我确定了我可以创建模型,例如,一个Person和this person对象可以有一些孩子(人类孩子),这些孩子也是一个数组中保存的Person对象(使用addPerson和removePerson方法,接受一个Person对象)。
然后我可以创建一个PersonMapper,我可以用它来获取一个具有特定'id‘的人,或者用来拯救一个人。
然后,这可以在查找表中查找关系数据,并为已被请求的人员创建相关联的子对象(如果有),并同样将数据保存在保存命令的查找表中。
这是我现在所知的极限……
如果我想要对具有不同楼层和不同房间的建筑进行建模,该怎么办?如果我想在那些房间里放些东西怎么办?
是否要为建筑物、楼层、房间和项目创建类
具有以下结构。
建筑物可以有一个或多个级别对象放在一个数组中级别可以有一个或多个房间对象放在一个数组中房间可以有一个或多个项目对象放在一个数组中
每个类的映射器和具有更高级别映射器的映射器使用子映射器来填充数组(根据顶级对象的请求或根据请求延迟加载)
这似乎紧密地耦合了不同的对象,尽管是在一个方向上(即。楼层不一定要在建筑物中,但建筑物可以有标高)
这是正确的做法吗?
在视图中,我想要显示一个具有选择级别的选项的建筑,然后显示具有选择房间等选项的级别。但我可能也想要显示一个树状结构的项目在建筑物中,以及他们是在什么水平和房间。
我希望这是有意义的。当oop的一般概念似乎是分离事物时,我只是在为相互嵌套对象的概念而苦苦挣扎。
如果有人能帮上忙,那将是非常有用的。
发布于 2013-02-11 12:18:53
假设你这样组织你的对象:

为了初始化整个建筑对象(具有层、房间、项目),你必须提供db层类来完成这项工作。获取建筑物树状视图所需的所有内容的一种方法是:
(缩放浏览器以获得更好的视图)

构建将根据作为参数提供给initializeById方法的映射器,使用适当的数据对自身进行初始化。初始化标高和房间时,此方法也适用。(注意:在初始化整个构建时重用这些initializeById方法将导致大量的db查询,所以我使用了一些结果索引技巧和SQL opetator)
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;
}
}现在让我们假设我们有这个db模式

最后是一些代码。
构建
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;
}
}发布于 2012-09-12 10:34:38
这正将我的知识推向极限.
您描述的建筑/楼层/房间/项目结构对我来说听起来非常好。领域驱动的设计就是理解你的领域,然后将概念建模为对象--如果你能用简单的语言描述你想要的,你就已经完成了你的任务。当你在设计你的领域时,把所有其他的东西(比如持久性)都排除在外,这样你就可以更容易地跟踪这些事情了。
这似乎紧密地耦合了不同的对象,尽管是在一个方向上
这没什么不对的。现实世界中的建筑物确实有楼层、房间等,你只是简单地模拟了这一事实。
每个类的
和映射器,以及使用子映射器的更高级别映射器
在DDD术语中,这些“映射器”称为“存储库”。此外,如果您的Building对象拥有其中的所有楼层/房间/项目,并且如果在没有建筑物的情况下单独加载Room没有意义,那么它可能会被视为“聚合”对象。在这种情况下,您只需要一个可以加载整个建筑树的BuildingRepository。如果您使用任何现代ORM库,它应该会自动为您完成所有映射工作(包括加载子对象)。
发布于 2012-09-12 03:38:18
如果我没理解错你的问题,你的主要问题是你没有正确地使用抽象类。基本上你应该为你的每一栋建筑,楼层,房间等有不同的类。例如,你应该有一个抽象类building,一个由Building等扩展的抽象类levels,这取决于你到底想要什么,就像你有一个树形建筑-> level ->room,但它更像是一个双向链表,因为每个建筑都有一个level对象数组,每个level都有一个父建筑对象。你也应该使用接口,因为很多人忽略了它们,它们在未来会对你和你的团队有很大的帮助。
关于以更通用的方式构建模型,在我看来,最好的方法是让一个类为您使用的每种类型的数据库或其他存储方法实现相同的方法。例如,你有一个mongo数据库和一个mysql数据库,你会为每一个数据库创建一个类,它们会有像add,remove,update,push等方法。为了确保你不会犯任何错误,一切都会正常工作,最好的方法是有一个接口数据库来存储这些方法,而你不会在没有定义mysql方法的地方使用mongo方法。您还可以为通用方法定义一个抽象类(如果它们有)。希望这将是有帮助的,干杯!
https://stackoverflow.com/questions/12373189
复制相似问题