jtahstu的博客

root@jtahstu.com   Github   英文博客  

最新碎语:以后没事写写小的知识点吧

您的位置:jtahstu的博客 >笔记> 设计模式从入门到放弃 - 工厂方法模式

设计模式从入门到放弃 - 工厂方法模式

模式定义

    在工厂方法模式中工厂父类负责定义创建产品对象的公共接口,而工厂子类则负责生成具体的产品对象,这样做的目的是将产品类的实例化操作延迟到工厂子类中完成,即通过工厂子类来确定究竟应该实例化哪一个具体产品类。

模式结构

    抽象工厂(Creator)角色:是工厂方法模式的核心,与应用程序无关。任何在模式中创建的对象的工厂类必须实现这个接口。

    具体工厂(Concrete Creator)角色:这是实现抽象工厂接口的具体工厂类,包含与应用程序密切相关的逻辑,并且受到应用程序调用以创建产品对象。在上图中有两个这样的角色:BulbCreator与TubeCreator。

    抽象产品(Product)角色:工厂方法模式所创建的对象的超类型,也就是产品对象的共同父类或共同拥有的接口。在上图中,这个角色是Light。

    具体产品(Concrete Product)角色:这个角色实现了抽象产品角色所定义的接口。某具体产品有专门的具体工厂创建,它们之间往往一一对应。            


UML图

java演示代码

//TV.java
public interface TV {
	public void play();
}

//HaierTV.java
public class HaierTV implements TV {
	public void play() {
		System.out.println("海尔电视机播放中......");
	}
}

//HisenseTV.java
public class HisenseTV implements TV {
	public void play() {
		System.out.println("海信电视机播放中......");
	}
}

//TVFactory.java
public interface TVFactory {
	public TV produceTV();
}

//HaierTVFactory.java
public class HaierTVFactory implements TVFactory {
	public TV produceTV() {
		System.out.println("海尔电视机工厂生产海尔电视机。");
		return new HaierTV();
	}
}

//HisenseTVFactory.java
public class HisenseTVFactory implements TVFactory {
	public TV produceTV() {
		System.out.println("海信电视机工厂生产海信电视机。");
		return new HisenseTV();
	}
}

//config.xml
<?xml version="1.0"?>
<config>
	<className>HaierTVFactory</className>
</config>


//Client.java
public class Client {
	public static void main(String args[]) {
		try {
			TV tv;
			TVFactory factory;
			factory = (TVFactory) XMLUtil.getBean();
			tv = factory.produceTV();
			tv.play();
		} catch (Exception e) {
			System.out.println(e.getMessage());
		}
	}
} 

php代码演示

<?php
/**
 * 工厂方法模式,定义一个用于创建对象的接口,让子类决定将哪一个类实例化。工厂方法模式让一个类的实例化延迟到其子类。
 *
 * 包含的对象
 * 1,抽象产品
 * 2,具体产品
 * 3,抽象工厂
 * 4,具体工厂
 *
 * 将简单工厂模式的工厂类进行抽象化,避免扩展时需要修改简单工厂模式中的工厂类。方便扩展、维护。
 **/

//运算类
abstract class Operation {
	abstract public function getValue($num1, $num2);
}

// 加法类
class OperationPlus extends Operation {
	public function getValue($num1, $num2) {
		return $num1 + $num2;
	}

}

//减法类
class OperationSub extends Operation {
	public function getValue($num1, $num2) {
		return $num1 - $num2;
	}

}

//乘法类
class OperationMul extends Operation {
	public function getValue($num1, $num2) {
		return $num1 * $num2;
	}

}

//除法类
class OperationDiv extends Operation {
	public function getValue($num1, $num2) {
		try {
			if ($num2 == 0) {
				throw new Exception('除数不能为0!');
			} else {
				return $num1 / $num2;
			}
		} catch (Exception $e) {
			echo $e -> getMessage();
		}
	}

}

//抽象工厂类
abstract class Factory {
	public abstract function getOperation();
}

//加法工厂
class FactoryPlus extends Factory {
	public function getOperation() {
		return new OperationPlus();
	}

}

//加法工厂
class FactorySub extends Factory {
	public function getOperation() {
		return new OperationSub();
	}

}

//加法工厂
class FactoryMul extends Factory {
	public function getOperation() {
		return new OperationMul();
	}

}

//加法工厂
class FactoryDiv extends Factory {
	public function getOperation() {
		return new OperationDiv();
	}

}

// 加法
$factory = new FactoryPlus();
$operation = $factory -> getOperation();
echo $operation -> getValue(10, 11);

// 减法
$factory = new FactorySub();
$operation = $factory -> getOperation();
echo $operation -> getValue(10, 11);

// 除法
$factory = new FactoryDiv();
$operation = $factory -> getOperation();
echo $operation -> getValue(10, 2);
?> 


工厂方法模式的优点

    首先,良好的封装性,代码结构清晰。一个对象创建是有条件约束的,如一个调用者需要一个具体的产品对象,只要知道这个产品的类名(或约束字符串)就可以了,不用知道创建对象的艰辛过程,减少模块间的耦合。       

    其次,工厂方法模式的扩展性非常优秀在增加产品类的情况下,只要适当地修改具体的工厂类或扩展一个工厂类,就可以完成“拥抱变化”。例如在我们的例子 中,需要增加一个棕色人种,则只需要增加一个BrownHuman类,工厂类不用任何修改就可完成系统扩展。       

    再次,屏蔽产品类。这一特点非常重要,产品类的实现如何变化,调用者都不需要关心,它只需要关心产品的接口,只要接口保持不表,系统中的上层模块就不要发 生变化,因为产品类的实例化工作是由工厂类负责,一个产品对象具体由哪一个产品生成是由工厂类决定的。在数据库开发中,大家应该能够深刻体会到工厂方法模 式的好处:如果使用JDBC连接数据库,数据库从MySql切换到Oracle,需要改动地方就是切换一下驱动名称(前提条件是SQL语句是标准语句), 其他的都不需要修改,这是工厂方法模式灵活性的一个直接案例。

    最后,工厂方法模式是典型的解耦框架。高层模块值需要知道产品的抽象类,其他的实现类都不用关心,符合迪米特原则,我不需要的就不要去交流;也符合依赖倒转原则,只依赖产品类的抽象;当然也符合里氏替换原则,使用产品子类替换产品父类,没问题!

工厂方法模式的缺点

    增加新产品的同时需要增加新的工厂,导致类的个数成对增加,在一定程度上增加了系统的复杂性。

    由于考虑到系统的可扩展性,需要引入抽象层,在客户端代码中均使用抽象层进行定义,增加了系统的抽象性和理解难度。

工厂方法模式适用环境

1.0版本

       不管是简单工厂模式,工厂方法模式还是抽象工厂模式,他们具有类似的特性,所以他们的适用场景也是类似的。

       首先,作为一种创建类模式,在任何需要生成复杂对象的地方,都可以使用工厂方法模式。有一点需要注意的地方就是复杂对象适合使用工厂模式,而简单对象,特别是只需要通过new就可以完成创建的对象,无需使用工厂模式。如果使用工厂模式,就需要引入一个工厂类,会增加系统的复杂度。

       其次,工厂模式是一种典型的解耦模式,迪米特法则在工厂模式中表现的尤为明显。假如调用者自己组装产品需要增加依赖关系时,可以考虑使用工厂模式。将会大大降低对象之间的耦合度。

       再次,由于工厂模式是依靠抽象架构,它把实例化产品的任务交由实现类完成,扩展性比较好。也就是说,当需要系统有比较好的扩展性时,可以考虑工厂模式,不同的产品用不同的实现工厂来组装。

2.0版本

      首先,工厂方法模式是new一个对象的替代品,所以在所有需要生成对象的地方都可以使用,但是需要慎重地考虑是否要增加一个工厂类进行管理,增加代码的复杂度。

      其次,需要灵活的、可扩展的框架时,可以考虑采用工厂方法模式。万物皆对象,那万物也就皆产品类,例如需要设计一个连接邮件服务器的框架,有三种网络协议 可供选择:POP3、IMAP、HTTP,我们就可以把这三种连接方法作为产品类,定义一个接口如IConnectMail,然后定义对邮件的操作方法, 三个具体的产品类(也就是连接方式)进行不同的实现,再定义一个工厂方法,按照不同的传入条件,选择不同的连接方式。如此设计,可以做到完美的扩展,如某 些邮件服务器提供了WebService接口,很好,我们只要增加一个产品类就可以了。

      再次,工厂方法模式可以用在异构项目中,例如通过WebService与一个非Java的项目交互,虽然WebService号称是可以做到异构系统的同 构化,但是在实际的开发中,还是会碰到很多问题,如类型问题、WSDL文件的支持问题,等等,从WSDL中产生的对象都认为是一个产品,然后由一个具体的 工厂类进行管理,减少与外围系统的耦合。

      最后,可以使用在测试驱动开发的框架下,例如,测试一个类A,就需要把与类A有关联关系的类B也同时产生出来,我们可以使用工厂方法模式把类B虚拟出来, 避免类A与类B的耦合。目前由于JMock和EasyMock的诞生,该使用场景已经弱化了,读者可以在遇到此种情况时直接考虑使用JMock或 EasyMock。

参考

  1. 百度百科 http://baike.baidu.com/view/1580263.htm

  2. csdn http://blog.csdn.net/zhengzhb/article/details/7348707

  3. 博客园 http://www.cnblogs.com/cbf4life/archive/2009/12/20/1628494.html

  4. 课本


---

本文章采用 知识共享署名2.5中国大陆许可协议 进行许可,欢迎转载,演绎或用于商业目的。

---

二维码加载中...

扫一扫移动端访问O(∩_∩)O

发表评论

46 + 18 =
路人甲 表情
看不清楚?点图切换 Ctrl+Enter快速提交
正在加载中……