1.概述
工厂设计模式是一种创建型模式,它提供了一种创建对象的最佳方式。在工厂模式中,我们创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象。
1.1针对的问题
在面向对象编程中,最常用的方法是new一个操作符产生一个对象实例,new对象操作符就是用来构造对象实例的,但是在一些情况下,new操作符直接生成对象会带来一些问题,举例说,许多类型对象的创建都需要一系列的步骤,可能需要计算或取得对象的初始设置,选择生成哪个子对象实例,或者在生成需要的对象之前必须先生成一些辅助功能的对象,这些情况下,对象的建立就是一个过程,不仅是一个操作。
1.2 分析
实例化一个对象sample 一般会想到的方法是通过构造器来创建$sample =new sample();但是在实际情况下最好不要这样做,如果sample类的在实例化的时候需要初始化参数而这些参数需要别的类的信息,这样你new的话会增加你代码的耦合度,不利于维护。所以我们就需要将创建实例的工作和使用使用实例的工作分开,即:使用工厂方法创建实例的工作封装起来。这样我们在需要调用对象的时候就不需要关心那些复杂的实例化问题。
1.3. 工厂模式类型
工厂模式主要是为创建对象提供过渡接口,以便将创建对象的具体过程屏蔽隔离起来,达到灵活性的目的。
工厂模式一共三类
(1)简单工厂模式: 允许接口创建对象,但不会暴露对象的创建逻辑。
(2)工厂方法模式:允许接口创建对象,但使用哪个类来创建对象,则是交由子类决定的。
(3)抽象工厂模式:抽象工厂是一个能够创建一系列相关的对象而无需指定/公开其具体类的接口。该模式能够提供其他工厂的对象,在其内部创建其他对象。
2.工厂模式特征
2.1简单工厂模式
2.1.1角色组成
1.抽象工厂类
2.抽象产品类
3.具体产品类
简单工厂模式中有抽象产品类:用来定义具体产品的共有属性,工厂类则负责生产具体产品。
直接看代码:定义一个抽象产品类形状类Shape
abstract class Shape{
abstract public shape();
}
定义具体产品:
class Circle extends Shape{
public function shape(){
echo '圆形';
}
}
class Rectangle extends Shape{
public function shape(){
echo '矩形';
}
}
class Triangle extends Shape{
public function shape(){
echo '三角形';
}
}
定义一个工厂生产具体产品
class ShapeFactory{
public static function getShape($sh)
{
switch($sh){
case '圆形':
return new Circle();
break;
case '矩形':
return new Rectanle();
break;
case '三角形':
return new Triangle();
break;
default:
echo "该形状不存在 \n";
break;
}
}
}
//调用
ShapeFactory::getShape('圆形');
从简单工厂中我们可以看出使用一个静态方法将实例化的创建和使用分离开。我们只需要调用方法传递参数就可以获得我们需要的对象。
缺点:我们不难看出如果我们想要增加一个形状类的产品不仅需要添加一个导出类而且我们必须要修改静态方法getshape,这样就违背了开闭原则(对于扩展是开放的,对于修改是封闭的)
这时我们就可以想我们是不是可以将工厂也抽象出来?让具体工厂来负责创建具体产品对象,就上面的例子我们可以创建一个圆形的具体工厂让他只负责创建圆形产品对象,这样当我们想要增加一个产品的时候我们就只需要增加对应的工厂就行了,不需要修改其他东西。接下来我们说说工厂模式
2.2工厂方法模式
工厂方法模式是对简单工厂模式进一步的解耦,因为在工厂方法模式中是一个子类对应一个工厂类,而这些工厂类都实现于一个抽象接口。这相当于是把原本会因为业务代码而庞大的简单工厂类,拆分成了一个个的工厂类,这样代码就不会都耦合在同一个类里了。
2.2.1 角色组成
1)抽象工厂角色: 这是工厂方法模式的核心,它与应用程序无关。是具体工厂角色必须实现的接口或者必须继承的父类。
2)具体工厂角色:它含有和具体业务逻辑有关的代码。由应用程序调用以创建对应的具体产品的对象。
3)抽象产品角色:它是具体产品继承的父类或者是实现的接口。
4)具体产品角色:具体工厂角色所创建的对象就是此角色的实例。
2.2.2实现
- 首先定义一个工厂接口:
interface Factory{
public function createOperation();
}
- 然后是具体的工厂类:
// 加法类工厂
class AddFactory implements Factory{
public function createOperation() {
echo "加法运算~\n";
return new Add();
}
}
// 减法类工厂
class SubFactory implements Factory{
public function createOperation() {
echo "减法运算~\n";
return new Sub();
}
}
...
- 运算类
//计算抽象类
interface Operation {
public function getResult($numberA,$numberB);
}
class Add implements Operation{
// 加法计算
public function getResult($numberA, $numberB) {
return $numberA + $numberB;
}
}
class Sub implements Operation{
// 减法计算
public function getResult($numberA, $numberB) {
return $numberA-$numberB;
}
}
- 客户端代码:
class Client{
public function run(){
$farm = "Factory";
$type = "Add";
$className = $type.$farm;
$addFactory = new AddFactory();
$subFactory = new SubFactory();
echo $addFactory->createOperation(1,1);
echo $subFactory->createOperation(1,1);
}
}
2.2.3分析
工厂模式中,要增加产品类时也要相应地增加工厂类,客户端的代码也增加了不少。工厂方法把简单工厂的内部逻辑判断转移到了客户端代码来进行。
你想要加功能,本来是改工厂类的,而现在是修改客户端。而且各个不同功能的实例对象的创建代码,也没有耦合在同一个工厂类里,这也是工厂方法模式对简单工厂模式解耦的一个体现。工厂方法模式克服了简单工厂会违背开-闭原则的缺点,又保持了封装对象创建过程的优点。
但工厂方法模式的缺点是每增加一个产品类,就需要增加一个对应的工厂类,增加了额外的开发量。
2.3抽象工厂模式 生产工厂的工厂 供应链
抽象工厂模式是工厂方法模式的升级版本,它用来创建一组相关或者相互依赖的对象。
抽象工厂模式提供一个创建一系列相关或者相互依赖对象的接口,而无需指定他们具体的类。就是一个工厂里放一些相关的类,使工厂数减少。
最大的好处便是易于交换产品系列,其次是让具体的创建实例过程与客户端分离,客户端通过他们的抽象接口操纵实例,产品的具体类名也被具体工厂的实现分离。不会出现在客户代码中
2.3.1场景:对数据库中的表进行修改
- 我们现在要对mysql/oracle数据库中的User表进行操作,User表定义如下:
public class User {
private $uid;
private $uname;
public int getUid() {
return $uid;
}
public function setUid($uid) {
$this->uid = $uid;
}
public function getUname() {
return $uname;
}
public function setUname($uname) {
$this->uname = $uname;
}
}
- 接下来我们定义一个对User进行操作的接口:
interface IUser {
public function insert($user);
public function getUser($uid);
}
- 实现一个对mysql中User进行操作的类:
class mysqlUser implements IUser{
public function insert($user){
print("在mysql中的user表中插入一条元素");
}
public function getUser($id){
print("在mysql中的user表得到id为".$id."的一条数据");
return null;
}
}
实现对oracle中User进行操作的类:
class oracleUser implements IUser{
public function insert($user) {
println("在oracle中的user表中插入一条元素");
}
public function getUser($uid) {
print("在oracle中的user表得到id为".$uid."的一条数据");
return null;
}
}
- 接下来定义一个工厂接口,用于生产访问User表的对象:
interface sqlFactory {
public function createUser(); //用于访问User表的对象
}
- 生产mysqlUser对象的mysql工厂类:
class mysqlFactory implements sqlFactory {
public function createUser() {
return new mysqlUser(); //访问mysql中User表的对象
}
}
生成oracleUser对象的oracle工厂类:
class oracleFactory implements sqlFactory {
public IUser createUser() {
return new oracleUser(); //访问oracle中User表的对象
}
}
- 最后用户测试类如下:
class test_abstractFactory {
public static function main($args) {
$factory1 = new mysqlFactory();
$userOperator = $factory1.createUser();
$userOperator.getUser(1);
$userOperator.insert(new User());
}
}
结果为:
在mysql中的user表得到id为1的一条数据
在mysql中的user表中插入一条元素
2.3.2 分析
抽象工厂模式是工厂方法模式的升级版本,他用来创建一组相关或者相互依赖的对象。他与工厂方法模式的区别就在于,工厂方法模式针对的是一个产品等级结构;而抽象工厂模式则是针对的多个产品等级结构。在编程中,通常一个产品结构,表现为一个接口或者抽象类,也就是说,工厂方法模式提供的所有产品都是衍生自同一个接口或抽象类,而抽象工厂模式所提供的产品则是衍生自不同的接口或抽象类。
总结
工厂具有下列优点:松耦合,即对象的创建可以独立于类的实现;客户端无需了解创建对象的类,但是照样可以使用它来创建对象。它只需要知道需要传递的接口、方法和参数,就能够创建 所需类型的对象了。这简化了客户端的实现;可以轻松地在工厂中添加其他类来创建其他类型的对象,而这无需更改客户端代码。最简单的情况下,客户端只需要传递一个参数就可以了;工厂还可以重用现有对象。但是,如果客户端直接创建对象的化,总是创建一个新对象。
网友评论