装饰器模式(转)

动态的给一个对象添加一些额外的职责。就增加功能来说,Decorator模式相比生成子类更为灵活【GOF95】
装饰模式是以对客户透明的方式动态地给一个对象附加上更多的职责。这也就是说,客户端并不会觉得对象在装饰前和装饰后有什么不同。装饰模式可以在不使用创造更多子类的情况下,将对象的功能加以扩展。

【装饰模式结构图】
装饰器模式结构图

【装饰模式中主要角色】
抽象构件(Component)角色:定义一个对象接口,以规范准备接收附加职责的对象,从而可以给这些对象动态地添加职责。
具体构件(Concrete Component)角色:定义一个将要接收附加职责的类。
装饰(Decorator)角色:持有一个指向Component对象的指针,并定义一个与Component接口一致的接口。
具体装饰(Concrete Decorator)角色:负责给构件对象增加附加的职责。
【装饰模式的优缺点】
装饰模式的优点:
1、比静态继承更灵活;
2、避免在层次结构高层的类有太多的特征
装饰模式的缺点:
1、使用装饰模式会产生比使用继承关系更多的对象。并且这些对象看上去都很想像,从而使得查错变得困难。
【装饰模式适用场景】
1、在不影响其他对象的情况下,以动态、透明的方式给单个对象添加职责。
2、处理那些可以撤消的职责,即需要动态的给一个对象添加功能并且这些功能是可以动态的撤消的。
3、当不能彩生成子类的方法进行扩充时。一种情况是,可能有大量独立的扩展,为支持每一种组合将产生大量的子类,使得子类数目呈爆炸性增长。另一种情况可能是因为类定义被隐藏,或类定义不能用于生成子类。
【装饰模式PHP示例】

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
99
100
101
102
103
104
105
106
107
108
109
110
<?php
/**
* 装饰模式 2010-06-03 sz
* @author phppan.p#gmail.com
* http://www.phppan.com 哥学社成员(http://www.blog-brother.com/)
* @package design pattern
*/

/**
* 抽象构件角色
*/
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();
}
}

/**
* 具体装饰类A
*/
class ConcreteDecoratorA extends Decorator {
public function __construct(Component $component) {
parent::__construct($component);

}

public function operation() {
parent::operation(); // 调用装饰类的操作
$this->addedOperationA(); // 新增加的操作
}

/**
* 新增加的操作A,即装饰上的功能
*/
public function addedOperationA() {
echo 'Add Operation A <br />';
}
}

/**
* 具体装饰类B
*/
class ConcreteDecoratorB extends Decorator {
public function __construct(Component $component) {
parent::__construct($component);

}

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

/**
* 新增加的操作B,即装饰上的功能
*/
public function addedOperationB() {
echo 'Add Operation B <br />';
}
}

/**
* 具体构件
*/
class ConcreteComponent implements Component{

public function operation() {
echo 'Concrete Component operation <br />';
}

}

/**
* 客户端
*/
class Client {

/**
* Main program.
*/
public static function main() {
$component = new ConcreteComponent();
$decoratorA = new ConcreteDecoratorA($component);
$decoratorB = new ConcreteDecoratorB($decoratorA);

$decoratorA->operation();
$decoratorB->operation();
}

}

Client::main();
?>

从以上示例可以看出:
1、装饰类中有一个属性$_component,其数据类型是Component;
2、装饰类实现了Component接口;
3、接口的实现是委派给父类,但并不是单纯的委派,还有功能的增强;
4、具体装饰类实现了抽象装饰类的operation方法。
本文转自:http://www.phppan.com/2010/06/php-design-pattern-4-decorator/

三种工厂模式

阅读《设计模式之禅》后的总结。

前言

之前的工厂模式有看过,但是没过多久就会忘,主要还是自己平常写代码业务逻辑的时候缺少思考,可能也就会用个单例模式。然后网上很多有关设计模式的介绍,感觉有些还是介绍的不够具体,或者说只介绍到了工厂模式的一部分吧。

工厂方法模式

工厂方法模式的定义:Define an interface for ceating an object ,but let subclasses decide which class to instantiate.Factory Method lets a class defer instantiation to subclasses(定义一个用于创建对象的接口,让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到器子类。)

1
2
3
4
5
6
7
8
  /**
* 产品类接口
*/
interface Product{
public function method1();
public function method2();

}
1
2
3
4
5
6
7
8
9
10
11
12
  /**
* 具体产品类
*/
public class ConcreteProduct1 implements Product{
public function method1(){
//业务逻辑
}
public function method2(){
//业务逻辑
};

}
1
2
3
4
5
6
  /**
* 工厂类
*/
interface Creator{
public creatorProduct();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
  /**
* 具体工厂类
*/
public class ConcreteCreator implements Creator{

public function creatorProduct($product){
try{
$productObj = new $product;
}catch(Exception $e){
//异常处理
}
return $productObj;
}
}
1
2
3
4
5
6
7
8
public class Client{
public function main(){
//用工厂类制造product1
$fc = new ConcreteCreator();
$cp1 = $fc->creatorProduct("ConcreteProduct1");
$cp1->method1();
}
}

简单工厂模式

简单工厂模式又称静态工厂模式

就是把上面的方法ConcreteCreator()改为静态就可以了。
优点很明显,就是不用实例化工厂类直接调用工厂方法就可以了。

抽象工厂模式

抽象工厂模式

优点

  • 封装性

  • 产品族内的约束为非公开状态

缺点

  • 产品族的扩展非常困难
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
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
<?php
/**
* 抽象工厂模式 2010-05-28 sz
* @author phppan.p#gmail.com
* http://www.phppan.com 哥学社成员(http://www.blog-brother.com/)
* @package design pattern
*/

/**
* 抽象工厂
*/
interface AbstractFactory {
/**
* 创建等级结构为A的产品的工厂方法
*/
public function createProductA();

/**
* 创建等级结构为B的产品的工厂方法
*/
public function createProductB();

}

/**
* 具体工厂1
*/
class ConcreteFactory1 implements AbstractFactory{

public function createProductA() {
return new ProductA1();
}

public function createProductB() {
return new ProductB1();
}
}


/**
* 具体工厂2
*/
class ConcreteFactory2 implements AbstractFactory{

public function createProductA() {
return new ProductA2();
}

public function createProductB() {
return new ProductB2();
}
}

/**
* 抽象产品A
*/
interface AbstractProductA {

/**
* 取得产品名
*/
public function getName();
}

/**
* 抽象产品B
*/
interface AbstractProductB {

/**
* 取得产品名
*/
public function getName();
}

/**
* 具体产品A1
*/
class ProductA1 implements AbstractProductA {
private $_name;

public function __construct() {
$this->_name = 'product A1';
}

public function getName() {
return $this->_name;
}
}


/**
* 具体产品A2
*/
class ProductA2 implements AbstractProductA {
private $_name;

public function __construct() {
$this->_name = 'product A2';
}

public function getName() {
return $this->_name;
}
}


/**
* 具体产品B1
*/
class ProductB1 implements AbstractProductB {
private $_name;

public function __construct() {
$this->_name = 'product B1';
}

public function getName() {
return $this->_name;
}
}

/**
* 具体产品B2
*/
class ProductB2 implements AbstractProductB {
private $_name;

public function __construct() {
$this->_name = 'product B2';
}

public function getName() {
return $this->_name;
}
}


/**
* 客户端
*/
class Client {

/**
* Main program.
*/
public static function main() {
self::run(new ConcreteFactory1());
self::run(new ConcreteFactory2());
}

/**
* 调用工厂实例生成产品,输出产品名
* @param $factory AbstractFactory 工厂实例
*/
public static function run(AbstractFactory $factory) {
$productA = $factory->createProductA();
$productB = $factory->createProductB();
echo $productA->getName(), '<br />';
echo $productB->getName(), '<br />';
}

}

Client::main();
?>

单例模式

单模式的定义:确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。

优点

  • 由于单励模式在内存中只有一个实例,减少了内存的开支,特别是一个对象需要频繁地创建和销毁,而且创建或销毁时性能又无法优化,单例模式的优势就非常明显。

  • 由于单例模式只生成一个实例,所以减少了系统的性能开销,当一个对象的产生需要比较多的资源时,如读取配置,产生其他依赖对象时,则可以通过在应用启动时产生一个单对象。

使用场景

  • 创建一个对象需要消耗很多资源的,如要访问IO和数据库等资源。

  • 在整个项目中需要一个共享的访问点或共享数据,例如一个Web页面上的计数器,可以不用每次刷新都记录到数据库中,使用单例模式保持计数器的值。

单模式的如下面代码所示。

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
<?php

/**
* 懒汉式单例类
*/
class Singleton {

/**
* 静态成品变量 保存全局实例
*/
private static $_instance = NULL;

/**
* 私有化默认构造方法,保证外界无法直接实例化
*/
private function __construct() {
}

/**
* 静态工厂方法,返还此类的唯一实例
*/
public static function getInstance() {
if (is_null(self::$_instance)) {
self::$_instance = new Singleton();
}

return self::$_instance;
}

/**
* 防止用户克隆实例
*/
public function __clone(){
die('Clone is not allowed.' . E_USER_ERROR);
}

/**
* 测试用方法
*/
public function test() {
echo 'Singleton Test!';
}

}

/**
* 客户端
*/
class Client {

/**
* Main program.
*/
public static function main() {
$instance = Singleton::getInstance();
$instance->test();
}
}

Client::main();
?>

适配器模式

读了《设计模式之蝉》之适配器模式后的总结。

C适配器模式的定义:
onvert the interface of a class into another interface clients expect.Adapter lets classes work togeher that couldn’t otherwise because of incompatible interfaces.(将一个类的接口变换成客户端所期待的另一种接口,从而使原本因接口不匹配而无发在一起工作的两个类能够在一起工作)

类适配器

原有公司的内部员工形象有这么一个接口(target)

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 
interface IuserInfo{
public function getUserName();
public function getHomeAdress();
public function getMobileNumber();
public function getOfficeTelNumber();
public function getJobPosition();
public function getHomeNumber();
}

public class UserInfo implements IuserInfo{
public function getUserName(){
echo "名字是...."
};
public function getHomeAdress(){
echo "家庭住址是...."
};
public function getMobileNumber(){
echo "手机号码是...."
};
public function getOfficeTelNumber(){
echo "办公室号码是..."
};
public function getHomeNumber(){
echo "家里的号码是..."
};

public function getJobPosition(){
echo "办公室位置式..."
};

}
public class clients{
public main function(){
$user = new UserInfo();
$user->getUserName();
}
}

一切看上去都没有什么问题,但是可能处于某种原因,公司需要从其他公司租用的员工的信息。而其他公司租用的员工信息的接口是这个样子的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?php
interface IouterUserInfo(){
public function userNumber();
public function userAddress();
}
public class OuterUser implements IouterUserInfo{
public function userNumber(){
$telnumber = ['moblie_number' => '135...',
'office_mobile_number' =>'001-12..',
'home_tel_number' => '002-22'
];
return $telnumber;
}
public function baseInfo(){
$info = ['name' => 'steve',
'office_address' => '办公室位置...',
'home_address' => '家的位置是...'
];
return $info;
}
}

这和我们原先的接口完全不同,我们希望还是希望以自己接口设计去获取信息,因为这样就不需要大修改原先已经写好的客户端类。
我们这里就可以用到适配器模式。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?php
//类适配器 OuterUserInfo
public class OuterUserInfo extends OuterUser implements UserInfo{
public function getUserName(){
echo $this->baseInfo()['name'];
};
public function getHomeAdress(){
};
public function getMobileNumber(){
};
public function getOfficeTelNumber(){
};
public function getHomeNumber(){
};
public function getJobPosition(){
};
}
//客户端相对于之前只是改了实例其他不变。
public class clients{
public main function(){
$user = new OuterUserInfo();
$user->getUserName();
}
}

对象适配器

如果是多个不同的接口则可以用对象适配器
对象适配器和类适配器的不同就是,对象适配器是把对象注入到适配器内部而不是通过继承直接使用。假如我们把IouterUserInfo一分为二,mobile归IouterUserMobile(通讯录),IouterUserInfo接口则是基本信息(姓名和地址)。那么类适配器是不能继承两个类的,所以采用对象适配器来解决。

1
2
3
4
5
6
7
8
9
<?php 
public class OuterUserInfo implements UserInfo{
private $outerUserMobile;
private $outerUserInfo;
public function __construct(OuterUserInfo $outrUserInfo,OuterUserMoblie $outerUserMobile) {
$this->outerUserMobile = $outerUserMobile;
$this->outerUserInfo = $outerUserInfo;
}
}
Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×