PHP 中的设计模式详解

本文主要讨论下Web开发中,准确而言,是PHP开发中的相关的设计模式及其应用。有经验的开发者肯定对于设计模式非常熟悉,但是本文主要是针对那些初级的开发者。首先我们要搞清楚到底什么是设计模式,设计模式并不是一种用来解释的模式,它们并不是像链表那样的常见的数据结构,也不是某种特殊的应用或者框架设计。事实上,设计模式的解释如下:

模式有三种:Architectural Pattern、Design Pattern、Coding
Pattern,即:框架模式、设计模式、编程模式。本文主要讲解javascript中的设计模式,好的设计模式能够提高代码的重用性,可读性,使代码更容易的维护和扩展。本文适合有一点javascript基础,对javascript的概念有所了解。

descriptions of communicating objects and classes that are customized
to solve a general design problem in a particular context.

一、单例模式:

 单例模式是javascript中最常用的模式,它是将自己的代码放在一个命名空间下,这样的好处是可以减少使用全局变量,在多人协同开发时也能避免命名冲突等问题。这样的好处是维护起来非常方便,如下例:

1 var m = {
2     name: 'dog',
3     action: function() {
4         console.log(this.name);
5     }
6 };
7 m.action();//调用

 

或者

1 var dog = function() {
2     this.name = 'dog';
3     this.action = function() {
4         return console.log(this.name);
5     };
6     action();
7 };
8 dog(); //调用

 

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

工厂模式:

工厂模式就是将对象的方法创建交给外部对象,这样的好处就是解决了对象之间的相互影响、即耦合,避免了使用new来实例化对象,有助于创建模块化的代码,维护起来也方便。 工厂模式分为简单工厂模式和抽象工厂模式,下面介绍简单工厂模式:

1 var m = {};
2 m.action = function() {
3     console.log('dog');
4 };
5 var demo = function() {
6     m.action();
7 };
8 demo()//调用

 

抽象工厂模式先设计一个抽象类,这个类不能被实例化,只能用来派生子类,最后通过对子类的扩展实现工厂方法。如下:

 1 var f = function() {};
 2 f.prototype = {
 3     c: function() {
 4         throw new Error('can't use this method');//如果调用此方法会报错,因为它是用来派生子类不能实例化
 5     }
 6 };
 7 var e = function() {
 8     f.call(this);
 9 }
10 e.prototype = new f();
11 e.prototype.constructor = e;
12 e.prototype.c = function() {
13     console.log('this method is redefine');
14 }
15 // 调用
16 var demo = new e();
17 demo.c();

 

目前常见的设计模式主要有23种,根据使用目标的不同可以分为以下三大类:

桥接模式:

桥接模式是将抽象与实现隔离,一遍二者独立变化。在设计一个javascript
API的时候,它可以用来弱化类和对象之间的耦合。它还可以用来把多个类联接在一起。例如:

 1 var class1 = function(a,b,c) {
 2     this.a = a;
 3     this.b = b;
 4     this.c = c;
 5 };
 6 var class2 = function(d) {
 7     this.d = d;
 8 };
 9 var demo = function(a,b,c,d) {
10     this.one = new Class1(a,b,c);
11     this.two = new Class2(d);
12 };

 

  • 创建模式:用于创建对象从而将某个对象从实现中解耦合。
  • 架构模式:用于在不同的对象之间构造大的对象结构。
  • 行为模式:用于在不同的对象之间管理算法、关系以及职责。

组合模式:

组合模式可以用一条命令在多个对象上激发复杂的或递归的行为。好处是可以用同样的发放处理对象的集合与其中的特定子对象,也可以用来把一批子对象组织成树形结构,并且使整个树都可被遍历。如下:

 1 // DynamicGallery Class
 2   var DynamicGallery =function (id) { // 实现Composite,GalleryItem组合对象类 
 3      this.children = [];
 4      this.element = document.createElement('div');
 5      this.element.id = id;
 6      this.element.className ='dynamic-gallery';
 7   }
 8   DynamicGallery.prototype = {
 9      // 实现Composite组合对象接口 
10      add: function (child) {
11         this.children.push(child);
12         this.element.appendChild(child.getElement());
13      },
14      remove: function (child) {
15         for (var node, i =0; node =this.getChild(i); i++) {
16            if (node == child) {
17               this.children.splice(i, 1);
18               break;
19            }
20         }
21         this.element.removeChild(child.getElement());
22      },
23      getChild: function (i) {
24         returnthis.children[i];
25      },
26      // 实现DynamicGallery组合对象接口 
27      hide: function () {
28         for (var node, i =0; node =this.getChild(i); i++) {
29            node.hide();
30         }
31         this.element.style.display ='none';
32      },
33      show: function () {
34         this.element.style.display ='block';
35         for (var node, i =0; node = getChild(i); i++) {
36            node.show();
37         }
38      },
39      // 帮助方法 
40      getElement: function () {
41         returnthis.element;
42      }
43   }

 

 1 var GalleryImage =function (src) { // 实现Composite和GalleryItem组合对象中所定义的方法 
 2      this.element = document.createElement('img');
 3      this.element.className ='gallery-image';
 4      this.element.src = src;
 5   }
 6   GalleryImage.prototype = {
 7      // 实现Composite接口 
 8      // 这些是叶结点,所以我们不用实现这些方法,我们只需要定义即可 
 9      add: function () { },
10      remove: function () { },
11      getChild: function () { },
12      // 实现GalleryItem接口 
13      hide: function () {
14         this.element.style.display ='none';
15      },
16      show: function () {
17         this.element.style.display ='';
18      },
19      // 帮助方法 
20      getElement: function () {
21         returnthis.element;
22      }
23   }

 1 var topGallery =new DynamicGallery('top-gallery'); 
 2   topGallery.add(new GalleryImage('/img/image-1.jpg')); 
 3   topGallery.add(new GalleryImage('/img/image-2.jpg')); 
 4   topGallery.add(new GalleryImage('/img/image-3.jpg')); 
 5   var vacationPhotos =new DyamicGallery('vacation-photos'); 
 6   for(var i =0, i <30; i++){ 
 7     vacationPhotos.add(new GalleryImage('/img/vac/image-'+ i +'.jpg')); 
 8   } 
 9   topGallery.add(vacationPhotos); 
10   topGallery.show(); 
11   vacationPhotos.hide();

 

Creational Patterns

门面模式:

门面模式常常是开发人员最亲密的朋友,他几乎是所有javascript库的核心原则。门面模式有两个作用:一是简化类的接口;二是消除类与使用它的客户代码之间的耦合。示例如下:

 1 function a() {
 2 
 3 }
 4 function b() {
 5 
 6 }
 7 function ab() {
 8     a();
 9     b();
10 }

 

Singleton(单例模式)

单例模式是最常见的模式之一,在Web应用的开发中,常常用于允许在运行时为某个特定的类创建一个可访问的实例。

<?php
/**
 * Singleton class
 */
final class Product
{

    /**
     * @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 function __construct() {
    }

    private function __clone() {
    }
}

$firstProduct = Product::getInstance();
$secondProduct = Product::getInstance();

$firstProduct->mix = 'test';
$secondProduct->mix = 'example';

print_r($firstProduct->mix);
// example
print_r($secondProduct->mix);
// example

在很多情况下,需要为系统中的多个类创建单例的构造方式,这样,可以建立一个通用的抽象父工厂方法:

<?php

abstract class FactoryAbstract {

    protected static $instances = array();

    public static function getInstance() {
        $className = static::getClassName();
        if (!(self::$instances[$className] instanceof $className)) {
            self::$instances[$className] = new $className();
        }
        return self::$instances[$className];
    }

    public static function removeInstance() {
        $className = static::getClassName();
        if (array_key_exists($className, self::$instances)) {
            unset(self::$instances[$className]);
        }
    }

    final protected static function getClassName() {
        return get_called_class();
    }

    protected function __construct() { }

    final protected function __clone() { }
}

abstract class Factory extends FactoryAbstract {

    final public static function getInstance() {
        return parent::getInstance();
    }

    final public static function removeInstance() {
        parent::removeInstance();
    }
}
// using:

class FirstProduct extends Factory {
    public $a = [];
}
class SecondProduct extends FirstProduct {
}

FirstProduct::getInstance()->a[] = 1;
SecondProduct::getInstance()->a[] = 2;
FirstProduct::getInstance()->a[] = 3;
SecondProduct::getInstance()->a[] = 4;

print_r(FirstProduct::getInstance()->a);
// array(1, 3)
print_r(SecondProduct::getInstance()->a);
// array(2, 4)

适配器模式:

适配器模式可以用来在现有接口和不兼容的类之间进行适配。从表面上看,适配器模式很像门面模式,都对别的对象进行包装并改变其呈现的接口。二者的区别在与它们如何改变接口,门面元素展现的是一个简化的接口,它并不提供额外的选择,而且有时为了方便完成常见任务它还会做出一些假定。而适配器则要把一个接口转换为另一个接口,它并不会过滤某些能力,也不会简化接口。

 1 var str = {
 2     a: 'a',
 3     b: 'b',
 4     c: 'c'
 5 };
 6 function i(s1,s2,s3) {
 7     console.log(s1 + ',' + s2 + ',' + s3);
 8 }
 9 function demo(o) {
10     i(o.a,o.b,o.c);
11 }

 

Registry

注册台模式并不是很常见,它也不是一个典型的创建模式,只是为了利用静态方法更方便的存取数据。

<?php
/**
* Registry class
*/
class Package {

    protected static $data = array();

    public static function set($key, $value) {
        self::$data[$key] = $value;
    }

    public static function get($key) {
        return isset(self::$data[$key]) ? self::$data[$key] : null;
    }

    final public static function removeObject($key) {
        if (array_key_exists($key, self::$data)) {
            unset(self::$data[$key]);
        }
    }
}

Package::set('name', 'Package name');

print_r(Package::get('name'));
// Package name

装饰者模式:

装饰者模式可用来透明地把对象包装在具有同样接口的另一个对象之中,装饰者可以用于为对象添加功能,可以用来代替大量子类。装饰者模式和组合模式有很多共同点,它们都与所包装的对象实现统一的接口并且会把任何方法条用传递给这些对象。可是组合模式用于把众多子对象组织为一个整体,而装饰者模式用于在不修改现有对象或从派生子类的前提下为其添加方法。如下:

1 var m = {};
2 m.child = {};
3 m.child.one = function() {};
4 m.child.two = function() {};

 

Factory(工厂模式)

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

<?php

interface Factory {
    public function getProduct();
}

interface Product {
    public function getName();
}

class FirstFactory implements Factory {

    public function getProduct() {
        return new FirstProduct();
    }
}

class SecondFactory implements Factory {

    public function getProduct() {
        return new SecondProduct();
    }
}

class FirstProduct implements Product {

    public function getName() {
        return 'The first product';
    }
}

class SecondProduct implements Product {

    public function getName() {
        return 'Second product';
    }
}

$factory = new FirstFactory();
$firstProduct = $factory->getProduct();
$factory = new SecondFactory();
$secondProduct = $factory->getProduct();

print_r($firstProduct->getName());
// The first product
print_r($secondProduct->getName());
// Second product

 享元模式:

 享元模式最适合于解决因创建大量类似对象而累及性能的问题。通过把大量独立对象转化为少量共享对象,可以降低运行web应用程序所需的资源数量。

javascript设计模式中的示例:

 1 //汽车登记示例
 2   var Car =function(make,model,year,owner,tag,renewDate){
 3     this.make=make;
 4     this.model=model;
 5     this.year=year;
 6     this.owner=owner;
 7     this.tag=tag;
 8     this.renewDate=renewDate;
 9   }
10   Car.prototype = {
11     getMake:function(){
12       returnthis.make;
13     },
14     getModel:function(){
15       returnthis.model;
16     },
17     getYear:function(){
18       returnthis.year;
19     },
20     transferOwner:function(owner,tag,renewDate){
21       this.owner=owner;
22       this.tag=tag;
23       this.renewDate=renewDate;
24     },
25     renewRegistration:function(renewDate){
26       this.renewDate=renewDate;
27     }
28   }
29   //数据量小到没多大的影响,数据量大的时候对计算机内存会产生压力,下面介绍享元模式优化后
30   //包含核心数据的Car类
31   var Car=function(make,model,year){
32     this.make=make;
33     this.model=model;
34     this.year=year;
35   }
36   Car.prototype={
37     getMake:function(){
38       returnthis.make;
39     },
40     getModel:function(){
41       returnthis.model;
42     },
43     getYear:function(){
44       returnthis.year;
45     }
46   }
47   //中间对象,用来实例化Car类
48   var CarFactory=(function(){
49     var createdCars = {};
50     return {
51       createCar:function(make,model,year){
52         var car=createdCars[make+"-"+model+"-"+year];
53         return car ? car : createdCars[make +'-'+ model +'-'+ year] =(new Car(make,model,year));
54       }
55     }
56   })();
57   //数据工厂,用来处理Car的实例化和整合附加数据
58   var CarRecordManager = (function() {
59     var carRecordDatabase = {};
60     return {
61       addCarRecord:function(make,model,year,owner,tag,renewDate){
62         var car = CarFactory.createCar(make, model, year);
63         carRecordDatabase[tag]={
64           owner:owner,
65           tag:tag,
66           renewDate:renewDate,
67           car:car
68       }
69     },
70       transferOwnership:function(tag, newOwner, newTag, newRenewDate){
71         var record=carRecordDatabase[tag];
72         record.owner = newOwner;
73         record.tag = newTag;
74         record.renewDate = newRenewDate;
75       },
76       renewRegistration:function(tag,newRenewDate){
77         carRecordDatabase[tag].renewDate=newRenewDate;
78       },
79       getCarInfo:function(tag){
80         return carRecordDatabase[tag];
81       }
82     }
83   })();

 

AbstractFactory(抽象工厂模式)

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

<?php

class Config {
    public static $factory = 1;
}

interface Product {
    public function getName();
}

abstract class AbstractFactory {

    public static function getFactory() {
        switch (Config::$factory) {
            case 1:
                return new FirstFactory();
            case 2:
                return new SecondFactory();
        }
        throw new Exception('Bad config');
    }

    abstract public function getProduct();
}

class FirstFactory extends AbstractFactory {
    public function getProduct() {
        return new FirstProduct();
    }
}
class FirstProduct implements Product {
    public function getName() {
        return 'The product from the first factory';
    }
}

class SecondFactory extends AbstractFactory {
    public function getProduct() {
        return new SecondProduct();
    }
}
class SecondProduct implements Product {
    public function getName() {
        return 'The product from second factory';
    }
}

$firstProduct = AbstractFactory::getFactory()->getProduct();
Config::$factory = 2;
$secondProduct = AbstractFactory::getFactory()->getProduct();

print_r($firstProduct->getName());
// The first product from the first factory
print_r($secondProduct->getName());
// Second product from second factory

代理模式:

代理是一个对象,它可以用来控制对另一个对象的访问。它与另外那个对象实现了同样的接口,并且会把任何方法调用传递给那个对象。代理模式适合处理实例化比较费时的本体,也适合处理那些需要较长时间才能把数据载入用户界面的类。

javascript设计模式中的示例:

 1 var Publication =new Interface('Publication', ['getIsbn', 'setIsbn', 'getTitle', 'setTitle', 'getAuthor', 'setAuthor', 'display']);
 2   var Book =function(isbn, title, author) {
 3       //...
 4   } 
 5   // implements Publication
 6   implements(Book,Publication);
 7 
 8   /* Library interface. */
 9   var Library =new Interface('Library', ['findBooks', 'checkoutBook', 'returnBook']);
10 
11   /* PublicLibrary class. */
12   var PublicLibrary =function(books) {
13       //...
14   };
15   // implements Library
16   implements(PublicLibrary,Library); 
17 
18   PublicLibrary.prototype = {
19       findBooks: function(searchString) {
20          //...
21       },
22       checkoutBook: function(book) {
23           //...
24       },
25       returnBook: function(book) {
26           //...
27       }
28   };
29 
30   /* PublicLibraryProxy class, a useless proxy. */
31   var PublicLibraryProxy =function(catalog) { 
32       this.library =new PublicLibrary(catalog);
33   };
34   // implements Library
35   implements(PublicLibraryProxy,Library);
36 
37   PublicLibraryProxy.prototype = {
38       findBooks: function(searchString) {
39           returnthis.library.findBooks(searchString);
40       },
41       checkoutBook: function(book) {
42           returnthis.library.checkoutBook(book);
43       },
44       returnBook: function(book) {
45           returnthis.library.returnBook(book);
46       }
47   };

 

Object pool(对象池)

澳门新葡亰娱乐在线,对象池可以用于构造并且存放一系列的对象并在需要时获取调用:

class Factory {

    protected static $products = array();

    public static function pushProduct(Product $product) {
        self::$products[$product->getId()] = $product;
    }

    public static function getProduct($id) {
        return isset(self::$products[$id]) ? self::$products[$id] : null;
    }

    public static function removeProduct($id) {
        if (array_key_exists($id, self::$products)) {
            unset(self::$products[$id]);
        }
    }
}

Factory::pushProduct(new Product('first'));
Factory::pushProduct(new Product('second'));

print_r(Factory::getProduct('first')->getId());
// first
print_r(Factory::getProduct('second')->getId());
// second

观察者模式:

观察者模式是一种管理人与其任务之间的关系的得力工具。观察者模式中存在两个角色:观察者和被观察者。这种模式的好处是你可以对程序中某个对象的状态进行观察,并且在其发生改变时能够得到通知。

 1   var f1 =function(){
 2      //code
 3   }
 4   var f2 =function(){
 5      //code
 6   }
 7   addEvent(element,'click',f1);
 8   addEvent(element,'click',f2)
 9 
10 
11   element.onclick = f1;
12   element.onclick = f2;

 

发表评论

电子邮件地址不会被公开。 必填项已用*标注

相关文章

网站地图xml地图