PHP构建对象和类的策略的几种设计模式
2018-07-01 本文已影响0人
鸿雁长飞光不度
1.组合设计模式
组合设计模式.png 改进的组合设计.png一个游戏里面的对象,包含射手,激光炮等基础的战斗单元,基础的战斗单元不能包含其他基础的战斗单元,但是可以被其他的非基础的战斗单元包含, 比如军队,战舰等,当然也有个别的例外,比如步兵类型的不能被战舰包含。但是不论是基础战斗元素和非基础战斗元素都可以获得攻击强度,防御能力(简化代码暂时不写)
改进后的组合模式把原来一个抽象类做了两个存在继承关系的抽象类,单独区分了基础元素和组合元素,把添加和移除元素的方法放在了compositeUnit里面。
组合模式定义了单一的集成体系,使具有截然不同职责的集合可以一起工作,比如下面案例的单个元素和集合元素都可以添加在集合元素里面,获取的方法也是一样的。
优点:
- 灵活:一切类都共享了一个父类型,轻松的添加新的组合对象,无需修改代码。
- 简单:只需要设计简单的接口,不用区分是基础对象还是集合对象,获取攻击强度都用同一个方法。
- 隐式到达:对象通过属性组织结构,每个对象都包含子对象的引用,对树里面某一部分操作会产生很大影响。比如移除一个军队集合到另一个军队里面的时候,下面所有的包含的对象全部都会改变。
- 显示到达:树的结构可以轻松遍历,必须下面在对添加元素的方法用isExist函数做了判断,防止子元素的中有重复的元素被加入。
缺点:
数据不适合保存在关系数据库,适合保存在XML文件。组合操作的成本有时候比较高,比如获得攻击强度要递归计算所有的元素,如果中间包含一些额外的计算可能成本比较高。
<?php
/**
* 一个游戏里面的对象,包含射手,激光炮等基础的战斗单元,基础的战斗单元不能包含其他基础的战斗单元,但是可以被其他的非基础的战斗单元包含,
* 比如军队,战舰等,当然也有个别的例外,比如步兵类型的不能被战舰包含。但是不论是基础战斗元素和非基础战斗元素都可以获得攻击强度,防御能力(暂时不写)
*/
//叶子元素应该继承的抽象类
abstract class Unit
{
//非叶子元素在具体类返回自己,叶子元素返回null
protected function getComposite()
{
return null;
}
//默认值1
function bombardStrength()
{
return 1;
}
}
//非叶子元素类应该继承的抽象类
abstract class CompositeUnit extends Unit
{
private $units = array();
public function getUnits() {
return $this->units;
}
protected function getComposite()
{
return $this->units;
}
function removeUnit(Unit $unit)
{
//移除元素
$this->units = array_udiff($this->units, array($unit), function ($val, $val2) {
return $val === $val2 ? 0 : 1;
});
return $this->units;
}
function addUnit(Unit $unit)
{
if (in_array($unit, $this->units, true)) {
return;
}
//为了防止递归中的重复,可以递归的查找unit下面的所有叶子元素的内容
// $result = $this->isExist($unit,$this);
// if ($result){
// return;
// }
$this->units[] = $unit;
}
//判断是否存在,递归查找
protected function isExist(Unit $newUnit,CompositeUnit $unitObject)
{
$isExist = false;
foreach ($unitObject->getUnits() as $unit) {
if ($unit instanceof Unit) { //仅仅为了代码提示做的判断
if (is_null($unit->getComposite())) { //叶子元素直接比较
if ($unit === $newUnit){
$isExist = true;
return $isExist;
}else{
$isExist = false;
}
}else{
if ($unit instanceof CompositeUnit){
$isExist |= $this->isExist($newUnit,$unit);
if ($isExist){
return true;
}
}
}
}
}
return $isExist;
}
function bombardStrength()
{
$ret = 0;
foreach ($this->units as $unit) {
if ($unit instanceof Unit) { //仅仅为了代码提示做的判断
$ret += $unit->bombardStrength();
}
}
return $ret;
}
}
//射手
class Archer extends Unit
{
//返回攻击强度
function bombardStrength()
{
return 2;
}
}
//激光炮
class LaserCannotUnit extends Unit {
function bombardStrength()
{
return 10;
}
}
class UnitException extends Exception {
public function __construct(string $message = "", int $code = 0, Throwable $previous = null)
{
parent::__construct($message, $code, $previous);
}
}
//军队元素
class Army extends CompositeUnit
{
function addUnit(Unit $unit)
{
//特殊的条件可以自己判断
// if ($unit instanceof LaserCannotUnit){
// throw new UnitException('军队里面不允许添加激光武器',101);
// }
return parent::addUnit($unit); // TODO: Change the autogenerated stub
}
}
$army1 = new Army();
$archer = new Archer();
$archer2 = new Archer();
$laser = new LaserCannotUnit();
$army1->addUnit($archer);
$army1->addUnit($archer2);
$army1->addUnit($laser);
echo $army1->bombardStrength()."\n";
$army2 = new Army();
$army2->addUnit($army1);
$army2->addUnit($archer);
$army2->addUnit($laser);
echo $army2->bombardStrength()."\n";
2.装饰模式
web应用需要在返回的给用户响应之前执行一系列的操作,例如验证用户的请求,将原始输入处理成某种数据结构,最后必须执行和操作。
class RequestHelper{
public $ip = '';
public function __construct($ip)
{
$this->ip = $ip;
}
}
abstract class ProcessRequest{
abstract function process (RequestHelper $req);
}
//主要的处理
class MainProcess extends ProcessRequest
{
function process(RequestHelper $req)
{
echo __CLASS__ ."do something".$req->ip;
}
}
//装饰器接口继承请求继承基础抽象类
abstract class DecorateProcess extends ProcessRequest {
protected $processRequest;
function __construct(ProcessRequest $pr)
{
$this->processRequest = $pr;
}
}
//记录日志
class LogRequest extends DecorateProcess {
function process(RequestHelper $req)
{
echo __CLASS__ .'LOG REQUEST';//先执行装饰器的操作,然后执行请求内部的操作
$this->processRequest->process($req);
}
}
class StructRequest extends DecorateProcess {
function process(RequestHelper $req)
{
echo __CLASS__ .'Struct REQUEST';
$this->processRequest->process($req);
}
}
$process = new StructRequest(new LogRequest(new MainProcess()));
$process->process(new RequestHelper('127.0.0.1'));
打印的顺序是按照从外到里的装饰顺序打印,可以任意的装饰和设置先后顺序。当需要改变执行顺序的时候避免了再代码中调整函数顺序,只要更改装饰顺序就可以了。
3.外观模式
主要用来为一个分层或者子系统创建一个单一的入口。
function getProductFileLines($file) {
return array();
}
function getIdFromLine($line){return 1;}
function getNameFromLine($line){return 'name';}
function getProductObject($id,$name){return ['id' => $id, 'name' => $name;]}
class ProductFacade {
private $products = array();
private $file;
function __construct($file)
{
$this->file = $file;
}
private function compile()
{
$lines = getProductFileLines($this->file);
foreach ($lines as $line) {
$id = getIdFromLine($line);
$name = getNameFromLine($line);
$this->products[$id] = getProductObject($id,$name);
}
}
public function getProduct($id)
{
return $this->products[$id];
}
public function getProducts()
{
return $this->products;
}
}
$facade = new ProductFacade('product.txt');
$facade->getProducts();
写一个类封装面向过程代码,调用起来简单。不用每次都这样写,以后需要改变的时候改变一个地方就可以了
$lines = getProductFileLines("product.txt");
$obejcts = [];
foreach ($lines as $line) {
$id = getIdFromLine($line);
$name = getNameFromLine($line);
$obejcts[] = getProductObject($id,$name);
}