自制PHP框架之设计模式

2025-05-29 0 101

为什么要使用设计模式

设计模式,我的理解是为了达到“可复用”这个目标,而设计的一套相互协作的类。

感兴趣的读者可以阅读《design patterns: elements of reusable object-oriented software》,四位作者(gang of four)在书中列举了业界闻名的23种设计模式

这里先介绍我们框架要涉及的三种设计模式

单例模式(singleton)

单例模式可以保证一个类只有一个对象实例, 常用在数据库存取类,从而节省硬件资源的消耗。

这里,我们改写上一章节的mysql类

?

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
class mysql extends db{

private static $instance=null;

public static function getinstance(){

if(self::$instance==null){

self::$instance=new mysql();

}

return self::$instance;

}

public function mysql(){

/*config*/

$this->ip='*';

$this->serverid='*';

$this->serverpassword='*';

$this->databasename='*';

/*end of config*/

$this->connection=mysqli_connect($this->ip,$this->serverid,$this->serverpassword,$this->databasename);

if(!$this->connection){

die('could not connect'.$this->connection);

}

mysqli_query($this->connection,'set names utf8');

}

public function execute($sql){

return mysqli_query($this->connection,$sql);

}

public function query($sql){

$result=mysqli_query($this->connection,$sql);

$arr=array();

while($row=mysqli_fetch_array($result)){

$arr[]=$row;

}

return $arr;

}

public function close(){

mysqli_close($this->connection);

}

}

这里要注意的是,如果实例化一个mysql类,我们不再写

?

1
$db=new mysql();

而是这样:

?

1
$db=mysql::getinstance();

因为只有getinstance这个静态函数,才能保证只调用一次mysql类的构造函数。

单例模式是很常用的设计模式,这里不再赘述。

外观模式(facade)

因为命名空间的问题,外观模式可以保证一个类的诸多方法看似是“一个类提供的”,这里我们先设计一个简单的服务提供者类

?

1

2

3

4

5
class serviceprovider{

public function write($arg){

echo $arg;

}

}

这个类只有一个write方法,就是把参数打印出来

然后定义一个facade类

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14
class facade{

public static function getinstance($classname,$args){

return new $classname($args);

}

public static function getfacadeaccessor(){

//

}

public static function __callstatic($method,$args){

$instance=static::getinstance(static::getfacadeaccessor(),$args);

return call_user_func_array(array($instance,$method),$args);

}

}

要理解这个类,我们只要关注最后一个函数,就是__callstatic魔术方法。这个方法就是facade类型对象或者其子类在调用他自身没有定义过的函数时,就会调用__callstatic方法,而这个方法最后调用了call_user_func_array函数,就是把任务交给提供这项服务的类去完成,同时完成参数的传递。

我们再写一个facade子类

?

1

2

3

4

5
class myfacade extends facade{

public static function getfacadeaccessor(){

return serviceprovider::class;

}

}

这里注意,子类实现了父类没有具体实现的getfacadeaccessor方法,这个方法就是要告诉父类的__callstatic方法:“我作为facade,代表的是什么哪个类,任务就由他来实现吧”,从语法上看,只是返回了一个表示类名的字符串。所以父类起初并不知道它的子类都代表着什么“服务提供者类”,只有当子类的静态函数被调用后,因为子类没有该静态函数,所以父类的__callstatic方法被启动了。

抽象工厂(factory)

我对抽象工厂有一个粗俗的理解:“对象与字符串的对应”,也就是用一个字符串就可以创造一个类的对象。这种做法主要用在两种情况下是很方便的:

1.类名不稳定,会在项目中频繁修改

类名修改,很多时候并不是设计者的“命名洁癖”或者“命名强迫症”导致的修改,而是在项目的不断迭代,发觉这个类设计的不合理。如果这个类用的不频繁,那么改个类名只要手工做一些小的修改即可,但是如果这个类通篇存在于代码之中(假如是数据库类),那修改工作量就大了,当然,我们也可以对代码文件使用“字符串替换”,但是假如一个php写成的项目,php文件有几十上百个,这也是不合理的事。

2.类的设计者并不是类的使用者

类的设计者和类的使用者不是同一个开发人员,那么记忆一个字符串或许比记忆一个类名要生动的多。我们都学过计算机网络原理,都知道记忆一个域名要比记忆一个ip地址要生动的多,这就是dns解决的问题。

因为抽象工厂很多教材都有涉及,不再赘述,本文将介绍一下目前非常流行的服务容器。

我们希望整个工程项目中,db类,session类,filesystem类“拿来即用”,不用每次繁琐的初始化,比如写$db=new db(arg1,arg2);这类语句,也希望db等类型的对象像一个“全局”变量一般,在整个程序运行期间,随时可以调用。

服务容器可以让调用db等类型的程序员不用知道这个类太多的细节,甚至可以用一个字符串的别名来创建这样一个对象。

我们定义一个服务容器类

?

1

2

3

4

5

6

7

8

9
class container{

public $bindings;

public function bind($abstract,$concrete){

$this->bindings[$abstract]=$concrete;

}

public function make($abstract,$parameters=[]){

return call_user_func_array($this->bindings[$abstract],$parameters);

}

}

可以把服务容器简单的看成一个全局变量,bind方法就是用关联数组把字符串和构造函数做绑定。

至此,有了服务容器,我们的model类就要做修改了

?

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
class model implements imodel{

public static $table;

public static $container;

public static $db;

public function __construct(){

self::$container=new container();

self::$container->bind('db',function(){

return mysql::getinstance();

});

self::$db=self::$container->make('db',[]);

}

public static function get($id){

return self::where('id',$id);

}

public static function where($condition,$value){

$sql=sprintf("select * from %s where %s='%s'",self::$table,$condition,$value);

return self::$db->query($sql);

}

public static function all(){

$sql=sprintf("select * from %s",self::$table);

return self::$db->query($sql);

}

}

观察上面代码,我们同时用了单例模式和服务容器。

总结:如果要做一个php框架,应该要做好代码的复用。设计模式一直是很多争论的焦点,“究竟该不该使用设计模式?”,本文开始,我也努力回避“过于纠结这个问题”,我认为,设计模式有其存在的价值,至少在具体项目中,确实在很多版本迭代中节省了工作量,提高工作效率,但是如果在一个小项目中为了“秀一下我会设计模式”而使用设计模式,就不合理了。

原文链接:http://www.cnblogs.com/sweng/p/6666008.html

收藏 (0) 打赏

感谢您的支持,我会继续努力的!

打开微信/支付宝扫一扫,即可进行扫码打赏哦,分享从这里开始,精彩与您同在
点赞 (0)

声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。

快网idc优惠网 建站教程 自制PHP框架之设计模式 https://www.kuaiidc.com/94461.html

相关文章

发表评论
暂无评论