This commit is contained in:
cloud 2022-04-13 06:00:06 +08:00
parent 5c906d7096
commit 0ef4f6a1e4
15 changed files with 514 additions and 2 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
.idea

8
.idea/.gitignore vendored Normal file
View File

@ -0,0 +1,8 @@
# 默认忽略的文件
/shelf/
/workspace.xml
# 基于编辑器的 HTTP 客户端请求
/httpRequests/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml

View File

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="WEB_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$" isTestSource="false" />
<excludeFolder url="file://$MODULE_DIR$/.idea/inspectionProfiles" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

8
.idea/modules.xml Normal file
View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/designModeDemo.git.iml" filepath="$PROJECT_DIR$/.idea/designModeDemo.git.iml" />
</modules>
</component>
</project>

6
.idea/vcs.xml Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>

View File

@ -1,3 +1,34 @@
# designModeDemo
# 设计模式演示
### PHP 示例代码 https://github.com/DesignPatternsPHP/DesignPatternsPHP
### PHP PSR 标准规范https://learnku.com/docs/psr
### PHP 设计模式全集https://learnku.com/docs/php-design-patterns/2018
### PHP 设计模式全集https://laravelacademy.org/books/php-design-pattern
### 环境PHP8.1.4
设计模式演示
# 学习顺序
### PHP 学习顺序 https://www.cnblogs.com/guola/archive/2013/01/02/2841997.html
| 学习顺序 | 设计模式 | 常用程度 | 适用层次 | 引入时机 | 结构复杂度 | 变化 | 实现 | 体现的原则 |
| ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- |--------|
| 1 | 工厂方法模式Factory Method | 很常用 | 代码级 | 编码时 | 简单 | 子类的实例化 | 对象的创建工作延迟到子类 | 开闭原则 |
| 2 | 单例模式Singleton | 很常用 | 代码级、应用级 | 设计时、编码时 | 简单 | 唯一实例 | 封装对象产生的个数 | |
| 3 | 门面模式Facade | 很常用 | 应用级、构架级 | 设计时、编码时 | 简单 | 子系统的高层接口 | 封装子系统 | 开闭原则 |
| 4 | 模板方法模式Template Method | 很常用 | 代码级 | 编码时、重构时 | 简单 | 算法子步骤的变化 | 封装算法结构 | 依赖倒置原则 |
| 5 | 抽象工厂模式Abstract Factory | 比较常用 | 应用级 | 设计时 | 比较复杂 | 产品家族的扩展 | 封装产品族系列内容的创建 | 开闭原则 |
| 6 | 组合模式Composite | 比较常用 | 代码级 | 编码时、重构时 | 比较复杂 | 复杂对象接口的统一 | 统一复杂对象的接口 | 里氏代换原则 |
| 7 | 代理模式Proxy | 比较常用 | 应用级、构架级 | 设计时、编码时 | 简单 | 对象访问的变化 | 封装对象的访问过程 | 里氏代换原则 |
| 8 | 命令行模式Command | 比较常用 | 应用级 | 设计时、编码时 | 比较简单 | 请求的变化 | 封装行为对对象 | 开闭原则 |
| 9 | 观察者模式Observer | 比较常用 | 应用级、构架级 | 设计时、编码时 | 比较简单 | 通讯对象的变化 | 封装对象通知 | 开闭原则 |
| 10 | 策略模式Strategy | 比较常用 | 应用级 | 设计时 | 一般 | 算法的变化 | 封装算法 | 里氏代换原则 |
| 11 | 建造者模式Builder | 一般 | 代码级 | 编码时 | 一般 | 对象组建的变化 | 封装对象的组建过程 | 开闭原则 |
| 12 | 适配器模式Adapter | 一般 | 代码级 | 重构时 | 一般 | 对象接口的变化 | 接口的转换 | |
| 13 | 桥梁模式Bridge | 一般 | 代码级 | 设计时、编码时 | 一般 | 对象的多维度变化 | 分离接口以及实现 | 开闭原则 |
| 14 | 装饰模式Decorator | 一般 | 代码级 | 重构时 | 比较复杂 | 对象的组合职责 | 在稳定接口上扩展 | 开闭原则 |
| 15 | 迭代器模式Iterator | 一般 | 代码级、应用级 | 编码时、重构时 | 比较简单 | 对象内部集合的变化 | 封装对象内部集合的使用 | 单一职责原则 |
| 16 | 中介者模式Mediator | 一般 | 应用级、构架级 | 编码时、重构时 | 一般 | 对象交互的变化 | 封装对象间的交互 | 开闭原则 |
| 17 | 备忘录模式Memento | 一般 | 代码级 | 编码时 | 比较简单 | 状态的辅助保存 | 封装对象状态的变化 | 接口隔离原则 |
| 18 | 状态模式State | 一般 | 应用级 | 设计时、编码时 | 一般 | 对象状态的变化 | 封装与状态相关的行为 | 单一职责原则 |
| 19 | 访问者模式Visitor | 一般 | 应用级 | 设计时 | 比较复杂 | 对象操作变化 | 封装对象操作变化 | 开闭原则 |
| 20 | 原型模式Prototype | 不太常用 | 应用级 | 编码时、重构时 | 比较简单 | 实例化的类 | 封装对原型的拷贝 | 依赖倒置原则 |
| 21 | 享元模式Flyweight | 不太常用 | 代码级、应用级 | 设计时 | 一般 | 系统开销的优化 | 封装对象的获取 | |
| 22 | 责任链模式Chain Of Responsibilities | 不太常用 | 应用级、构架级 | 设计时、编码时 | 比较复杂 | 对象的请求过程 | 封装对象的责任范围 | |
| 23 | 解释器模式Interpreter | 不太常用 | 应用级 | 设计时 | 比较复杂 | 领域问题的变化 | 封装特定领域的变化 | |

139
bin/Base.php Normal file
View File

@ -0,0 +1,139 @@
<?php
namespace bin;
use ReflectionClass;
/**
* 基础类
*/
class Base
{
/**
* @var string 源目录名
*/
protected string $dir = 'demo';
/**
* @var array 命名空间列表
*/
protected array $dirFiles = [];
/**
* @var string|mixed 获取url参数
*/
protected string $queryString = '';
/**
* 初始化入口
*/
public function __construct()
{
if ($this->queryString = $_SERVER['QUERY_STRING']) {
self::init();
} else {
self::setDirFiles();
self::outDirFiles();
}
}
/**
* 通过命名空间调用对应类方法
*
* @return void
*/
public function init()
{
try {
// 初始化类,再执行对应方法
$class = $this->newClass($this->queryString);
$class->main();
} catch (\ReflectionException $e) {
var_dump($e->getMessage());
}
echo "<div style='display: block;margin-top: 100px;'>
<a href='/'><span style='font-size: 24px; color:green'>返回首页</span></a>
</div>";
}
/**
* 输出可访问的命名空间路径到页面
*
* @return void
*/
public function outDirFiles()
{
$li = '';
foreach ($this->dirFiles as $file) {
$newClass = $this->newClass($file);
$designUrl = '';
try {
if (is_object($newClass)) {
$designName = $newClass->designName;
$designUrl = $newClass->designRefUrl;
} else {
$designName = $newClass;
}
} catch (\Exception $e) {
$designName = $e->getMessage();
}
$nameUrl = $designUrl === '' ? $designName : "<a href='$designUrl' target='_blank'>$designName</a>";
$li .= "<li>$nameUrl <a href='index.php?$file'>$file</a></li>";
}
echo "<div><ul>$li</ul></div>";
}
/**
* 根据命名空间获取对应对象
*
* @param $namespace
* @return object|string
*/
protected function newClass($namespace) : object|string
{
try {
// 先获取类信息
$reflect = new ReflectionClass($namespace);
// 初始化类,再执行对应方法
return $reflect->newInstanceArgs();
} catch (\ReflectionException $e) {
return $e->getMessage();
}
}
/**
* 设置demo所有可用文件
*
* @return void
*/
protected function setDirFiles()
{
// $dir = ROOTDIR . DIRECTORY_SEPARATOR . $this->dir;
$this->scandir($this->dir);
}
/**
* 循环目录
*
* @param $dir
* @return void
*/
protected function scandir($dir)
{
$dirFiles = scandir($dir);
$oldDir = $dir . DIRECTORY_SEPARATOR;
foreach ($dirFiles as $file) {
if ($file !== '.' && $file !== '..') {
$newsDir = $oldDir . $file;
if (is_dir($newsDir)) {
$this->scandir($newsDir);
} elseif ($file === 'Run.php') {
// 这样可以屏蔽非入口
$this->dirFiles[] = $oldDir . 'Run';
}
}
}
}
}

29
bin/Design.php Normal file
View File

@ -0,0 +1,29 @@
<?php
namespace bin;
class Design
{
/**
* 对应设计模式名称
*
* @var string
*/
public string $designName;
/**
* 参考地址
*
* @var string
*/
public string $designRefUrl;
/**
* 统一返回错误信息
*
* @throws \Exception
*/
public function __get($name)
{
throw new \Exception("$name 未定义");
}
}

144
bin/Psr4Autoload.php Normal file
View File

@ -0,0 +1,144 @@
<?php
namespace bin;
class Psr4Autoload
{
/**
* 关联数组,键名为命名空间前缀,键值为一个基本目录数组。
*
* @var array
*/
protected array $prefixes = [];
/**
* 通过 SPL 自动加载器栈注册加载器
*
* @return void
*/
public function register()
{
spl_autoload_register(array($this, 'loadClass'));
$this->addNamespace('bin', ROOTDIR . DIRECTORY_SEPARATOR . 'bin', true);
$this->addNamespace('demo', ROOTDIR . DIRECTORY_SEPARATOR . 'demo', true);
$this->addNamespace('mode', ROOTDIR . DIRECTORY_SEPARATOR . 'mode', true);
}
/**
* 为命名空间前缀添加一个基本目录
*
* @param string $prefix 命名空间前缀。
* @param string $base_dir 命名空间下类文件的基本目录
* @param bool $prepend 如果为真,预先将基本目录入栈
* 而不是后续追加;这将使得它会被首先搜索到。
* @return void
*/
public function addNamespace(string $prefix, string $base_dir, bool $prepend = false)
{
// 规范化命名空间前缀
$prefix = trim($prefix, '\\') . '\\';
// 规范化尾部文件分隔符
$base_dir = rtrim($base_dir, DIRECTORY_SEPARATOR) . '/';
// 初始化命名空间前缀数组
if (isset($this->prefixes[ $prefix ]) === false) {
$this->prefixes[ $prefix ] = array();
}
// 保留命名空间前缀的基本目录
if ($prepend) {
array_unshift($this->prefixes[ $prefix ], $base_dir);
} else {
array_push($this->prefixes[ $prefix ], $base_dir);
}
}
/**
* 加载给定类名的类文件
*
* @param string $class 合法类名
* @return string|bool 成功时为已映射文件名,失败则为 false
*/
public function loadClass(string $class)
{
// 当前命名空间前缀
$prefix = $class;
// 通过完整的命名空间类名反向映射文件名
while (false !== $pos = strrpos($prefix, '\\')) {
// 在前缀中保留命名空间分隔符
$prefix = substr($class, 0, $pos + 1);
// 其余的是相关类名
$relative_class = substr($class, $pos + 1);
// 尝试为前缀和相关类加载映射文件
$mapped_file = $this->loadMappedFile($prefix, $relative_class);
if ($mapped_file) {
return $mapped_file;
}
// 删除 strrpos() 下一次迭代的尾部命名空间分隔符
$prefix = rtrim($prefix, '\\');
}
// 找不到映射文件
return false;
}
/**
* 为命名空间前缀和相关类加载映射文件。
*
* @param string $prefix 命名空间前缀
* @param string $relative_class 相关类
* @return string|bool Boolean 无映射文件则为false否则加载映射文件
*/
protected function loadMappedFile(string $prefix, string $relative_class)
{
// 命名空间前缀是否存在任何基本目录
if (isset($this->prefixes[ $prefix ]) === false) {
return false;
}
// 通过基本目录查找命名空间前缀
foreach ($this->prefixes[ $prefix ] as $base_dir) {
// 用基本目录替换命名空间前缀
// 用目录分隔符替换命名空间分隔符
// 给相关的类名增加 .php 后缀
$file = $base_dir
. str_replace('\\', '/', $relative_class)
. '.php';
// 如果映射文件存在,则引入
if ($this->requireFile($file)) {
// 搞定了
return $file;
}
}
// 找不到
return false;
}
/**
* 如果文件存在从系统中引入进来
*
* @param string $file 引入文件
* @return bool 文件存在则 true 否则 false
*/
protected function requireFile(string $file) : bool
{
if (file_exists($file)) {
require $file;
return true;
}
return false;
}
}

View File

@ -0,0 +1,35 @@
<?php
namespace demo\AbstractFactory;
/**
* 抽象工厂类
*
* 该设计模式实现了设计模式的依赖倒置原则,因为最终由具体子类创建具体组件
*
* 在本例中,抽象工厂为创建 Web 组件产品提供了接口这里有两个组件文本和图片有两种渲染方式HTML
* JSON对应四个具体实现类。
*
* 尽管有四个具体类,但是客户端只需要知道这个接口可以用于构建正确的 HTTP 响应即可,无需关心其具体实现。
*/
abstract class AbstractFactory
{
/**
* 创建本文组件
*
* @param string $content
*
* @return Text
*/
abstract public function createText($content);
/**
* 创建图片组件
*
* @param string $path
* @param string $name
*
* @return Picture
*/
abstract public function createPicture($path, $name = '');
}

View File

@ -0,0 +1,37 @@
<?php
namespace demo\AbstractFactory;
/**
* JsonFactory类
*
* JsonFactory 是用于创建 JSON 组件的工厂
*/
class JsonFactory extends AbstractFactory
{
/**
* 创建图片组件
*
* @param string $path
* @param string $name
*
* @return Json\Picture|Picture
*/
public function createPicture($path, $name = '')
{
return new Json\Picture($path, $name);
}
/**
* 创建文本组件
*
* @param string $content
*
* @return Json\Text|Text
*/
public function createText($content)
{
return new Json\Text($content);
}
}

View File

@ -0,0 +1,14 @@
<?php
namespace demo\AbstractFactory;
class Run extends \bin\Design
{
public string $designName = "抽象工厂模式";
public string $designRefUrl = "https://laravelacademy.org/post/2471";
public function main()
{
var_dump('开始了');
}
}

35
index.php Normal file
View File

@ -0,0 +1,35 @@
<?php
const ROOTDIR = __DIR__;
/**
* 输出字符串或数组
*
* @param string/array $vars 输出字符串或数组
* @param string $label 提示标题
* @param bool|string $return 是否有返回值
*/
function dump($vars, bool|string $return = false, string $label = '')
{
if (ini_get('html_errors')) {
$content = "<pre>\n";
if ($label != '') {
$content .= "<strong>{$label} :</strong>\n";
}
$content .= htmlspecialchars(print_r($vars, true), ENT_COMPAT, 'ISO-8859-1');
$content .= "\n</pre>\n";
} else {
$content = $label . " :\n" . print_r($vars, true);
}
echo $content;
if ($return) exit;
return null;
}
require ROOTDIR . '/bin/Psr4Autoload.php';
(new \bin\Psr4Autoload)->register();
(new \bin\Base);

3
info.php Normal file
View File

@ -0,0 +1,3 @@
<?php
phpinfo();

11
mode/Ceshi.php Normal file
View File

@ -0,0 +1,11 @@
<?php
namespace mode;
class Ceshi
{
public function __construct()
{
var_dump('mode ceshi');
}
}