• [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对象。

  • 相关阅读:
    .Net开源微型ORM框架测评
    使用SQL-Server分区表功能提高数据库的读写性能
    C# 打印PDF文档的10种方法
    使用SQL-Server分区表功能提高数据库的读写性能
    oracle查看执行最慢与查询次数最多的sql语句及其执行速度很慢的问题分析
    Oracle中取日斯的sql语句
    高并发系统设计(二十六):【配置中心】成千上万的配置项要如何管理?
    高并发系统设计(二十五):【压力测试】怎样设计全链路压力测试平台?
    高并发系统设计(二十四):服务端监控要怎么做?
    minio搭建对象存储服务
  • 原文地址:https://www.cnblogs.com/phisy/p/3371867.html
Copyright © 2020-2023  润新知