PHP

设计模式

Posted by epimetheusQ on 2021-09-10

PHP中的设计模式

设计模式:提供了一种广泛的可重用的方式来解决我们日常编程中常常遇见的问题。设计模式并不一定就是一个类库或者第三方框架,它们更多的表现为一种思想并且广泛地应用在系统中。它们也表现为模板或者一种模式,可以在多个不同场景下用于解决问题。设计模式可以用于加速开发,并且将很多大的想法或者设计以一种简单的方式实现。当然,虽然设计模式在开发中很有作用,但是千万要避免在不适当的场景误用他们。

设计模式分类

根据目的和范围,设计模式可分为5类。

按照目的分为:创建设计模式,结构设计模式,以及行为设计模式。
按照范围分为:类的设计模式,以及对象设计模式。

按照目的划分

分类 模式 分析
创建设计模式[Creational Patterns] 用于创建对象时的设计模式。更具体一点,初始化对象流程的设计模式。当程序日益复杂时,需要更加灵活地创建对象,同时减少创建时的依赖,而创建设计模式就是解决此问题的一类设计模式
单例模式[Singleton]
工厂模式[Factory]
抽象工厂模式[AbstractFactory]
建造者模式[Builder]
原型模式[Prototype]
结构设计模式[Structural Patterns] 用于继承和接口时的设计模式。结构设计模式用于新类的函数方法设计,减少不必要的类定义,减少代码的冗余
适配器模式[Adapter]
桥接模式[Bridge]
合成模式[Composite]
装饰器模式[Decorator]
门面模式[Facade]
代理模式[Proxy]
享元模式[Flyweight]
行为模式[Behavioral Patterns] 用于方法实现以及对应算法的设计模式,同时也是最复杂的设计模式。行为设计模式不仅仅用于定义类的函数行为,同时也用于不同类之间的协议、通信。
策略模式[Strategy]
模板方法模式[TemplateMethod]
观察者模式[Observer]
迭代器模式[Iterator]
责任链模式[ResponsibilityChain]
命令模式[Command]
备忘录模式[Memento]
状态模式[State]
访问者模式[Visitor]
中介者模式[Mediator]
解释器模式[Interpreter]

按照范围划分

模式 分析
类的设计模式 用于类的具体实现的设计模式。包含了如何设计和定义类,以及父类和子类的设计模式
对象设计模式 用于对象的设计模式。与类的设计模式不同,对象设计模式主要用于运行期对象的状态改变、动态行为变更等。

设计模式实现

单例模式

1
2
3
4
5
6
7
8
9
10
11
12
class Mysql {
private static $instance;

public static function getInstance()
{
if (! (self::$instance instanceof self)) {
self::$instance = new self();
}

return self::$instance;
}
}

工厂模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
interface Factory {
public static function createSystem($type);
}

class MySystemFactory implements SystemFactory{

public static function createSystem($type) {

$class = \App\Service\ . ucfirst($tpye) . Services;

if (! class_exitsts($class)) {
throw new \Exception('Invild Class');
}

return new $class;
}
}

// 使用
$factory = MySystemFactory::create($type);

抽象工厂模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php

// 有些情况下我们需要根据不同的选择逻辑提供不同的构造工厂,而对于多个工厂而言需要一个统一的抽象工厂:

interface Factory() {
public function createSystem();
public function createSoft();
}

class MacFactory implements Factory {
public function createSystem() {
return new MacSystem();
}

public function createSoft() {
return new MacSoft();
}
}

Builder(建造者模式)

建造者模式主要创建一些复杂的对象。将一个复杂的对象的构造与它的表示分离,使同样的构建过程可以创建不同的表示的设计模式。

我们这里举一个例子,小王和小明都需要创建个人档案,将个人档案创建成功后,发送给老师,老师处理或者说(评语)个人档案信息。这里有三个角色

角色 解释
创建方式(具体创建方法) 比如:小王需要创建个人档案,个人档案都需要先创建哪些信息,比如性别,年龄,身高,视力等等
创建人(谁来创建) 比如:小王填写了创建成功的个人档案信息
创建什么产品 比如:创建一个个人档案
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
<?php

// 首先创建一个产品,也就是一个个人档案,后期可能还有另一种产品,如学生证,产品是多样的
class PersonFile
{
public $record;

public function __construct()
{
$this->record = [];
}

public function setAttr($keyword, $value)
{
$this->record[$keyword] = $value;
}
}

// 然后我们创建一个Build抽象类,创建档案信息,和创建学生证信息,都将继承该类
abstract class AbstractBuild()
{
public function setHigh();
public function setSex();
public function setName();
}

// 我们创建一个个人档案的方法,继承AbstractBuild
class PersonFileBuild extends AbstractBuild
{
public $personFile;

public function __construct(PersonFile $file)
{
$this->personFile = $file;
}

public function setHigh($value) {
$this->personFile->setAttr('high', $value);
}

public function setSex($value) {
$this->personFile->setAttr('sex', $value);
}

public function setName($value) {
$this->personFile->setAttr('name', $value);
}
}

// 小王去创建一个个人档案
class XWFile {

public $createRes;

public function __construct(AbstractBuild $builder) {
$builder->setHigh('1.81');
$builder->setSex(1);
$builder->setName('小王');

$this->createResl = $builder;
}

public function getXWFile()
{
return $this->createRes;
}
}

// 调用方式
// 最后得出小王个人档案成品
$XwPersonFile = (new XWFile(new PersonFileBuild(new PersonFile())))->getXWFile();

适配器模式

简单来讲,对外层进行封装,同样的功能把不能用的接口封装成可以用的接口就可以了。

假设这里对返回数据进行format格式化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// 先定义一个适配器接口
interface Format
{
public function Format(string $cnt);
}

// 具体适配器
class UTF8Format implements Format
{
// 将内容转化为utf8格式
public function format(string $cnt)
{
return '将格式转化为了utf8';
}
}

// 具体业务
class MainServers {
// 此时内容是gbk格式的
public function content($cnt) {
//处理中...

// 返回格式
(new UTF8Format($content))->getContent();
}
}

// 这种业务可以在,同时接多个发卡方,转化多元字符,等等场景出现

观察者模式

开篇从名字说起,观察者模式中的观察者三个字信息量很大。玩过网络游戏的人都知道,即便是斗地主,除了玩家,还有一个角色叫“观察者”。在我们今天讨论的模式设计中,观察者也是如此。首先要有一个主题,观察者才能搬着小板凳聚在一起,其次,观察者还必须要有自己的操作。否则你聚在一起也没有什么意义。

从面向过程的角度来看,首先是观察者向主题注册,注册完之后,主题在通知观察者做出相应的操作,整个事情就完了。

从面向对象的角度来看,主题提供注册和通知的接口,观察者提供自身操作的接口。观察者利用主题的接口向主题注册,而主题利用观察者接口通知观察者,耦合度相当之低。接下来我们看一下实现过程。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
// 两个角色,观察者和主题,观察者先注册主题,主题通知观察者

// 主题接口
interface Theme
{
// 观察者需要先注册至主题中
public function register(Observer $observer);
// 主题方可通知观察者
public function notify();
}

// 观察者接口
interface Observer
{
// 注册成功至主题中,执行主题触发后的操作
public function opt();
}


// 定义多个观察者,假如527、577、517都在看鬼片
class Observer527 implements Observer
{
public function opt()
{
echo "太无聊了,尿点";
}
}

class Observer517 implements Observer
{
public function opt()
{
echo "太可怕了";
}
}

class Observer577 implements Observer
{
public function opt()
{
echo "没意思";
}
}

// 观察者定义完,我们需要定义主题!看鬼片
class GhostFilmTheme implements Theme
{
public $observers = [];

public function register(Observer $observer)
{
$this->observers[] = $observer;
}

public function notify()
{
foreach ($this->observers as $observer) {
$observer->opt();
}
}
}

// 执行流程,首先获取一个电影的主题
$film = new GhostFilmTheme();

$film->register(new Observer527);
$film->register(new Observer517);
$film->register(new Observer577);

$film->notify();

注册树模式

本人感觉无聊至极的模式,简单来讲就是将数组赋值取出扩展成类,调用的方式来取出赋值.
注意:数组是放在静态变量中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Register{
protected static $objects;

public static function set($alias, $object) {
self::$objects[$alias] = $object;
}

public static function get($alias) {
return self::$objects[$alias];
}

public static function unset($alias) {
unset(self::$objects[$alias]);
}
}

Register::set('rand', 'xxxxx');
// 取出数据
pr(Register::get('rand'));

桥接模式

桥接模式主要解决:解耦一个对象的实现与抽象,这样两者可以独立地变化。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
// 桥接模式所要表达的内容是什么?
// 解耦一个对象的实现和抽象,这样两者可以独立的变化。
// 首先创建一个format接口,然后我们定义几个格式化数据
interface Format
{
public function format(string $text);
}

// JsonFormat
class JsonFormat implements Format
{
public function format(string $text)
{
return json_encode($text);
}
}

// HTMLFormat
class HTMLFormat implements Format
{
public function format(string $text)
{
return htmlspecialchars($text);
}
}

// 接下来我们定义一个抽象总服务,该服务主要是为了处理业务后,将数据转化成为需要的格式,这时候我们用到上述的各种格式类
abstract class Service {
protected $implementation;

public function __construct(Format $format)
{
$this->implementation = $format;
}

// abstract是抽象类,抽象类的标志是抽象方法,和接口不同,抽象类是继承,接口是实现
abstract public function get();
}

// 随意一个子服务继承抽象类
class XWService extends Service
{
public function get($text)
{
return $this->implementation->format($text);
}
}

// 使用::::
new XWService(new JsonFormat)->get('hello world');

装饰器模式

装饰器模式允许我们根据运行时不同的情景动态地为某个对象调用前后添加不同的行:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
// 正常业务中,我们在既定的流程,想要添加一些其他数据,可以是日志,可以是任何操作

// 首先定义一个接口
interface Component
{
public function operation();
}

// 定义一个修饰器抽象类
abstract class Decorator implements Component
{
protected $component;

public function __construct(Component $component) {
$this->component = $component;
}

public function operation() {
$this->component->operation();
}
}

class ConcreteDecoratorA extends Decorator {
public function __construct(Component $component) {
parent::__construct($component);
}

public function operation() {
parent::operation();
$this->addedOperationA();
}

public function addedOperationA()
{
echo "A加点";
}
}

class ConcreteDecoratorB extends Decorator {
public function __construct(Component $component) {
parent::__construct($component);
}

public function operation() {
parent::operation();
$this->addedOperationB();
}

public function addedOperationB()
{
echo "ceshi B";
}
}

class ConcreteComponent implements Component{
public function operation() {

}
}

// 使用
$component = new ConcreteComponent();
$decoratorA = new ConcreteDecoratorA($component)->operation();
$decoratorB = new ConcreteDecoratorB($decoratorA)->operation();

依赖注入

没什么说的,一直在用,一个类必须依赖另一个类,以参数的形式注入进另一个类中。

代理模式(Proxy)

个人理解:代理模式就是访问代理对象,从而添加一些额外的功能,或者说限制访问对象,这种是不是类似于中间件?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
abstract class Subject
{
abstract public function action();
}

class RealSubject extends Subject {
public function __construct() {

}

public function action() {

}
}

class ProxySubject extends Subject {
private $realSubject = NULL;

public function __construct() {

}

public function action () {
$this->beforeAction();

if (is_null($this->realSubject)) {
$this->realSubject = new RealSubject();
}

$this->realSubject->action();
$this->afterAction();
}

private function beforeAction()
{
echo "action方法调用前操作";
}

private function afterAction()
{
echo "action方法后调用";
}
}

// 我们在操作时,只需要调用代理类,就可以实现对实际方法的操作,并且添加附加功能
$Subject = new ProxySubject();
$Subject->action();

策略模式

类似于laravel的依赖注入,策略模式主要为了让客户类能够更好地使用某些算法而不需要知道其具体的实现。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34

<?php

interface Strategy{
public function do_method();
}

class StrategyA implements Strategy{
public function do_method(){
echo "do method A";
}
}

class StrategyB implements Strategy{
public function do_method(){
echo "do method B";
}
}

class Question{
private $strategy;

public function __construct(Strategy $strategy) {
$this->strategy = $strategy;
}

public function handle_question() {
$this->strategy->do_method();
}
}

// 可以注入不同的类,只要注册相同的interface就可以实现不同的类的实现
$strategy = new Question(new StrategyA)->handle_question();

模板方法模式

就是利用抽象方法实现具体功能

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?php
abstract class AbstractClass { // 抽象模板角色
public function templateMethod() { // 模板方法 调用基本方法组装顶层逻辑
$this->primitiveOperation1();
$this->primitiveOperation2();
}
abstract protected function primitiveOperation1(); // 基本方法
abstract protected function primitiveOperation2();
}

class ConcreteClass extends AbstractClass { // 具体模板角色
protected function primitiveOperation1() {}
protected function primitiveOperation2(){}

}

$class = new ConcreteClass();
$class->templateMethod();
?>