zoukankan      html  css  js  c++  java
  • [php]领域模型和数据映射器

            业务逻辑层使用的是领域模型,因为它能使用数据映射器中的大部分模式。

            “万物皆对象”,领域模型就是对于项目中各种个体的抽象表达,就是一个类。它常常被描述为一组属性及附加的操作。它们是做某些相关事的某个东西。

             领域模型的复杂性主要来自于尝试使模型纯粹(pure),即将领域模型从应用中其他层中分离出来。把领域模型的参与者从表现层分离出来不难,但将这些参与者从数据层中分离出来则不太容易。在理想情形下,领域模型应该只包含它要表达和解决的问题,但在现实中领域模型很难完全去除数据库操作。

            领域模型常常映射到数据库结构上。通过将模型与数据库分离,整个层会更加容易测试,而且不会受到数据库结构的改变的影响,也不会受到存储机制的影响。领域模型只关心要完成的核心工作和承担的责任。领域模型设计的简单还是复杂取决于业务逻辑的复杂度。

            先来个简单例子(之后都是用这个例子):一个Classroom有多个Student,每个Student有个Score

            sql脚本:

    CREATE TABLE `classroom` (
      `id` int(11) NOT NULL AUTO_INCREMENT,
      `name` varchar(32) NOT NULL,
      PRIMARY KEY (`id`)
    ) 
    
    CREATE TABLE `student` (
      `id` int(11) NOT NULL AUTO_INCREMENT,
      `cid` int(11) NOT NULL,
      `name` varchar(16) NOT NULL,
      PRIMARY KEY (`id`),
      KEY `cs_id` (`cid`),
      CONSTRAINT `cs_id` FOREIGN KEY (`cid`) REFERENCES `classroom` (`id`)
    ) 
    
    CREATE TABLE `score` (
      `id` int(11) NOT NULL AUTO_INCREMENT,
      `sid` int(11) NOT NULL,
      `course_name` varchar(32) NOT NULL,
      `score` tinyint(4) NOT NULL,
      PRIMARY KEY (`id`,`sid`,`course_name`),
      KEY `sc_id` (`sid`),
      CONSTRAINT `sc_id` FOREIGN KEY (`sid`) REFERENCES `student` (`id`)
    ) 

            

            领域模型抽象基类:DomainObject

    namespace demodomain;
    
    /**
     * 领域模型抽象基类
     */
    abstract class DomainObject {
    	protected  $id;
    	
    	public function __construct($id = null) {
    		if (is_null($id)) {
    			$id = -1;
    		} else {
    			$this->id = $id;
    		}
    	}
    	
    	public function getId() {
    		return $this->id;
    	}
    	
    	public function setId($id) {
    		$this->id = $id;
    	}
    	
    	// 现在比较简单,之后还会扩展
    	// ......
    }


             Score类(对应Score表):

    namespace demodomain;
    
    use demodomainStudent;
    
    /**
     * Score 
     * 对应表score
     */
    class Score extends DomainObject {
    	private $score;
    	private $courseName;
    	// Student对象引用
    	private $student;
    	
    	public function __construct($id = null, $score = 0, $courseName = 'unknown') {
    		parent::__construct($id);
    		$this->score = $score;
    		$this->courseName = $courseName;
    	}
    	
    	public function getScore() {
    		return $this->score;
    	}
    	
    	public function setScore($score) {
    		$this->score = $score;
    	}
    	
    	public function setCourseName($courseName) {
    		$this->courseName = $courseName;
    	}
    	
    	public function getCourseName() {
    		return $this->courseName;
    	}
    	
    	public function getStudent() {
    		return $this->student;
    	}
    	
    	public function setStudent(Student $student) {
    		$this->student = $student;
    	}
    }


            之前说到领域模型最复杂的是映射到数据库结构。我们可以使用数据映射器模式。

            数据映射器是一个负责将数据库中的一行数据映射到一个对象的类。

            有个概念叫“对象关系阻抗不匹配”,指的是对象和关系数据库性质上的差异,比如对象可以有复杂的继承层次,对象中还可以包含另一个对象(关系型数据库的表不行吧),而表可以通过外键表示与其他表之间的关联等。

            来看看Mapper的类层次图吧:

            

            Mapper抽象基类:

    namespace demomapper;
    
    use demoaseAppException;
    use demoaseApplicationRegistry;
    
    /**
     * Mapper
     */
    abstract  class Mapper {
    	// PDO
    	protected static $PDO;
    	// config
    	protected static  $dsn, $dbUserName, $dbPassword;
    	// PDO选项
    	protected static $options = array(
        	PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8',
        	PDO::ATTR_ERRMODE,
        	PDO::ERRMODE_EXCEPTION,
    	); 
    	
    	public function __construct() {
    		if (!isset(self::$PDO)) {
    			// ApplicationRegistry获取数据库连接信息
    			$appRegistry = ApplicationRegistry::getInstance();
    			self::$dsn = $appRegistry->getDsn();
    			self::$dbUserName = $appRegistry->getDbUserName();
    			self::$dbPassword = $appRegistry->getDbPassword();
    			
    			if (!self::$dsn || !self::$dbUserName || !self::$dbPassword) {
    				throw  new AppException('Mapper init failed!');
    			}
    			
    			self::$PDO = new PDO(self::$dsn, self::$dbUserName, self::$dbPassword, self::$options);
    		}
    	}
    	
    	/**
    	 * 查找指定ID
    	 * @param int $id
    	 */
    	public function findById($id) {
    		$pStmt = $this->getSelectStmt();		
    		$pStmt->execute(array($id));	
    		$data = $pStmt->fetch();		
    		$pStmt->closeCursor();		
    		
    		$obj = null;
    		if (!is_array($data) || !isset($data['id'])) {
    			return $obj;
    		}
    		
    		$obj = $this->createObject($data);
    		return $obj;
    	}
    	
    	/**
    	 * 插入数据
    	 * @param demodomainDomainObject $obj
    	 */
    	public function insert(demodomainDomainObject $obj) {
    		return $this->doInsert($obj);
    	}
    	
    	/**
    	 * 删除指定ID
    	 * @param int $id
    	 */
    	public function deleteById($id) {
    		$pStmt = $this->getDeleteStmt();
    		$flag = $pStmt->execute(array($id));
    		
    		return $flag;
    	}
    	
    	/**
    	 * 生成一个$data中值属性的对象
    	 * @param array $data
    	 */
    	public function createObject(array $data) {
    		$obj = $this->doCreateObject($data);
    		return $obj;
    	}
    	
    	public abstract function update(demodomainDomainObject $obj);
    	
    	protected abstract function doInsert(demodomainDomainObject $obj);
    	
    	protected abstract function doCreateObject(array $data);
    	
    	protected abstract function getSelectStmt();
    	
    	protected abstract function getDeleteStmt();
    }


            一个具体Mapper子类ScoreMapper:

    namespace demomapper;
    
    use demoaseAppException;
    use demodomainDomainObject;
    use demodomainScore;
    use demomapperStuStudentMapper;
    
    /**
     * ScoreMapper
     */
    class ScoreMapper  extends Mapper {
    	private  static $selectStmt;
    	private  static $insertStmt;
    	private  static $updateStmt;
    	private  static $deleteStmt;
    	private  static $init = false;
    	
    	public function __construct() {
    		if (!self::$init) {
    			parent::__construct();
    		
    			$selectSql = 'select * from score where id = ?';
    			$insertSql = 'insert into score (sid, course_name, score) values (?, ?, ?)';
    			$updateSql = 'update score set sid = ?, course_name = ?, score = ? where id = ?';
    			$deleteSql = 'delete from score where id = ?';
    			
    			// 预编译生成prepareStatement对象
    			self::$selectStmt = self::$PDO->prepare($selectSql);
    			self::$insertStmt = self::$PDO->prepare($insertSql);
    			self::$updateStmt = self::$PDO->prepare($updateSql);
    			self::$deleteStmt = self::$PDO->prepare($deleteSql);
    			self::$init = true;
    		}
    	}
    	
    	public function update(DomainObject $obj) {
    		// 类型安全检查
    		// if (!($obj instanceof Score)) {
    		//	  throw new AppException('Object is not instance of Student');	
    		// }
    		
    		$data = array($obj->getStudent()->getId()
    				, $obj->getCourseName(), $obj->getScore(), $obj->getId());
    		$flag = self::$updateStmt->execute($data);
    		return $flag;
    	}	
    	
    	protected function doInsert(DomainObject $obj) {
    		$data = array($obj->getStudent()->getId() , $obj->getCourseName(), $obj->getScore());
    		$flag = self::$insertStmt->execute($data);
    			
    		// 数据行返回设置对象
    		if ($flag) {
    			$lastId = self::$PDO->lastInsertId();
    			$obj->setId($lastId);
    		}
    		
    		return $flag;
    	}
    
    	protected  function doCreateObject(array $data) {
    		$obj = new Score($data['id']);
    		$obj->setScore($data['score']);
    		$obj->setCourseName($data['course_name']);
    		// setStudent()
    		$stuMapper = new StudentMapper();
    		$stuObj = $stuMapper->findById($data['sid']);
    		$obj->setStudent($stuObj);
    		
    		return $obj;
    	}
    	
    	protected  function getSelectStmt() {
    		return self::$selectStmt;
    	}
    	
    	protected function getDeleteStmt() {
    		return self::$deleteStmt;
    	}
    }


            使用的例子:

    $score = new Score(0);
    $scoreMapper = new ScoreMapper();
    $score->setCourseName('Math');
    // 插入
    $scoreMapper->insert($score);
    // 查找
    $score = $scoreMapper->findById($score->getId());
    var_dump($score);
    $score->setCourseName('English');
    // 更新
    $scoreMapper->update($score);
    // 删除
    $scoreMapper->deleteById($score->getId());


             数据映射器的好处是消除了领域层和数据库操作之间的耦合,Mapper可以应用各种对象关系映射。比如insert、update的传递的参数是DomainObject对象,保存到数据库的是数据行;findById把数据库的数据行转换成DomainObject对象。 而它的缺点是需要创建大量的具体的映射器类,不过大部分都是相似的代码,也可以通过反射机制来生成这些相似的代码。

            findById是获取一条数据,而findAll是获取一个数据集,那么需要一个什么对象来保持和数据集的映射才比较好呢?接下来介绍Collection对象。

  • 相关阅读:
    2020.10.23 19级training 补题报告
    2020.10.17 天梯赛练习 补题报告
    2020.10.16 19级training 补题报告
    2020.10.9 19级training 补题报告
    2020.10.10 天梯赛练习 补题报告
    2020.10.3 天梯赛练习 补题报告
    2020.10.2 19级training 补题报告
    第十届山东省ACM省赛复现补题报告
    VVDI Key Tool Plus Adds VW Passat 2015 Key via OBD
    Xhorse VVDI Prog Software V5.0.3 Adds Many MCUs
  • 原文地址:https://www.cnblogs.com/phisy/p/3371867.html
Copyright © 2011-2022 走看看