1.概述
工廠設計模式是一種創(chuàng)建型模式,它提供了一種創(chuàng)建對象的最佳方式。在工廠模式中,我們創(chuàng)建對象時不會對客戶端暴露創(chuàng)建邏輯,并且是通過使用一個共同的接口來指向新創(chuàng)建的對象。
1.1針對的問題
在面向?qū)ο缶幊讨?,最常用的方法是new一個操作符產(chǎn)生一個對象實例,new對象操作符就是用來構造對象實例的,但是在一些情況下,new操作符直接生成對象會帶來一些問題,舉例說,許多類型對象的創(chuàng)建都需要一系列的步驟,可能需要計算或取得對象的初始設置,選擇生成哪個子對象實例,或者在生成需要的對象之前必須先生成一些輔助功能的對象,這些情況下,對象的建立就是一個過程,不僅是一個操作。
1.2 分析
實例化一個對象sample 一般會想到的方法是通過構造器來創(chuàng)建$sample =new sample();但是在實際情況下最好不要這樣做,如果sample類的在實例化的時候需要初始化參數(shù)而這些參數(shù)需要別的類的信息,這樣你new的話會增加你代碼的耦合度,不利于維護。所以我們就需要將創(chuàng)建實例的工作和使用使用實例的工作分開,即:使用工廠方法創(chuàng)建實例的工作封裝起來。這樣我們在需要調(diào)用對象的時候就不需要關心那些復雜的實例化問題。
1.3. 工廠模式類型
工廠模式主要是為創(chuàng)建對象提供過渡接口,以便將創(chuàng)建對象的具體過程屏蔽隔離起來,達到靈活性的目的。
工廠模式一共三類
(1)簡單工廠模式: 允許接口創(chuàng)建對象,但不會暴露對象的創(chuàng)建邏輯。
(2)工廠方法模式:允許接口創(chuàng)建對象,但使用哪個類來創(chuàng)建對象,則是交由子類決定的。
(3)抽象工廠模式:抽象工廠是一個能夠創(chuàng)建一系列相關的對象而無需指定/公開其具體類的接口。該模式能夠提供其他工廠的對象,在其內(nèi)部創(chuàng)建其他對象。
2.工廠模式特征
2.1簡單工廠模式
2.1.1角色組成
1.抽象工廠類
2.抽象產(chǎn)品類
3.具體產(chǎn)品類
簡單工廠模式中有抽象產(chǎn)品類:用來定義具體產(chǎn)品的共有屬性,工廠類則負責生產(chǎn)具體產(chǎn)品。
直接看代碼:定義一個抽象產(chǎn)品類形狀類Shape
abstract class Shape{
abstract public shape();
}
定義具體產(chǎn)品:
class Circle extends Shape{
public function shape(){
echo '圓形';
}
}
class Rectangle extends Shape{
public function shape(){
echo '矩形';
}
}
class Triangle extends Shape{
public function shape(){
echo '三角形';
}
}
定義一個工廠生產(chǎn)具體產(chǎn)品
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;
}
}
}
//調(diào)用
ShapeFactory::getShape('圓形');
從簡單工廠中我們可以看出使用一個靜態(tài)方法將實例化的創(chuàng)建和使用分離開。我們只需要調(diào)用方法傳遞參數(shù)就可以獲得我們需要的對象。
缺點:我們不難看出如果我們想要增加一個形狀類的產(chǎn)品不僅需要添加一個導出類而且我們必須要修改靜態(tài)方法getshape,這樣就違背了開閉原則(對于擴展是開放的,對于修改是封閉的)
這時我們就可以想我們是不是可以將工廠也抽象出來?讓具體工廠來負責創(chuàng)建具體產(chǎn)品對象,就上面的例子我們可以創(chuàng)建一個圓形的具體工廠讓他只負責創(chuàng)建圓形產(chǎn)品對象,這樣當我們想要增加一個產(chǎn)品的時候我們就只需要增加對應的工廠就行了,不需要修改其他東西。接下來我們說說工廠模式
2.2工廠方法模式
工廠方法模式是對簡單工廠模式進一步的解耦,因為在工廠方法模式中是一個子類對應一個工廠類,而這些工廠類都實現(xiàn)于一個抽象接口。這相當于是把原本會因為業(yè)務代碼而龐大的簡單工廠類,拆分成了一個個的工廠類,這樣代碼就不會都耦合在同一個類里了。
2.2.1 角色組成
1)抽象工廠角色: 這是工廠方法模式的核心,它與應用程序無關。是具體工廠角色必須實現(xiàn)的接口或者必須繼承的父類。
2)具體工廠角色:它含有和具體業(yè)務邏輯有關的代碼。由應用程序調(diào)用以創(chuàng)建對應的具體產(chǎn)品的對象。
3)抽象產(chǎn)品角色:它是具體產(chǎn)品繼承的父類或者是實現(xiàn)的接口。
4)具體產(chǎn)品角色:具體工廠角色所創(chuàng)建的對象就是此角色的實例。
2.2.2實現(xiàn)
- 首先定義一個工廠接口:
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分析
工廠模式中,要增加產(chǎn)品類時也要相應地增加工廠類,客戶端的代碼也增加了不少。工廠方法把簡單工廠的內(nèi)部邏輯判斷轉(zhuǎn)移到了客戶端代碼來進行。
你想要加功能,本來是改工廠類的,而現(xiàn)在是修改客戶端。而且各個不同功能的實例對象的創(chuàng)建代碼,也沒有耦合在同一個工廠類里,這也是工廠方法模式對簡單工廠模式解耦的一個體現(xiàn)。工廠方法模式克服了簡單工廠會違背開-閉原則的缺點,又保持了封裝對象創(chuàng)建過程的優(yōu)點。
但工廠方法模式的缺點是每增加一個產(chǎn)品類,就需要增加一個對應的工廠類,增加了額外的開發(fā)量。
2.3抽象工廠模式 生產(chǎn)工廠的工廠 供應鏈
抽象工廠模式是工廠方法模式的升級版本,它用來創(chuàng)建一組相關或者相互依賴的對象。
抽象工廠模式提供一個創(chuàng)建一系列相關或者相互依賴對象的接口,而無需指定他們具體的類。就是一個工廠里放一些相關的類,使工廠數(shù)減少。
最大的好處便是易于交換產(chǎn)品系列,其次是讓具體的創(chuàng)建實例過程與客戶端分離,客戶端通過他們的抽象接口操縱實例,產(chǎn)品的具體類名也被具體工廠的實現(xiàn)分離。不會出現(xiàn)在客戶代碼中
2.3.1場景:對數(shù)據(jù)庫中的表進行修改
- 我們現(xiàn)在要對mysql/oracle數(shù)據(jù)庫中的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);
}
- 實現(xiàn)一個對mysql中User進行操作的類:
class mysqlUser implements IUser{
public function insert($user){
print("在mysql中的user表中插入一條元素");
}
public function getUser($id){
print("在mysql中的user表得到id為".$id."的一條數(shù)據(jù)");
return null;
}
}
實現(xiàn)對oracle中User進行操作的類:
class oracleUser implements IUser{
public function insert($user) {
println("在oracle中的user表中插入一條元素");
}
public function getUser($uid) {
print("在oracle中的user表得到id為".$uid."的一條數(shù)據(jù)");
return null;
}
}
- 接下來定義一個工廠接口,用于生產(chǎn)訪問User表的對象:
interface sqlFactory {
public function createUser(); //用于訪問User表的對象
}
- 生產(chǎn)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());
}
}
結(jié)果為:
在mysql中的user表得到id為1的一條數(shù)據(jù)
在mysql中的user表中插入一條元素
2.3.2 分析
抽象工廠模式是工廠方法模式的升級版本,他用來創(chuàng)建一組相關或者相互依賴的對象。他與工廠方法模式的區(qū)別就在于,工廠方法模式針對的是一個產(chǎn)品等級結(jié)構;而抽象工廠模式則是針對的多個產(chǎn)品等級結(jié)構。在編程中,通常一個產(chǎn)品結(jié)構,表現(xiàn)為一個接口或者抽象類,也就是說,工廠方法模式提供的所有產(chǎn)品都是衍生自同一個接口或抽象類,而抽象工廠模式所提供的產(chǎn)品則是衍生自不同的接口或抽象類。
總結(jié)
工廠具有下列優(yōu)點:松耦合,即對象的創(chuàng)建可以獨立于類的實現(xiàn);客戶端無需了解創(chuàng)建對象的類,但是照樣可以使用它來創(chuàng)建對象。它只需要知道需要傳遞的接口、方法和參數(shù),就能夠創(chuàng)建 所需類型的對象了。這簡化了客戶端的實現(xiàn);可以輕松地在工廠中添加其他類來創(chuàng)建其他類型的對象,而這無需更改客戶端代碼。最簡單的情況下,客戶端只需要傳遞一個參數(shù)就可以了;工廠還可以重用現(xiàn)有對象。但是,如果客戶端直接創(chuàng)建對象的化,總是創(chuàng)建一個新對象。