面向对象设计原则 Dependency Inversion Principle 依赖倒置原则 php实现 面向对象
Dependency Inversion Principle 依赖倒置原则 dip
dip有两个原则
1.高层模块不应该依赖于底层模块,二者都应该依赖抽象
2.抽象不应该依赖细节,细节应该依赖于抽象
举一个例子
php后端项目,写一个列表数据的接口,带分页功能
输入的参数 有两个 page和per_page,
输出接口的字段有
total 数据的总数
per_page 每页数
current_page 当前页数
collection 具体的数据集合
以下是mysql的表结构
CREATE TABLE ba_admin_rule (
id int unsigned NOT NULL AUTO_INCREMENT COMMENT 'ID',
pid int unsigned NOT NULL DEFAULT '0' COMMENT '上级菜单',
type enum('menu_dir','menu','button') COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'menu' COMMENT '类型:menu_dir=菜单目录,menu=菜单项,button=页面按钮',
title varchar(50) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '标题',
name varchar(50) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '规则名称',
path varchar(100) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '路由路径',
icon varchar(50) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '图标',
menu_type enum('tab','link','iframe') COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '菜单类型:tab=选项卡,link=链接,iframe=Iframe',
url varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT 'Url',
component varchar(100) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '组件路径',
keepalive tinyint unsigned NOT NULL DEFAULT '0' COMMENT '缓存:0=关闭,1=开启',
extend enum('none','add_rules_only','add_menu_only') COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'none' COMMENT '扩展属性:none=无,add_rules_only=只添加为路由,add_menu_only=只添加为菜单',
remark varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '备注',
weigh int NOT NULL DEFAULT '0' COMMENT '权重',
status enum('0','1') COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '1' COMMENT '状态:0=禁用,1=启用',
update_time bigint unsigned DEFAULT NULL COMMENT '更新时间',
create_time bigint unsigned DEFAULT NULL COMMENT '创建时间',
PRIMARY KEY (id),
KEY pid (pid)
) ENGINE=InnoDB AUTO_INCREMENT=91 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC COMMENT='菜单和权限规则表';
生成的pojo类
<?php
namespace App\Http\Pojo;
class AdminRulePojo
{
private int $id;
private int $pid = 0;
private string $type = 'menu';
private string $title = '';
private string $name = '';
private string $path = '';
private string $icon = '';
private ?string $menu_type = null;
private string $url = '';
private string $component = '';
private int $keepalive = 0;
private string $extend = 'none';
private string $remark = '';
private int $weigh = 0;
private string $status = '1';
private ?int $update_time = null;
private ?int $create_time = null;
public function getId(): int
{
return $this->id;
}
public function setId(int $id): void
{
$this->id = $id;
}
public function getPid(): int
{
return $this->pid;
}
public function setPid(int $pid): void
{
$this->pid = $pid;
}
public function getType(): string
{
return $this->type;
}
public function setType(string $type): void
{
$this->type = $type;
}
public function getTitle(): string
{
return $this->title;
}
public function setTitle(string $title): void
{
$this->title = $title;
}
public function getName(): string
{
return $this->name;
}
public function setName(string $name): void
{
$this->name = $name;
}
public function getPath(): string
{
return $this->path;
}
public function setPath(string $path): void
{
$this->path = $path;
}
public function getIcon(): string
{
return $this->icon;
}
public function setIcon(string $icon): void
{
$this->icon = $icon;
}
public function getMenuType(): ?string
{
return $this->menu_type;
}
public function setMenuType(?string $menu_type): void
{
$this->menu_type = $menu_type;
}
public function getUrl(): string
{
return $this->url;
}
public function setUrl(string $url): void
{
$this->url = $url;
}
public function getComponent(): string
{
return $this->component;
}
public function setComponent(string $component): void
{
$this->component = $component;
}
public function getKeepalive(): int
{
return $this->keepalive;
}
public function setKeepalive(int $keepalive): void
{
$this->keepalive = $keepalive;
}
public function getExtend(): string
{
return $this->extend;
}
public function setExtend(string $extend): void
{
$this->extend = $extend;
}
public function getRemark(): string
{
return $this->remark;
}
public function setRemark(string $remark): void
{
$this->remark = $remark;
}
public function getWeigh(): int
{
return $this->weigh;
}
public function setWeigh(int $weigh): void
{
$this->weigh = $weigh;
}
public function getStatus(): string
{
return $this->status;
}
public function setStatus(string $status): void
{
$this->status = $status;
}
public function getUpdateTime(): ?int
{
return $this->update_time;
}
public function setUpdateTime(?int $update_time): void
{
$this->update_time = $update_time;
}
public function getCreateTime(): ?int
{
return $this->create_time;
}
public function setCreateTime(?int $create_time): void
{
$this->create_time = $create_time;
}
}
先声明接口的字段,这样可以快速跟前端对接。
{
"code": 0,
"message": "成功",
"data": {
"conllection": [
{
"id": 76,
"pid": 0,
"type": "menu",
"title": "",
"name": "",
"path": "",
"icon": "local-logo",
"menu_type": null,
"url": "",
"component": "",
"keepalive": 0,
"extend": "none",
"remark": "",
"weigh": 0,
"status": "1",
"update_time": null,
"create_time": null
}
],
"current_page": 1,
"per_page": 1,
"total": 1
}
}
这个是定义好的分页数据返回类,
<?php
namespace App\Http\Pagination;
use App\Http\Pojo\AdminRulePojo;
class AdminRulePagination
{
// use Pagination;
protected int $current_page;
protected int $per_page;
protected int $total;
protected array $conllection = [];
public function getConllection(): array
{
return $this->conllection;
}
public function addConllection(AdminRulePojo $adminRulePojo): void
{
$this->conllection[] = $adminRulePojo;
}
public function getCurrentPage(): int
{
return $this->current_page;
}
public function setCurrentPage(int $current_page): void
{
$this->current_page = $current_page;
}
public function getPerPage(): int
{
return $this->per_page;
}
public function setPerPage(int $per_page): void
{
$this->per_page = $per_page;
}
public function getTotal(): int
{
return $this->total;
}
public function setTotal(int $total): void
{
$this->total = $total;
}
}
因为php不支持泛型。所以用以下的方法来实现同一对象集合
protected array $conllection = [];
public function getConllection(): array
{
return $this->conllection;
}
public function addConllection(AdminRulePojo $adminRulePojo): void
{
$this->conllection[] = $adminRulePojo;
}
然后定义list返回数据的接口,定义好接收的参数和返回的类型.
<?php
namespace App\Http\Service;
use App\Http\Pagination\AdminRulePagination;
interface IAdminRuleService
{
public function list(int $per_page) :AdminRulePagination;
}
接口实现类
<?php
namespace App\Http\Service\impl;
use App\Http\Pagination\AdminRulePagination;
use App\Http\Pojo\AdminRulePojo;
use App\Http\Service\IAdminRuleService;
use App\Models\AdminRuleModel;
class AdminRuleService implements IAdminRuleService
{
private AdminRuleModel $adminRuleModel;
private AdminRulePagination $adminRulePagination;
public function __construct(AdminRuleModel $adminRuleModel,AdminRulePagination $adminRulePagination)
{
$this->adminRuleModel=$adminRuleModel;
$this->adminRulePagination=$adminRulePagination;
}
public function list(int $per_page) :AdminRulePagination
{
$res = $this->adminRuleModel::where('status', 1)->paginate($per_page)->toArray();
if (!empty($res['data'])) {
foreach ($res['data'] as $item) {
$AdminRulePojo = new AdminRulePojo();
$AdminRulePojo->setComponent($item['component']);
$AdminRulePojo->setId($item['id']);
$AdminRulePojo->setIcon($item['icon']);
$this->adminRulePagination->addConllection($AdminRulePojo);
}
}
$this->adminRulePagination->setCurrentPage($res['current_page']);
$this->adminRulePagination->setTotal($res['total']);
$this->adminRulePagination->setPerPage($res['per_page']);
return $this->adminRulePagination;
}
}
使用laravel的服务容器,控制反转,来绑定接口和实现类。
$this->app->bind(IAdminRuleService::class, AdminRuleService::class);
定义好接口
之后的实现类。可以换成原生sql查询的。其他数据库查询的。
比如mangodb获取数据的。
<?php
namespace App\Http\Service\impl;
use App\Http\Pagination\AdminRulePagination;
use App\Http\Pojo\AdminRulePojo;
use App\Http\Service\IAdminRuleService;
use App\Models\AdminRuleModel;
use Illuminate\Support\Facades\DB;
class MangoDbAdminRuleService implements IAdminRuleService
{
private AdminRulePagination $adminRulePagination;
public function __construct(AdminRulePagination $adminRulePagination)
{
$this->adminRulePagination=$adminRulePagination;
}
public function list(int $per_page) :AdminRulePagination
{
}
}
在服务容器那里改一下绑定就可以了。
$this->app->bind(IAdminRuleService::class, MangoDbAdminRuleService::class);
这个是控制器,声明接口变量IAdminRuleService
<?php
namespace App\Http\ServiceController;
use App\Http\Service\IAdminRuleService;
class AdminRule
{
use ServiceController;
use Response;
private IAdminRuleService $adminRuleService;
public function __construct(IAdminRuleService $adminRuleService)
{
$this->adminRuleService = $adminRuleService;
}
public function list(int $per_page): array
{
$obj = $this->adminRuleService->list($per_page);
$data = $this->objectToArray($obj);
$this->setData($data);
return $this->Success();
}
}
以上就是这个依赖倒置的例子。
这个php的api接口查询数据的例子。可以随时换框架,随时换数据库操作类。使用orm或者sql查询的都行,也可以换数据库类型。
控制器作为高层模块,不依赖低层service层模块。两者通过接口和定义好的数据类AdminRulePagination来交换数据。
符合了依赖倒置的两大原则
1.高层模块不应该依赖于底层模块,二者都应该依赖抽象
2.抽象不应该依赖细节,细节应该依赖于抽象
抽象不依赖细节,按这个例子的情况就是,控制器层依赖IAdminRuleService接口,而不是依赖具体的实现类。
这个例子有点复杂。说了一些泛型的东西。但是实现这个过程中,会遇到php的缺点和不足。如果以上的例子用java实现。那就会容易一些。