Home php教程 php手册 浅析php单态设计模式之单例模式的理解

浅析php单态设计模式之单例模式的理解

May 26, 2016 am 08:20 AM
select

单态模式的主要作用是保证在面向对象编程设计中,一个类只能有一个实例对象存在。作为对象的创建模式,单例模式确保某一个类只有一个实例,而且自行实例化并向整个系统全局地提供这个实例。它不会创建实例副本,而是会向单例类内部存储的实例返回一个引用。

1.单态设计模式含义:

单态模式的主要作用是保证在面向对象编程设计中,一个类只能有一个实例对象存在。作为对象的创建模式,单例模式确保某一个类只有一个实例,而且自行实例化并向整个系统全局地提供这个实例。它不会创建实例副本,而是会向单例类内部存储的实例返回一个引用。

2.单台模式的三个关键点:

① 需要一个保存类的唯一实例的静态成员变量;

②构造函数和克隆函数必须声明为私有的,防止外部程序new类从而失去单例模式的意义;

③必须提供一个访问这个实例的公共的静态方法(通常为getInstance方法),从而返回唯一实例的一个引用 。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

<?php

class DB {

    private static $obj = null; //声明一个私有的,静态的成员属性$obj

    private function__construct() { //私有构造方法,只能在类的内部实例化对象

        echo "连接数据库成功<br>";

    }

    public static function getInstance() { // 通过此静态方法才能获取本类的对象

        if (is_null(self::$obj)) //如果本类中的$obj为空,说明还没有被实例化过

        self::$obj = new self(); //实例化本类对象

        return self::$obj; //返回本类的对象

    }

    public function query($sql) { //执行SQL语句完成对数据库的操作

        echo $sql;

    }

}

$db = DB::getInstance(); //只能使用静态方法getInstance()去获取DB类的对象

$db->query("select *from user"); //访问对象中的成员

?>

Copy after login

单例模式,就是保持一个对象只存在一个实例。并且为该唯一实例提供一个全局访问点(一般是一个静态的getInstance方法)。单例模式应用场景非常广泛,例如:

数据库操作对象

日志写入对象

全局配置解析对象等

这些场景的共同特征是从业务逻辑上来看运行期间改对象却是

只需要一个实例

不断new多个实例会增加不必要的资源消耗

全局调用便利

下面分别说明这三个方面:

1. 业务上只需要一个实例

以数据库连接对象为例,加入有一个购物网站,有一个MySQL数据库

127.0.0.1:3306

,那么在一个进程中无论我们需要进行多少次针对改数据库的操作,都只需要连接数据库一次,使用相同的数据库连接句柄(MySQL Connection Resource),从业务需求上来看就只需要一个实例。

相反,同样以购物网站为例,存在许多商品,这些商品都不一样(id,name,price..),这个时候需要显示一个商品列表,加入我们建立一个

Product

作为数据映射对象,那么从业务需求上来说,一个实例就无法满足业务需求,因为每个商品都不一样。

2. 不断new操作增加不必要的资源消耗

我们一般会在类的构造方法(new操作肯定会调用)中进行一些业务操作,例如数据库连接对象可能会在构造方法中尝试读取数据库配置并进行数据库连接(如mysqli::__construct())、日志写入对象会判断日志写入目录是否存在并写入(不存在可能尝试创建改目录)、全局配置解析对象可能需要定位配置文件的保存目录并进行文件扫描等。

这些业务都会消耗相当的资源,如果在一个进程中我们值需要做一次,将会非常有利于我们提高应用的运行效率。

3. 全局调用便利

因为单例模式的一大特点就是通过静态方法获取对象实例,那么就意味着访问对象的方法时不需要先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

39

40

class Logger{

    //首先,需要一个私有的静态变量来存储产生的对象实例

    private static $instance;

    //业务变量,保存日志写入路径

    private $logDir;

    //构造方法,注意必须也是私有的,不允许被外部实例化(即在外部被new)

    private function __construct(){

        //调试输出,测试对象被new的次数

        echo "new Logger instance rn";

        $logDir = sys_get_temp_dir(). DIRECTORY_SEPARATOR . "logs";

        if(!is_dir($logDir) || !file_exists($logDir)){

            @mkdir($logDir);

        }

        $this->logDir = $logDir;

    }

    //类唯一实例的全局访问点,用于判断并返回对象实例,供外部调用

    public static function getInstance(){

        if(is_null(self::$instance)){

            $class = __CLASS__; //获取本对象的类型,也可以用new self()方式

            self::$instance = new $class();

        }

        return self::$instance;

    }

    //重载__clone()方法,不允许对象对克隆

    public function __clone(){

        throw new Exception("Singleton Class Can Not Be Cloned");

    }

    //具体的业务方法,实际可以有很多方法

    public function logError($message){

        $logFile = $this->logDir . DIRECTORY_SEPARATOR . "error.log";

        error_log($message, 3, $logFile);

    }

}

//日志调用

$logger = Logger::getInstance();

$logger->logError("An error occured");

$logger->logError("Another error occured");

//或者这样调用

Logger::getInstance()->logError("Still have error");

Logger::getInstance()->logError("I should fix it");

Copy after login

在单例模式中可能遇到一种比较特殊的情况,比如数据库连接对象,对于大型应用来说,很可能需要连接多台数据库,那么不同的数据库公用一个对象可能会产生问题,比如连接的分配、获取

insert_id

last_error

等。这个问题也比较好解决,就是把我们的$instance变量变成一个关联数组,通过给getInstance方法传入不同的参数获取不同的"单例对象"(引号的含义是:严格来说类可能被new多次,但是这个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

class MysqlServer{

    //注意,变成复数了哦^_^  当然只是为了标识而已

    private static $instances = array();

    //业务变量,保持当前实例的mysqli对象

    private $conn;

    //显著特征:私有的构造方法,避免在类外部被实例化

    private function __construct($host, $username, $password, $dbname, $port){

        $this->conn = new mysqli($host, $username, $password, $dbname, $port);

    }

    //类唯一实例的全局访问点

    public static function getInstance($host=&#39;localhost&#39;, $username=&#39;root&#39;, $password=&#39;123456&#39;, $dbname=&#39;mydb&#39;, $port=&#39;3306&#39;){

        $key = "{$host}:{$port}:{$username}:{$dbname}";

        if (empty(self::$instances[$key])){

            //这里也可以用 new self(); 的方式

            $class = __CLASS__;

            self::$instances[$key] = new $class($host, $username, $password, $dbname, $port);

        }

        return self::$instances[$key];

    }

    //重载__clone方法,不允许对象实例被克隆

    public function __clone(){

        throw new Exception("Singleton Class Can Not Be Cloned");

    }

    //查询业务方法,后面省略其它业务方法

    public function query($sql){

        return $this->conn->query($sql);

    }

    //尽早释放资源

    public function __destruct(){

        $this->conn->close();

    }

}

Copy after login

问题1:单例类能否拥有子类,因为单例类的构造方法是私有的,因此无法被继承,如果要继承则需要将构造方法改为protected或public,这就违背了单例模式的本意。因此,如果你想给单例类加子类,那就需要回头想想是否错用了模式,或者结构设计上有问题。

问题2:单例滥用,单例模式相对来说比较好理解和实现,因此一旦认识到单例模式的好处,很可能什么类都想写成单例,因此在使用次模式之前一定要考虑上述3种情况,看是否真的有必要使用。


永久地址:

转载随意~请带上教程地址吧^^

Statement of this Website
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn

Hot AI Tools

Undresser.AI Undress

Undresser.AI Undress

AI-powered app for creating realistic nude photos

AI Clothes Remover

AI Clothes Remover

Online AI tool for removing clothes from photos.

Undress AI Tool

Undress AI Tool

Undress images for free

Clothoff.io

Clothoff.io

AI clothes remover

AI Hentai Generator

AI Hentai Generator

Generate AI Hentai for free.

Hot Article

R.E.P.O. Energy Crystals Explained and What They Do (Yellow Crystal)
3 weeks ago By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O. Best Graphic Settings
3 weeks ago By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O. How to Fix Audio if You Can't Hear Anyone
3 weeks ago By 尊渡假赌尊渡假赌尊渡假赌
WWE 2K25: How To Unlock Everything In MyRise
4 weeks ago By 尊渡假赌尊渡假赌尊渡假赌

Hot Tools

Notepad++7.3.1

Notepad++7.3.1

Easy-to-use and free code editor

SublimeText3 Chinese version

SublimeText3 Chinese version

Chinese version, very easy to use

Zend Studio 13.0.1

Zend Studio 13.0.1

Powerful PHP integrated development environment

Dreamweaver CS6

Dreamweaver CS6

Visual web development tools

SublimeText3 Mac version

SublimeText3 Mac version

God-level code editing software (SublimeText3)

Asynchronous processing method of Select Channels Go concurrent programming using golang Asynchronous processing method of Select Channels Go concurrent programming using golang Sep 28, 2023 pm 05:27 PM

Asynchronous processing method of SelectChannelsGo concurrent programming using golang Introduction: Concurrent programming is an important area in modern software development, which can effectively improve the performance and responsiveness of applications. In the Go language, concurrent programming can be implemented simply and efficiently using Channels and Select statements. This article will introduce how to use golang for asynchronous processing methods of SelectChannelsGo concurrent programming, and provide specific

How to hide the select element in jquery How to hide the select element in jquery Aug 15, 2023 pm 01:56 PM

How to hide the select element in jquery: 1. hide() method, introduce the jQuery library into the HTML page, you can use different selectors to hide the select element, the ID selector replaces the selectId with the ID of the select element you actually use; 2. css() method, use the ID selector to select the select element that needs to be hidden, use the css() method to set the display attribute to none, and replace selectId with the ID of the select element.

How to implement change event binding of select elements in jQuery How to implement change event binding of select elements in jQuery Feb 23, 2024 pm 01:12 PM

jQuery is a popular JavaScript library that can be used to simplify DOM manipulation, event handling, animation effects, etc. In web development, we often encounter situations where we need to change event binding on select elements. This article will introduce how to use jQuery to bind select element change events, and provide specific code examples. First, we need to create a dropdown menu with options using labels:

What is the reason why Linux uses select? What is the reason why Linux uses select? May 19, 2023 pm 03:07 PM

Because select allows developers to wait for multiple file buffers at the same time, it can reduce IO waiting time and improve the IO efficiency of the process. The select() function is an IO multiplexing function that allows the program to monitor multiple file descriptors and wait for one or more of the monitored file descriptors to become "ready"; the so-called "ready" state is Refers to: the file descriptor is no longer blocked and can be used for certain types of IO operations, including readable, writable, and exceptions. select is a computer function located in the header file #include. This function is used to monitor file descriptor changes—reading, writing, or exceptions. 1. Introduction to the select function. The select function is an IO multiplexing function.

How to use the select syntax of mysql How to use the select syntax of mysql Jun 01, 2023 pm 07:37 PM

1. Keywords in SQL statements are not case-sensitive. SELECT is equivalent to SELECT, and FROM is equivalent to from. 2. To select all columns from the users table, you can use the symbol * to replace the column name. Syntax--this is a comment--query out [all] data from the [table] specified by FEOM. * means [all columns] SELECT*FROM--query out the specified data from the specified [table] from FROM Data of column name (field) SELECT column name FROM table name instance--Note: Use English commas to separate multiple columns selectusername, passwordfrom

Implement Select Channels Go concurrent programming performance optimization through golang Implement Select Channels Go concurrent programming performance optimization through golang Sep 27, 2023 pm 01:09 PM

Implementing SelectChannels through golang Performance optimization of Go concurrent programming In the Go language, it is very common to use goroutine and channel to implement concurrent programming. When dealing with multiple channels, we usually use select statements for multiplexing. However, in the case of large-scale concurrency, using select statements may cause performance degradation. In this article, we will introduce some implementations of select through golang

Select Channels Go concurrent programming for reliability and robustness using golang Select Channels Go concurrent programming for reliability and robustness using golang Sep 28, 2023 pm 05:37 PM

SelectChannels for Reliability and Robustness Using Golang Introduction to Concurrent Programming: In modern software development, concurrency has become a very important topic. Using concurrent programming can make programs more responsive, utilize computing resources more efficiently, and be better able to handle large-scale parallel computing tasks. Golang is a very powerful concurrent programming language. It provides a simple and effective way to implement concurrent programming through go coroutines and channel mechanisms.

How to use golang for Select Channels Go concurrent programming How to use golang for Select Channels Go concurrent programming Sep 27, 2023 pm 09:07 PM

How to use golang for SelectChannelsGo concurrent programming Go language is a language that is very suitable for concurrent programming, in which the channel (Channel) and the Select statement are two important elements to achieve concurrency. This article will introduce how to use golang's SelectChannels for concurrent programming and provide specific code examples. 1. The concept of channel Channel is used for communication and data between goroutines

See all articles