简单工厂模式
简单工厂模式是属于创建型模式,又叫做静态工厂方法(Static Factory Method)模式,但不属于23种GOF设计模式之一。简单工厂模式是由一个工厂对象决定创建出哪一种产品类的实例。简单工厂模式是工厂模式家族中最简单实用的模式,可以理解为是不同工厂模式的一个特殊实现。
简单工厂模式中工厂对象的主要功能为决定创建出哪一种产品类,所以,工厂类中是存在判断的。以实现一个简单的计算器为例,代码如下:
客户端代码:
<?php
$numberA = isset($_GET['a'])?$_GET['a']:0;
$numberB = isset($_GET['b'])?$_GET['b']:0;
$type = isset($_GET['type'])?$_GET['type']:'+';
include './Factory.php';
$server = Factory::getClaculator($type);
if (empty($server))
$result = 0;
else
$result = $server->evaluate($numberA, $numberB);
var_dump($result);
工厂类代码:
<?php
include './Claculator.php';
include './claculator/Add.php';
include './claculator/Divide.php';
include './claculator/Tract.php';
include './claculator/Ride.php';
class Factory
{
public static function getClaculator($type)
{
switch ($type)
{
case '+' :
$server = new Add();
break;
case '-' :
$server = new Tract();
break;
case '*' :
$server = new Ride();
break;
case '/' :
$server = new Divide();
break;
default :
$server = null;
break;
}
return $server;
}
}
计算类统一接口:
<?php
Interface Claculator
{
function evaluate($numberA, $numberB);
}
加法类:
<?php
class Add implements Claculator
{
public function evaluate($numberA, $numberB)
{
return $numberA+$numberB;
}
}
减法类:
<?php
class Tract implements Claculator
{
public function evaluate($numberA, $numberB)
{
return $numberA-$numberB;
}
}
乘法类:
<?php
class Ride implements Claculator
{
public function evaluate($numberA, $numberB)
{
return $numberA*$numberB;
}
}
除法类:
<?php
class Divide implements Claculator
{
public function evaluate($numberA, $numberB)
{
if ($numberB==0)
return null;
return $numberA/$numberB;
}
}
UML图:
简单工厂模式主要减少了客户端代码的修改,降低了耦合性。举个例子,假如我们现在需要扩展这个计算器,为这个计算器加一个平方根的计算功能,我们只需要增加一个SquareRoot类继承Claculator接口,在简单工厂中增加一条分支即可,无需改变客户端的代码。但是设计模式的六大原则中存在一个开闭原则(Open-Closed Principle, OCP),其中的Factory显然违背了这一原则,所以简单工厂模式并不属于23种GOF设计模式之一,而工厂模式与简单工厂模式的区别主要也在这里。工厂模式下一篇整理。
策略模式
策略模式定义了算法家族,分别封装起来,让它们之间可以相互替换,此模式让算法的变化,不会影响到使用算法的客户。
示例:在实际项目中,我们经常使用接口用来做数据对接,而JSON格式和XML格式常作为对接数据的格式,我们可以使用策略模式进行封装。
客户端:
<?php
include './Strategy.php';
include './strategy/JsonData.php';
include './strategy/XmlData.php';
Class Client
{
public static function index($type)
{
$data = [
['name' => 'renling', 'age' => '18', 'sex' => '1'],
['name' => 'renling', 'age' => '18', 'sex' => '1'],
['name' => 'renling', 'age' => '18', 'sex' => '1'],
['name' => 'renling', 'age' => '18', 'sex' => '1']
];
switch ($type) {
case 'json':
$server = new JsonData();
break;
case 'xml':
$server = new XmlData();
break;
default :
$server = null;
break;
}
if (empty($server)) {
return '';
} else {
$server->getData($data);
}
}
}
$type = isset($_GET['type'])?$_GET['type']:'json';
Client::index($type);
策略抽象类:
<?php
interface ResponseStrategy
{
function getData($data);
}
JSON格式对象:
<?php
class JsonData implements ResponseStrategy
{
public function getData($data)
{
ob_end_clean();
header('Content-Type:application/json');
echo json_encode($data, JSON_UNESCAPED_UNICODE);
die;
}
}
XML格式对象(封装XML的方法是仿照thinkphp5写的):
<?php
class XmlData implements ResponseStrategy
{
public function getData($data)
{
$options = [
// 根节点名
'root_node' => 'strategy',
// 根节点属性
'root_attr' => '',
//数字索引的子节点名
'item_node' => 'item',
// 数字索引子节点key转换的属性名
'item_key' => 'id',
// 数据编码
'encoding' => 'utf-8',
];
$data = $this->xmlEncode($data, $options['root_node'], $options['item_node'], $options['root_attr'], $options['item_key'], $options['encoding']);
ob_end_clean();
header('Content-Type:text/xml');
echo $data;die;
}
protected function xmlEncode($data, $root, $item, $attr, $id, $encoding)
{
if (is_array($attr)) {
$array = [];
foreach ($attr as $key => $value) {
$array[] = "{$key}=\"{$value}\"";
}
$attr = implode(' ', $array);
}
$attr = trim($attr);
$attr = empty($attr) ? '' : " {$attr}";
$xml = "<?xml version=\"1.0\" encoding=\"{$encoding}\"?>";
$xml .= "<{$root}{$attr}>";
$xml .= $this->dataToXml($data, $item, $id);
$xml .= "</{$root}>";
return $xml;
}
protected function dataToXml($data, $item, $id)
{
$xml = $attr = '';
if ($data instanceof Collection || $data instanceof Model) {
$data = $data->toArray();
}
foreach ($data as $key => $val) {
if (is_numeric($key)) {
$id && $attr = " {$id}=\"{$key}\"";
$key = $item;
}
$xml .= "<{$key}{$attr}>";
$xml .= (is_array($val) || is_object($val)) ? $this->dataToXml($val, $item, $id) : $val;
$xml .= "</{$key}>";
}
return $xml;
}
}
我们可以通过访问Client.php?type=xml或者Client.php?type=json来获取不同格式的数据。
UML图:
策略模式与简单工厂模式的区别
- 所属类别不同。总体来说设计模式分为五大类,分别为创建型模式、结构型模式、行为型模式、并发型模式和线程池模式,而策略模式属于行为型模式、简单工厂模式则属于创建型模式(PS:由于简单工厂不属于不属于23种GOF设计模式之一,所以我是根据工厂模式划分的)。
- 简单工厂模式是由一个工厂对象决定创建出哪一种产品类的实例,而策略模式是由客户端的代码自行判断创建哪一种产品的实例。
- 作用不一样。简单工厂模式只负责创建出产品类,而策略模式定义一系列的算法,并把它们一个个封装起来,并且使它们可相互替换(JsonData和XmlData可以相互替换)。
- 需要注意的一点是,策略模式定义的不同算法是可以相互替换并能完成相同的功能的。
两者的结合使用
两者结合使用时,将客户端的判断使用哪一种对象的代码放到简单工厂类中就好了,只需要修改Client类和增加Factory类即可,其余的与策略模式代码一致。
客户端:
<?php
include './Factory.php';
Class Client
{
public static function index($type)
{
$data = [
['name' => 'renling', 'age' => '18', 'sex' => '1'],
['name' => 'renling', 'age' => '18', 'sex' => '1'],
['name' => 'renling', 'age' => '18', 'sex' => '1'],
['name' => 'renling', 'age' => '18', 'sex' => '1']
];
Factory::getData($data, $type);
}
}
$type = isset($_GET['type'])?$_GET['type']:'json';
Client::index($type);
简单工厂类:
<?php
include './Strategy.php';
include './strategy/JsonData.php';
include './strategy/XmlData.php';
class Factory
{
public static function getData($data, $type = 'json')
{
switch ($type) {
case 'json':
$server = new JsonData();
break;
case 'xml':
$server = new XmlData();
break;
default :
$server = null;
break;
}
if (empty($server)) {
return '';
} else {
$server->getData($data);
}
}
}
UML图:
下一篇
初识设计模式——工厂模式