上几章都大致了解了在OOP设计中,应该针对抽象编程而不是具体实现编程。
但是上面几章的代码中或多或少都又"new"来创建对象的实例。那么在这些地方,就不是针对抽象编程,而成了具体实现的编程。
但使用"new" 有错吗?从本质上讲是没错的,因为这是OOP的基础。但是,从另一个角度去说,他是错误的。但错不在他。而在程序上面。
简单的说,就是我们使用了new关键词将代码的执行硬编码进了程序之中。他不能在程序运行时来决定运行哪一个。也就是说,当我们希望一个项目需要改变的时候,需要添加新的对象的时候,需要打开文件进去修改
第三节的装饰模式重点讲过一个原则:对修改关闭,对扩展打开。但是,频繁使用new这个创建实例的办法就是在破坏这个原则。因为你总是会要在不久的将来改变他的。只要你要添加新的对象。你必定要修改源文件。
那错误具体是由什么导致的?第一章我说过,OO编程的基本原理不是那些原则,而是Change!改变。一切都是因为改变捣的鬼。
看看下面这个例子:
我们需要开一个面包店。已经建立好了面包订购的类(Bread):
下面用另一个类的方法调用他。
function orderBread(){
$bread = new Bread();//如果说大家看了上面的设计模式,或者已经对设计模式有了一定的认识,这里肯定那个希望是建立一个抽象类或者接口。可是那样就没办法实例化了
//对面包进行各样的操作
$bread->prepare();
$bread->bake();
$bread->cut();
$bread->box();
}
这里本身没有任何错误。但是,如果我要更多的面包呢?只能由我们来决定面包的种类,然后叫这个方法来制造面包了
function orderBread($_bread){
if($_bread == "BlackBread"){
$this->break = new BlackBread();
}else if ($_break == "WhiteBread"){
$this->break = new WhiteBread();
}else if($_break == "SweetBread"){
$this->break = new SweetBread();
}else ($_break == "NutsBread"){
$this->break = new NutsBread();
}
//对面包进行各样的操作
$bread->prepare();
$bread->bake();
$bread->cut();
$bread->box();
}
手写一堆代码。。真麻烦
恩,这样就完成了。如果以后要改呢?要增加面包,或者减少面包种类呢?是不是必须得打开这个文件来修改他。删删改改的。
这样就找到问题结症了。问题不在new,而在这一堆的改变上面。他们随时会改变的。你就得随时的去改变这些文件。
那就把它拿出来。
if($_bread == "BlackBread"){
$this->break = new BlackBread();
}else if ($_break == "WhiteBread"){
$this->break = new WhiteBread();
}else if($_break == "SweetBread"){
$this->break = new SweetBread();
}else ($_break == "NutsBread"){
$this->break = new NutsBread();
}
这一块改变拿出来,放到另一个类里面。这样方法orderBread就不用关心是要定什么面包了。他只要知道调用它的时候需要制作一块面包。
这就是工厂!用工厂来建立对象!
先从工厂入手:
<?php
require("Bread.php");
class BreadFactory{
private $bread;
//通过这个creatBread方法创建我们的面包
public function creatBread($_bread){
if(is_string($_bread)){
//类型判断是必须的。php是弱类型语言。
if($_bread == "BlackBread"){
$this->bread = new BlackBread();
}elseif ($_bread == "WhiteBread"){
$this->bread = new WhiteBread();
}elseif($_bread == "SweetBread"){
$this->bread = new SweetBread();
}elseif($_bread == "NutsBread"){
$this->bread = new NutsBread();
}else{
echo "本工厂不提供这款面包";
}
//以上没有任何变化。将他从原来的面包点的代码中拿出来
}
return $this->bread;
}
}
?>
然后是面包种类
<?php
/**
* 这里存放很多很多种类的面包类。共用抽象类,Bread
*/
abstract class Bread {
abstract function prepare();
abstract function bake();
abstract function cut();
abstract function box();
}
class BlackBread extends Bread {
public function prepare(){
echo "准备好黑面包</br>";
}
public function bake(){
echo "烘烤黑面包</br>";
}
public function cut(){
echo "切片黑面包</br>";
}
public function box(){
echo "黑面包已经打包好了</br>";
}
}
class WhiteBread extends Bread{
public function prepare(){
echo "准备好白面包</br>";
}
public function bake(){
echo "烘烤白面包</br>";
}
public function cut(){
echo "切片白面包</br>";
}
public function box(){
echo "白面包已经打包好了</br>";
}
}
class SweetBread extends Bread{
public function prepare(){
echo "准备好甜面包</br>";
}
public function bake(){
echo "烘烤甜面包</br>";
}
public function cut(){
echo "切片甜面包</br>";
}
public function box(){
echo "甜面包已经打包好了</br>";
}
}
class NutsBread extends Bread{
public function prepare(){
echo "准备好果仁面包</br>";
}
public function bake(){
echo "烘烤果仁面包</br>";
}
public function cut(){
echo "切片果仁面包</br>";
}
public function box(){
echo "果仁面包已经打包好了</br>";
}
}
?>
面包和工厂准备好了。最后开店
/**
* 面包店类。开店咯
*/
require("BreadFactory.php");
class BreadStore{
private $bread;
//通过构造函数来创建面包.你开店必须得找到源头啊。所以得定义工厂是谁
public function __construct(BreadFactory $_bread){
$this->bread = $_bread;
}
public function orderBread($_type){
$orderBread = $this->bread->creatBread($_type);
$orderBread->prepare();
$orderBread->bake();
$orderBread->cut();
$orderBread->box();
}
}
最后测试一下
$a=new BreadFactory();
$b=new BreadStore($a);
//测试一下.我先订购一款黑面包
$b->orderBread("BlackBread");
//再来个白面包如何
$b->orderBread("WhiteBread");
结果是
准备好黑面包
烘烤黑面包
切片黑面包
黑面包已经打包好了
准备好白面包
烘烤白面包
切片白面包
白面包已经打包好了
是不是很简单,思路也非常清晰~
注意,我在Bread文件里面让所有的面包都继承了个抽象类,单是在工厂里却没使用这个抽象类。实际上,所有的变量前面你应该理解成Bread的类型。
这个其实是简单工厂。并不能叫做完全的模式。但是我个人在很多的教程里面都看到将这样的过程称为工厂模式。事实上,这只是个编程习惯,但经常使用。所以还是能将其归纳进工厂模式的。
下面来看看我的简单面包点类图是如何的
再次提醒!这里所说的实现接口并不是单指:写一个类,用implements关键字来实现一个接口。而是泛指实现某个超类型(可以是一个类也可以是具体接口)的某个方法
现在我们又有了新的改变。面包店生意太好了。有一些其他省市的面包店希望加盟我们。
好像问题不是很大,用上面的简单工厂方式。那就每个工厂一个工厂类。HNBreadFactory,GDBreadFactory。
这样并没多大问题。但是我们面包店生意好就是因为我们的一些质量有保证。如果我希望多点质量控制呢?都用同样的揉面,切面,装箱。流程都是一模一样的。如何操作?下面就用工厂方法来完成这个工作。看看他是如何工作的。
先将creatBread方法从工厂类里面移出来,并放入我们原先的面包店。但他只知道建立面包。具体建立什么面包他并不知道。需要完成具体面包的是在他的子类里,也就是说将他抽象话。不仅抽象整个Bread类,也要抽象这个creatBread方法。
下面是代码
<?php
/**
* 面包店类。开店咯
*/
abstract class BreadStore{
private $bread;
public function orderBread($_type){
//调用自身的抽象方法
$orderBread = $this->creatBread($_type);
$orderBread->prepare();
$orderBread->bake();
$orderBread->cut();
$orderBread->box();
}
abstract function creatBread($_type);
}
?>
这样做的好处是抽象的类BreadStore并不能决定建立什么样的面包。当然,我这里说的由子类建立面包,并不是他自身在运行时决定。而是在下面的子类来决定他应该如何提供面包。你从这个代码里面只能理解到。
$orderBread = $this->creatBread($_type);
订购面包。
订购什么样的面包,BreadStore并不知道这个面包是什么样的。可他是抽象的,不能实例化。所以是由他的子类来重写creatBread类来决定建立什么样的面包。
看看他的子类如何做的
<?php
require("Bread.php");
require("BreadStore.php");
//在湖南开店
class HNBreadStore extends BreadStore{
public function creatBread($_bread){
if(is_string($_bread)){
//类型判断是必须的。php是弱类型语言。
if($_bread == "BlackBread"){
return new HNBlackBread();
}elseif ($_bread == "WhiteBread"){
return new HNiteBread();
}elseif($_bread == "SweetBread"){
return new HNSweetBread();
}elseif($_bread == "NutsBread"){
return new HNNutsBread();
}
}
}
}
//在广东开店
class GDBreadStore extends BreadStore{
public function creatBread($_bread){
if(is_string($_bread)){
//类型判断是必须的。php是弱类型语言。
if($_bread == "BlackBread"){
return new GDBlackBread();
}elseif ($_bread == "WhiteBread"){
return new GDiteBread();
}elseif($_bread == "SweetBread"){
return new GDSweetBread();
}elseif($_bread == "NutsBread"){
return new GDNutsBread();
}else return null;
}
}
}
?>
子类里通过重写方法来决定如何建立面包。每一个加盟店都有自己的面包对象家族。他来决定采用哪样的面包。
下面是关键的地方。因为光又面包店,却没有面包,当然是不行的。
这里相对于上面的简单工厂模式有了点改变,因为为了更好的扩展面包的种类。所以面包抽象类进行了改变,直接由他来定义好了各种质量保证流程
<?php
/**
* 这里存放很多很多种类的面包类。共用抽象类,Bread
*/
abstract class Bread {
protected $name;
protected $douch;
protected $sauce;
protected $toppings=array();
public function prepare(){
echo "正在准备".$this->name."</br>";
echo "揉入".$this->douch."...<br>";
echo "加入调料".$this->sauce."...<br>";
echo "加入配料...";
foreach ($this->toppings as $value){
echo " ".$value." ";
}
echo "</br>";
}
function bake(){
echo "烘烤10分钟<br>";
}
function cut(){
echo "切成圆形<br>";
}
function box(){
echo "放入盒子<br>";
}
}
class HNBlackBread extends Bread {
public function __construct(){
$this->name = "湖南黑面包";
$this->douch = "酵母面团";
$this->sauce = "辣椒酱";
$this->toppings = array("鸡蛋");
}
}
class HNWhiteBread extends Bread{
public function __construct(){
$this->name = "湖南白面包";
$this->douch = "不发酵面团";
$this->sauce = "辣椒酱";
$this->toppings = array("鸡蛋");
}
}
class HNSweetBread extends Bread{
public function __construct(){
$this->name = "湖南甜面包";
$this->douch = "酵母面团";
$this->sauce = "辣椒酱";
$this->toppings = array("鸡蛋","糖精","果酱");
}
}
class HNNutsBread extends Bread {
public function __construct(){
$this->name = "湖南果仁面包";
$this->douch = "酵母面团";
$this->sauce = "辣椒酱";
$this->toppings = array("鸡蛋","开心果");
}
}
class GDBlackBread extends Bread {
public function __construct(){
$this->name = "广东黑面包";
$this->douch = "酵母面团";
$this->sauce = "糖精";
$this->toppings = array("鸡蛋");
}
}
class GDWhiteBread extends Bread{
public function __construct(){
$this->name = "广东白面包";
$this->douch = "不发酵面团";
$this->sauce = "糖精";
$this->toppings = array("鸡蛋");
}
}
class GDSweetBread extends Bread{
public function __construct(){
$this->name = "广东甜面包";
$this->douch = "酵母面团";
$this->sauce = "糖精";
$this->toppings = array("鸡蛋","糖精","果酱");
}
}
class GDNutsBread extends Bread {
public function __construct(){
$this->name = "广东果仁面包";
$this->douch = "酵母面团";
$this->sauce = "糖精";
$this->toppings = array("鸡蛋","开心果");
}
}
?>
这样整个工作就完成了。好吧,订购两个不同地区的面包来尝一下吧。
$b =new GDBreadStore();
$b->orderBread("BlackBread");
$b =new HNBreadStore();
$b->orderBread("BlackBread");
如果你将两个简单商店类的creaderBread设立成静态的那就更简单,大家可以自己测试一下。
下面是显示结果:
正在准备广东黑面包
揉入酵母面团...
加入调料糖精...
加入配料... 鸡蛋
烘烤10分钟
切成圆形
放入盒子
正在准备湖南黑面包
揉入酵母面团...
加入调料辣椒酱...
加入配料... 鸡蛋
烘烤10分钟
切成圆形
放入盒子
是不是很简单。再回头来看看测试代码
$b =new GDBreadStore();
$b->orderBread("BlackBread");
$b =new HNBreadStore();
$b->orderBread("BlackBread");
我建立两个店,然后订购。非常的复合我们自身在生活中的习惯。
ok。如果上面的都明白了。那你也明白了什么叫做工厂方法模式。
他和简单工厂很相似。所以很多人混为一谈!但他是通过子类来决定建立什么样的面包。而简单工厂是有其他的类来确定建立什么样的面包。
下面看看我们的面包加盟店的类图。可能这个关系更清楚一点:
解释都写在图上。就不啰嗦了=。=
定义我们的工厂方法模式:
定义一个创建对象的接口,但是具体对象有它的子类来决定。也就是工厂方法将对象实例化推迟到了子类
具体的工厂方法的类图
工厂方法让具体的对象解脱了出来。并不再依赖具体的类。而是抽象。
它正式是复合OOP设计中非常重要的一个原则:
依赖倒置原则(经常听到,却不明白到底什么意思)
定义:
依赖抽象编程。而不要依赖具体编程
看起来这个原则和上面的
针对接口编程,不针对实现编程
这个原则非常的相似。但是有本质区别的
他更注重的是依赖抽象。
就像上面的面包店。本身面包店BreadStore他不能决定生产什么样的面包,他只能依赖他的底层子类来决定。
面包本身(各种面包)也不能依赖自身去决定怎么样去切片怎么样去装箱。只能依赖他的抽象类Bread。
这个原则说明了:不要让高层组件依赖于底层组件。而是不管高还是底,都应该依赖抽象对象。
换句话说。如果这个例子里所有的面包对象都是依赖面包这个具体类而建立的,就是违反了这个原则,就导致了解耦的失败。
依赖倒置原则到底倒置了哪里呢?
先想想看,你要开一个面包店会怎么考虑?
A:为了能够烘烤,切片,装到盒子里给客户,我必须先准备好不同种类的面包。黑的白的,甜的或者果仁的。甚至更多。
这样是没错的,但是这样就依赖各种面包的具体类。也就是这是从高层开始依赖。而违反了这个原则。如果我倒过来呢?
A:先提供一个抽象接口,他告诉我可以提供这些面包,而我不用担心会如何制作。
很好。这样就不用理会那些具体的面包类了。剩下的就是如何去开面包店了。思维~~~倒过来了。从底层开始往上面思考。并且依赖抽象。而不是具体的类了。
下面几个原则来告诉你如何做到尽量不要违反这个原则:切记!是尽量不要违反。而不是任何地方都不违反。这和OO设计原则一个道理的!不可能完全遵循OO设 计原则而增加自己的工作两。设计原则和设计模式只是为了解决问题而存在,而不是产生问题。如果你连建立一个字符对象都用到此原则说明你有问题了。你应该思 考一下这个对象将来是否会发生什么改变,如果不会发生改变,不遵循任何原则也没什么影响。
变量不可以持有具体的引用!
(用new关键词就是在次优具体的引用了。尽量用工厂模式中的一种避免它)
不要让类派生自具体类
(如果派生自具体类,你就会依赖具体类。请派生自一个抽象)
不要覆盖基类中已经实现的方法
(如果覆盖基类已经实现的方法,那么你的基类就不是一个真正适合被继承的抽象。基类中已实现的方法,应该由所有的子类共享)
你不可能完全遵守这些方针。你应该尽量达到这个原则。而不是随时都遵守这个原则。自己慢慢体会吧=。=
让我们进入真正的抽象工厂模式吧。
现在我们需要进行原料控制,因为面包加盟店有些可能会偷工减料。所以我们给其指定原料工厂提供原料。
先准备一下原料工厂
interface MaterialFactory{
public function creatDouch();
public function creatSauce();
public function creatToppings();
}
/**
* 各个地区的原料工厂
*/
class HNMaterialFactory implements MaterialFactory{
public function creatDouch(){
return new FermentDouch();
}
public function creatSauce(){
return new ChiliSauce();
}
public function creatToppings(){
return new setToppings(new Egg(),new NewChiliSauce(),new Jam());
}
}
class GDMaterialFactory implements MaterialFactory{
public function creatDouch(){
return new NotFermentDouch();
}
public function creatSauce(){
return new Saccharin();
}
public function creatToppings(){
return new setToppings(new Egg(),new NewSaccharin(),new Jam());//作为示例,将这里写死也没什么影响
}
}
上面的原料工厂里也看到了。每个原料都来至不同的原料对象。下面是对原料进行简单的定义
<?php
/**
* 存储各种各样的原材料,当然,每种原料都是依赖的接口
*/
/**
* 各种原材料的接口
*
*/
interface Douch{
public function getDouch();
}
interface Sauce{
public function getSauce();
}
interface Toppings{
public function getToppings();
}
//面粉团集群
class FermentDouch implements Douch {
protected $douch;
public function __construct(){
$this->douch = "发酵面包";
}
public function getDouch(){
return $this->douch;
}
}
class NotFermentDouch implements Douch {
protected $douch;
public function __construct(){
$this->douch = "不发酵面包";
}
public function getDouch(){
return $this->douch;
}
}
//酱汁集群
class ChiliSauce implements Sauce {
protected $sauce;
public function __construct(){
$this->sauce = "辣椒酱";
}
public function getSauce(){
return $this->sauce;
}
}
class Saccharin implements Sauce{
protected $sauce;
public function __construct(){
$this->sauce = "果酱";
}
public function getSauce(){
return $this->sauce;
}
}
//作料集群
class setToppings implements Toppings {
protected $toppings;
public function __construct(Toppings $_egg,Toppings $_sauce,Toppings $_jam){
$this->toppings=array($_egg->getToppings(),$_sauce->getToppings(),$_jam->getToppings());
}
public function getToppings(){
return implode(",",$this->toppings);
}
}
class Egg implements Toppings {
protected $name;
public function __construct(){
$this->name = "鸡蛋";
}
public function getToppings(){
return $this->name;
}
}
class NewChiliSauce implements Toppings {
protected $name;
public function __construct(){
$this->name = "苏丹红辣椒酱";//辣飞你
}
public function getToppings(){
return $this->name;
}
}
class Jam implements Toppings {
protected $name;
public function __construct(){
$this->name = "高级果酱";
}
public function getToppings(){
return $this->name;
}
}
class NewSaccharin implements Toppings {
protected $name;
public function __construct(){
$this->name = "加量糖精";
}
public function getToppings(){
return $this->name;
}
}
?>
原料准备完毕。那如何将这个原料提供给具体的客户呢?
//在湖南开店
class HNBreadStore extends BreadStore{
public function creatBread($_bread){
$material = new HNMaterialFactory();
if(is_string($_bread)){
//类型判断是必须的。php是弱类型语言。
if($_bread == "BlackBread"){
$bread = new BlackBread($material);
$bread->setName("湖南口味的黑面包");
}elseif ($_bread == "WhiteBread"){
$bread = new WiteBread($material);
$bread->setName("湖南口味的白面包");
}elseif($_bread == "SweetBread"){
$bread = new SweetBread($material);
$bread->setName("湖南口味的甜面包");
}elseif($_bread == "NutsBread"){
$bread = new NutsBread($material);
$bread->setName("湖南口味的果仁面包");
}else return null;
}
return $bread;
}
}
//在广东开店
class GDBreadStore extends BreadStore{
public function creatBread($_bread){
$material = new GDMaterialFactory();
if(is_string($_bread)){
//类型判断是必须的。php是弱类型语言。
if($_bread == "BlackBread"){
$bread = new BlackBread($material);
$bread->setName("广东口味的黑面包");
}elseif ($_bread == "WhiteBread"){
$bread = new WiteBread($material);
$bread->setName("广东口味的白面包");
}elseif($_bread == "SweetBread"){
$bread = new SweetBread($material);
$bread->setName("广东口味的甜面包");
}elseif($_bread == "NutsBread"){
$bread = new NutsBread($material);
$bread->setName("广东口味的果仁面包");
}else return null;
}
return $bread;
}
}
重点在
$material = new HNMaterialFactory();
$bread = new BlackBread($material);
这里重写了新建不同面包的对象方法。
$material就是原料工厂。告诉$bread从这个原料工厂里提取原料。这样保证完成正确的面包种类。
面包的抽象类并没有发生任何改变:
/**
* 面包店类。开店咯
*/
abstract class BreadStore{
private $bread;
public function orderBread($_type){
//调用自身的抽象方法
$orderBread = $this->creatBread($_type);
$orderBread->prepare();
$orderBread->toString();
$orderBread->bake();
$orderBread->cut();
$orderBread->box();
}
abstract function creatBread($_type);
}
决定生产什么样的面包还是由各加盟店的子类来决定的。
下面是各种面包的定义
class BlackBread extends Bread {
protected $factory;
public function __construct(MaterialFactory $_factory){
$this->factory = $_factory;
}
public function prepare(){
$this->name = "正在准备".$this->name;
$this->douch = $this->factory->creatDouch();
$this->sauce = $this->factory->creatSauce();
$this->toppings = $this->factory->creatToppings();
}
}
class WhiteBread extends Bread{
public function __construct(MaterialFactory $_factory){
$this->factory = $_factory;
}
public function prepare(){
$this->name = "正在准备".$this->name;
$this->douch = $this->factory->creatDouch();
$this->sauce = $this->factory->creatSauce();
$this->toppings = $this->factory->creatToppings();
}
}
class SweetBread extends Bread{
public function __construct(MaterialFactory $_factory){
$this->factory = $_factory;
}
public function prepare(){
$this->name = "正在准备".$this->name;
$this->douch = $this->factory->creatDouch();
$this->sauce = $this->factory->creatSauce();
$this->toppings = $this->factory->creatToppings();
}
}
class NutsBread extends Bread {
public function __construct(MaterialFactory $_factory){
$this->factory = $_factory;
}
public function prepare(){
$this->name = "正在准备".$this->name;
$this->douch = $this->factory->creatDouch();
$this->sauce = $this->factory->creatSauce();
$this->toppings = $this->factory->creatToppings();
}
}
这里和上面的工厂方法是有区别的。大家可以自己翻上去查看。
重点改变是在新建这些面包种类的实例对象的时候在构造函数中引进了原料工厂。
真正起到作用的就是这里。
public function __construct(MaterialFactory $_factory){
$this->factory = $_factory;
}
准备工作中只是在准备,并不知道他准备的是什么口味的。所以弱耦合还是存在,并没破坏的。
由什么源材料决定什么样的面包。这是很自然的道理。
现在一切都有,只欠东风了。
$b =new GDBreadStore();
$b->orderBread("BlackBread");
$b =new HNBreadStore();
$b->orderBread("BlackBread");
输出其实和上面的一样。只是工作模式不同
正在准备广东口味的黑面包
不发酵面包
果酱
鸡蛋,加量糖精,高级果酱
烘烤10分钟
切成圆形
放入盒子
正在准备湖南口味的黑面包
发酵面包
辣椒酱
鸡蛋,苏丹红辣椒酱,高级果酱
烘烤10分钟
切成圆形
放入盒子
上面的工厂方法和简单工厂理解了的这个正式的工厂模式其实就非常容易理解了。。只是方式不同而已。可能细心的朋友已经发现了。工厂方法是蕴藏在抽象工厂模式中。
通过抽象工厂所提供的接口,可以创建产品的家族。利用这个接口书写代码,我们的代码将从实际工厂解耦,以便在不同上下文中实现各种各样的工厂,制造出各种不同的产品。例如,不同的区域,不同的操作系统,不同的外观以及操作。
因为代码从实际的产品中解耦了,所以我们可以替换不同的工厂来取得不同的行为(例如,取得番茄酱料,而不是取得大蒜酱料);
好了。让我们来定义抽象工厂模式吧:
提供一个接口,用于创建相关或者依赖对象的家族,而不需要明确指定具体类。
抽象工厂允许客户使用抽象的接口来创建一组相关的产品。而不需要知道(或关心)实际产出的具体产品是什么。这样一来,客户就从具体的产品中被解耦。
类图。。。。。如下。。
画得比较乱。。。呃。。其实我已经理解了。只是不知道怎么用我这个软件话出来。。不能画折线。囧
至于为什么抽象工厂的方法经常以工厂方法的方式实现。。截一段深入潜出设计模式中的说明吧。
抽象工厂的任务是定义一个负责创建一组产品的接口。这个接口内的每个方法都负责创建一个具体的产品,同时,我们利用实现抽象工厂的子类来提供这些具体的做法。所以,在抽象工厂中利用工厂方法实现生产方法是相当自然的做法。
一下来区别工厂方法和抽象工厂之间:
工厂方法是使用类,而抽象工厂是操作的对象。
工厂方法创建对象的方法是继承,抽象工厂创建工厂的方法是组合。
抽象工厂是通过抽象方法(MaterialFactory),由这个类型的子类定义产品被产品的方法。但必须先实例化。然后将他传入一些针对抽象类型所写的代码中
工厂方法是直接通过抽象方法的子类决定如何创建产品。不需要在外部再实例化产品传入具体代码。
不管是使用工厂方法还是抽象工厂,都可以将对象创建封装起来,使应用程序解耦,并降低对特定实现的依赖。
抽象工厂是在需要创建产品家族和想让制造的相关产品集合其来的时候使用。
工厂方法在把客户代码需要实例化的具体类中解耦或者如果目前不知道将来需要实例化那些具体类的时候使用。
相对而言进过个人经验来判断是使用哪一个工厂模式。工厂方法相当于轻量级的设计模式。简单处理就可以使用他,只需要把抽象方法继承成子类并实现这个工厂方法就可以了。
抽象工厂的范围更广泛一点。