PHP 23种设计模式介绍(一)---- 创建型模式

简介

  设计模式分为三大类:

  创建型模式,共五种:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。

  结构型模式,共七种:适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。

  行为型模式,共十一种:策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。

  设计模式的六大原则:

原则解释
单一原则 (SRP)一个类只做一件事
开放-封闭原则(OCP)软件实体(类、模块、函数)可以拓展,但是不可修改
依赖倒转原则(DIP) A.高层模块不应该依赖底层,两个都应该依赖抽。B.抽象不应该依赖细节,细节依赖抽象
里氏代换原则(LSP)子类型必须能够替换掉它们的父类型
迪米特法则(LoD)如果两个类不必直接通信,那么这两个类不应当发生直接的相互作用。如果其中一个类需要调用另一个类的某一个方法的话,可通过第三者发起这个调用
合成/聚合复用原则(CARP)尽量使用合成/聚合,尽量不要使用类继承


---------------------------------------------------------------------------------------------------------------------------------

单例模式(Singleton)

单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。

这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。

注意:

  • 1、单例类只能有一个实例。
  • 2、单例类必须自己创建自己的唯一实例。
  • 3、单例类必须给所有其他对象提供这一实例。

介绍

意图:保证一个类仅有一个实例,并提供一个访问它的全局访问点。

主要解决:一个全局使用的类频繁地创建与销毁。

何时使用:当您想控制实例数目,节省系统资源的时候。

如何解决:判断系统是否已经有这个单例,如果有则返回,如果没有则创建。

关键代码:构造函数是私有的。

应用实例:

  • 1、一个班级只有一个班主任。
  • 2、Windows 是多进程多线程的,在操作一个文件的时候,就不可避免地出现多个进程或线程同时操作一个文件的现象,所以所有文件的处理必须通过唯一的实例来进行。
  • 3、一些设备管理器常常设计为单例模式,比如一个电脑有两台打印机,在输出的时候就要处理不能两台打印机打印同一个文件。

优点:

  • 1、在内存里只有一个实例,减少了内存的开销,尤其是频繁的创建和销毁实例(比如管理学院首页页面缓存)。
  • 2、避免对资源的多重占用(比如写文件操作)。

缺点:没有接口,不能继承,与单一职责原则冲突,一个类应该只关心内部逻辑,而不关心外面怎么样来实例化。

使用场景:

  • 1、要求生产唯一序列号。
  • 2、WEB 中的计数器,不用每次刷新都在数据库里加一次,用单例先缓存起来。
  • 3、创建的一个对象需要消耗的资源过多,比如 I/O 与数据库的连接等。

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

/**

 * Singleton class[单例模式]

 * @author 侯蜀黍

 */

final class Mysql

{

    /**

     *

     * @var self[该属性用来保存实例]

     */

    private static $instance;

    /**

     *

     * @var mixed

     */

    public $mix;

    /**

     * Return self instance[创建一个用来实例化对象的方法]

     *

     * @return self

     */

    public static function getInstance()

    {

        if (! (self::$instance instanceof self)) {

            self::$instance new self();

        }

        return self::$instance;

    }

    /**

     * 构造函数为private,防止创建对象

     */

    private function __construct()

    {}

    /**

     * 防止对象被复制

     */

    private function __clone()

    {

        trigger_error('Clone is not allowed !');

    }

}

// @test

$firstMysql = Mysql::getInstance();

$secondMysql = Mysql::getInstance();

$firstMysql->mix = 'ityangs_one';

$secondMysql->mix = 'ityangs_two';

print_r($firstMysql->mix);

// 输出: ityangs_two

print_r($secondMysql->mix);

// 输出: ityangs_two

  

工厂模式(Factory)

工厂模式(Factory Pattern)是 Java 中最常用的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。

在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象。

介绍

意图:定义一个创建对象的接口,让其子类自己决定实例化哪一个工厂类,工厂模式使其创建过程延迟到子类进行。

主要解决:主要解决接口选择的问题。

何时使用:我们明确地计划不同条件下创建不同实例时。

如何解决:让其子类实现工厂接口,返回的也是一个抽象的产品。

关键代码:创建过程在其子类执行。

应用实例: 1、您需要一辆汽车,可以直接从工厂里面提货,而不用去管这辆汽车是怎么做出来的,以及这个汽车里面的具体实现。 2、Hibernate 换数据库只需换方言和驱动就可以。

优点: 1、一个调用者想创建一个对象,只要知道其名称就可以了。 2、扩展性高,如果想增加一个产品,只要扩展一个工厂类就可以。 3、屏蔽产品的具体实现,调用者只关心产品的接口。

缺点:每次增加一个产品时,都需要增加一个具体类和对象实现工厂,使得系统中类的个数成倍增加,在一定程度上增加了系统的复杂度,同时也增加了系统具体类的依赖。这并不是什么好事。

使用场景: 1、日志记录器:记录可能记录到本地硬盘、系统事件、远程服务器等,用户可以选择记录日志到什么地方。 2、数据库访问,当用户不知道最后系统采用哪一类数据库,以及数据库可能有变化时。 3、设计一个连接服务器的框架,需要三个协议,"POP3"、"IMAP"、"HTTP",可以把这三个作为产品类,共同实现一个接口。

注意事项:作为一种创建类模式,在任何需要生成复杂对象的地方,都可以使用工厂方法模式。有一点需要注意的地方就是复杂对象适合使用工厂模式,而简单对象,特别是只需要通过 new 就可以完成创建的对象,无需使用工厂模式。如果使用工厂模式,就需要引入一个工厂类,会增加系统的复杂度。

工厂模式是另一种非常常用的模式,正如其名字所示:确实是对象实例的生产工厂。某些意义上,工厂模式提供了通用的方法有助于我们去获取对象,而不需要关心其具体的内在的实现。

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

<?php

/**

 * Factory class[工厂模式]

 * @author ITYangs<ityangs@163.com>

 */

interface SystemFactory

{

    public function createSystem($type);

}

class MySystemFactory implements SystemFactory

{

    // 实现工厂方法

    public function createSystem($type)

    {

        switch ($type) {

            case 'Mac':

                return new MacSystem();

            case 'Win':

                return new WinSystem();

            case 'Linux':

                return new LinuxSystem();

        }

    }

}

class System{ /* ... */}

class WinSystem extends System{ /* ... */}

class MacSystem extends System{ /* ... */}

class LinuxSystem extends System{ /* ... */}

//创建我的系统工厂

$System_obj new MySystemFactory();

//用我的系统工厂分别创建不同系统对象

var_dump($System_obj->createSystem('Mac'));//输出:object(MacSystem)#2 (0) { }

var_dump($System_obj->createSystem('Win'));//输出:object(WinSystem)#2 (0) { }

var_dump($System_obj->createSystem('Linux'));//输出:object(LinuxSystem)#2 (0) { }

抽象工厂模式(AbstractFactory)

抽象工厂模式(Abstract Factory Pattern)是围绕一个超级工厂创建其他工厂。该超级工厂又称为其他工厂的工厂。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。

在抽象工厂模式中,接口是负责创建一个相关对象的工厂,不需要显式指定它们的类。每个生成的工厂都能按照工厂模式提供对象。

介绍

意图:提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。

主要解决:主要解决接口选择的问题。

何时使用:系统的产品有多于一个的产品族,而系统只消费其中某一族的产品。

如何解决:在一个产品族里面,定义多个产品。

关键代码:在一个工厂里聚合多个同类产品。

应用实例:工作了,为了参加一些聚会,肯定有两套或多套衣服吧,比如说有商务装(成套,一系列具体产品)、时尚装(成套,一系列具体产品),甚至对于一个家庭来说,可能有商务女装、商务男装、时尚女装、时尚男装,这些也都是成套的,即一系列具体产品。假设一种情况(现实中是不存在的,要不然,没法进入共产主义了,但有利于说明抽象工厂模式),在您的家中,某一个衣柜(具体工厂)只能存放某一种这样的衣服(成套,一系列具体产品),每次拿这种成套的衣服时也自然要从这个衣柜中取出了。用 OOP 的思想去理解,所有的衣柜(具体工厂)都是衣柜类的(抽象工厂)某一个,而每一件成套的衣服又包括具体的上衣(某一具体产品),裤子(某一具体产品),这些具体的上衣其实也都是上衣(抽象产品),具体的裤子也都是裤子(另一个抽象产品)。

优点:当一个产品族中的多个对象被设计成一起工作时,它能保证客户端始终只使用同一个产品族中的对象。

缺点:产品族扩展非常困难,要增加一个系列的某一产品,既要在抽象的 Creator 里加代码,又要在具体的里面加代码。

使用场景: 1、QQ 换皮肤,一整套一起换。 2、生成不同操作系统的程序。

注意事项:产品族难扩展,产品等级易扩展。

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

<?php

class System{}

class Soft{}

class MacSystem extends System{}

class MacSoft extends Soft{}

class WinSystem extends System{}

class WinSoft extends Soft{}

/**

 * AbstractFactory class[抽象工厂模式]

 * @author 侯蜀黍

 */

interface AbstractFactory {

    public function CreateSystem();

    public function CreateSoft();

}

class MacFactory implements AbstractFactory{

    public function CreateSystem(){ return new MacSystem(); }

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

}

class WinFactory implements AbstractFactory{

    public function CreateSystem(){ return new WinSystem(); }

    public function CreateSoft(){ return new WinSoft(); }

}

//@test:创建工厂->用该工厂生产对应的对象

//创建MacFactory工厂

$MacFactory_obj new MacFactory();

//用MacFactory工厂分别创建不同对象

var_dump($MacFactory_obj->CreateSystem());//输出:object(MacSystem)#2 (0) { }

var_dump($MacFactory_obj->CreateSoft());// 输出:object(MacSoft)#2 (0) { }

//创建WinFactory

$WinFactory_obj new WinFactory();

//用WinFactory工厂分别创建不同对象

var_dump($WinFactory_obj->CreateSystem());//输出:object(WinSystem)#3 (0) { }

var_dump($WinFactory_obj->CreateSoft());//输出:object(WinSoft)#3 (0) { }

建造者模式(Builder)

建造者模式(Builder Pattern)使用多个简单的对象一步一步构建成一个复杂的对象。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。

一个 Builder 类会一步一步构造最终的对象。该 Builder 类是独立于其他对象的。

介绍

意图:将一个复杂的构建与其表示相分离,使得同样的构建过程可以创建不同的表示。

主要解决:主要解决在软件系统中,有时候面临着"一个复杂对象"的创建工作,其通常由各个部分的子对象用一定的算法构成;由于需求的变化,这个复杂对象的各个部分经常面临着剧烈的变化,但是将它们组合在一起的算法却相对稳定。

何时使用:一些基本部件不会变,而其组合经常变化的时候。

如何解决:将变与不变分离开。

关键代码:建造者:创建和提供实例,导演:管理建造出来的实例的依赖关系。

应用实例: 1、去肯德基,汉堡、可乐、薯条、炸鸡翅等是不变的,而其组合是经常变化的,生成出所谓的"套餐"。 

优点: 1、建造者独立,易扩展。 2、便于控制细节风险。

缺点: 1、产品必须有共同点,范围有限制。 2、如内部变化复杂,会有很多的建造类。

使用场景: 1、需要生成的对象具有复杂的内部结构。 2、需要生成的对象内部属性本身相互依赖。

注意事项:与工厂模式的区别是:建造者模式更加关注与零件装配的顺序。

建造者模式一般认为有四个角色:

     1.产品角色,产品角色定义自身的组成属性

     2.抽象建造者,抽象建造者定义了产品的创建过程以及如何返回一个产品

     3.具体建造者,具体建造者实现了抽象建造者创建产品过程的方法,给产品的具体属性进行赋值定义

     4.指挥者,指挥者负责与调用客户端交互,决定创建什么样的产品
 

实现

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

<?php

/**

 *

 * 产品本身

 */

class Product {

    private $_parts;

    public function __construct() { $this->_parts = array(); }

    public function add($part) { return array_push($this->_parts, $part); }

}

/**

 * 建造者抽象类

 *

 */

abstract class Builder {

    public abstract function buildPart1();

    public abstract function buildPart2();

    public abstract function getResult();

}

/**

 *

 * 具体建造者

 * 实现其具体方法

 */

class ConcreteBuilder extends Builder { 

    private $_product;

    public function __construct() { $this->_product = new Product(); }

    public function buildPart1() { $this->_product->add("Part1"); }

    public function buildPart2() { $this->_product->add("Part2"); }

    public function getResult() { return $this->_product; }

}

 /**

  *

  *导演者

  */

class Director {

    public function __construct(Builder $builder) {

        $builder->buildPart1();//导演指挥具体建造者生产产品

        $builder->buildPart2();

    }

}

// client

$buidler new ConcreteBuilder();

$director new Director($buidler);

$product $buidler->getResult();

echo "<pre>";

var_dump($product);

echo "</pre>";

/*输出: object(Product)#2 (1) {

["_parts":"Product":private]=>

array(2) {

    [0]=>string(5) "Part1"

    [1]=>string(5) "Part2"

}

} */

?>

原型模式(Prototype)

原型模式(Prototype Pattern)是用于创建重复的对象,同时又能保证性能。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。

这种模式是实现了一个原型接口,该接口用于创建当前对象的克隆。当直接创建对象的代价比较大时,则采用这种模式。例如,一个对象需要在一个高代价的数据库操作之后被创建。我们可以缓存该对象,在下一个请求时返回它的克隆,在需要的时候更新数据库,以此来减少数据库调用。

介绍

意图:用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。

主要解决:在运行期建立和删除原型。

何时使用: 1、当一个系统应该独立于它的产品创建,构成和表示时。 2、当要实例化的类是在运行时刻指定时,例如,通过动态装载。 3、为了避免创建一个与产品类层次平行的工厂类层次时。 4、当一个类的实例只能有几个不同状态组合中的一种时。建立相应数目的原型并克隆它们可能比每次用合适的状态手工实例化该类更方便一些。

如何解决:利用已有的一个原型对象,快速地生成和原型对象一样的实例。

关键代码: 1、实现克隆操作,在 JAVA 继承 Cloneable,重写 clone(),在 .NET 中可以使用 Object 类的 MemberwiseClone() 方法来实现对象的浅拷贝或通过序列化的方式来实现深拷贝。 2、原型模式同样用于隔离类对象的使用者和具体类型(易变类)之间的耦合关系,它同样要求这些"易变类"拥有稳定的接口。

应用实例: 1、细胞分裂。 2、JAVA 中的 Object clone() 方法。

优点: 1、性能提高。 2、逃避构造函数的约束。

缺点: 1、配备克隆方法需要对类的功能进行通盘考虑,这对于全新的类不是很难,但对于已有的类不一定很容易,特别当一个类引用不支持串行化的间接对象,或者引用含有循环结构的时候。 2、必须实现 Cloneable 接口。

使用场景: 1、资源优化场景。 2、类初始化需要消化非常多的资源,这个资源包括数据、硬件资源等。 3、性能和安全要求的场景。 4、通过 new 产生一个对象需要非常繁琐的数据准备或访问权限,则可以使用原型模式。 5、一个对象多个修改者的场景。 6、一个对象需要提供给其他对象访问,而且各个调用者可能都需要修改其值时,可以考虑使用原型模式拷贝多个对象供调用者使用。 7、在实际项目中,原型模式很少单独出现,一般是和工厂方法模式一起出现,通过 clone 的方法创建一个对象,然后由工厂方法提供给调用者。原型模式已经与 Java 融为浑然一体,大家可以随手拿来使用。

注意事项:与通过对一个类进行实例化来构造新对象不同的是,原型模式是通过拷贝一个现有对象生成新对象的。浅拷贝实现 Cloneable,重写,深拷贝是通过实现 Serializable 读取二进制流。

概念理解:原型模式是先创建好一个原型对象,然后通过clone原型对象来创建新的对象。适用于大对象的创建,因为创建一个大对象需要很大的开销,如果每次new就会消耗很大,原型模式仅需内存拷贝即可。

主要角色:

Prototype(抽象原型角色):声明一个克隆自身的接口

Concrete Prototype(具体原型角色):实现一个克隆自身的操作

实现

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

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

<?php

interface Prototype

{

    public function shallowCopy();

    public function deepCopy();

}

class ConcretePrototype implements Prototype

{

    private $_name;

    public function __construct($name)

    {

        $this->_name = $name;

    }

    public function setName($name)

    {

        $this->_name = $name;

    }

    public function getName()

    {

        return $this->_name;

    }

    /**

     * 浅拷贝

     * */

    public function shallowCopy()

    {

        return clone $this;

    }

    /**

     * 深拷贝

     * */

    public function deepCopy()

    {

        $serialize_obj = serialize($this);

        $clone_obj = unserialize($serialize_obj);

        return $clone_obj;

    }

}

class Demo

{

    public $string;

}

class UsePrototype

{

    public function shallow()

    {

        $demo new Demo();

        $demo->string = "susan";

        $object_shallow_first new ConcretePrototype($demo);

        $object_shallow_second $object_shallow_first->shallowCopy();

        var_dump($object_shallow_first->getName());

        echo '<br/>';

        var_dump($object_shallow_second->getName());

        echo '<br/>';

        $demo->string = "sacha";

        var_dump($object_shallow_first->getName());

        echo '<br/>';

        var_dump($object_shallow_second->getName());

        echo '<br/>';

    }

    public function deep()

    {

        $demo new Demo();

        $demo->string = "Siri";

        $object_deep_first new ConcretePrototype($demo);

        $object_deep_second $object_deep_first->deepCopy();

        var_dump($object_deep_first->getName());

        echo '<br/>';

        var_dump($object_deep_second->getName());

        echo '<br/>';

        $demo->string = "Demo";

        var_dump($object_deep_first->getName());

        echo '<br/>';

        var_dump($object_deep_second->getName());

        echo '<br/>';

    }

}

$up new UsePrototype;

$up->shallow(); //浅拷贝

echo '<hr>';

$up->deep();    //深拷贝

  效果

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值