0.1
This commit is contained in:
parent
5c906d7096
commit
0ef4f6a1e4
|
@ -0,0 +1 @@
|
|||
.idea
|
|
@ -0,0 +1,8 @@
|
|||
# 默认忽略的文件
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
# 基于编辑器的 HTTP 客户端请求
|
||||
/httpRequests/
|
||||
# Datasource local storage ignored files
|
||||
/dataSources/
|
||||
/dataSources.local.xml
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
35
README.md
35
README.md
|
@ -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) | 不太常用 | 应用级 | 设计时 | 比较复杂 | 领域问题的变化 | 封装特定领域的变化 | |
|
|
@ -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';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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 未定义");
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -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 = '');
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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('开始了');
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
<?php
|
||||
|
||||
namespace mode;
|
||||
|
||||
class Ceshi
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
var_dump('mode ceshi');
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue