2、2 工厂模式
定义
简单工厂
我们需要一个对象,会有一个工厂帮我们创建这个对象。
所有在用简单工厂的地方,都可以考虑用反射技术来取出 switch 或 if,减少分支判断带来的耦合。
简单工厂模式的最大优点在于工厂类中包含了必要的逻辑判断,根据客户端的选择条件动态实例化相关的类,对于客户端来说,去除了与具体产品的依赖。
该模式是工厂方法模式的弱化,因为简单,所以称为简单工厂模式,也叫做静态工厂模式。缺点是工厂类的扩展(不能通过继承来改变创建方法的行为)比较困难,不符合开闭原则,但它仍然是一个经常使用的设计模式。
工厂方法
定义一个用于创建对象的接口,让子类决定实例化哪一个类(作为一个生产的管理者,只要知道生产什么就可以了,而不需要事物的具体信息)。工厂方法使一个类的实例化延迟到其子类。
通用类图
Product:定义工厂方法所创建的对象的接口;
Creator:声明工厂方法,该方法返回一个 Product 类型的对象;
ConcreteProduct:具体的产品,实现了 Product 接口;
ConcreteCreator:重定义工厂方法返回一个 ConcreteProduct 实例。
使用场景
1.工厂方法模式一个 new 一个对象的替代品,所以在所有需要生产对象的地方都可以使用,但是需要慎重地考虑是否要增加一个工厂类进行管理,增加代码的复杂度。
2.需要灵活地、可扩展的框架时。(比如三种网络协议:POP3、IMAP、HTTP)
3.工厂方法可以用在异构项目中。(比如从 WEBSERVICES 的 WSDL 中产生的对象都认为是一个产品,然后由一个具体的工厂类进行管理,减少与外围系统的耦合。)
4.可以使用再测试驱动开发的框架下。(比如测试一个类 A,可以把与类 A 有关联关系的类 B 也同时产生/虚拟出来,避免类 A 与类 B 的耦合。现在弱化了,可用 JMock 和 EasyMock 代替。)
案例
案例 1(雷锋办好事)
初始设计
图形结构
代码示例
/**
* 现在只有一个大学生去帮敬老院去干活
*/
public class Gongchang1 {
public static void main(String[] args) {
LeiFeng leiFeng = new Undergraduate();
leiFeng.sweep();
leiFeng.wash();
leiFeng.buy();
}
}
class LeiFeng{
public void sweep(){
System.out.println("扫地");
}
public void wash(){
System.out.println("洗衣服");
}
public void buy(){
System.out.println("买东西");
}
}
/**
* 学雷锋的大学生
*/
class Undergraduate extends LeiFeng{
}
简单工厂
图形结构
代码示例
/**
* 现在不只有一个大学生去帮敬老院去干活,如果有多个大学生,或者有社区志愿者的话,那么可以编写简单工厂。
*/
public class Gongchang2 {
public static void main(String[] args) {
//在这里会发现重复了好多代码,出现了"坏味道",那么就可以通过Gongchang3工厂模式来改善这种问题。
LeiFeng undergraduate1 = SimpleFactory.createLeiFeng("undergraduate");
undergraduate1.sweep();
undergraduate1.wash();
undergraduate1.buy();
System.out.println("------------------------------");
LeiFeng undergraduate2 = SimpleFactory.createLeiFeng("undergraduate");
LeiFeng undergraduate3 = SimpleFactory.createLeiFeng("undergraduate");
}
}
class LeiFeng{
public void sweep(){
System.out.println("扫地");
}
public void wash(){
System.out.println("洗衣服");
}
public void buy(){
System.out.println("买东西");
}
}
/**
* 学雷锋的大学生
*/
class Undergraduate extends LeiFeng{
}
/**
* 社区志愿者
*/
class Volunteer extends LeiFeng{
}
/**
* 简单雷锋工厂
*/
class SimpleFactory{
public static LeiFeng createLeiFeng(String type){
LeiFeng leiFeng = null;
switch (type){
case "undergraduate":
leiFeng = new Undergraduate();
break;
case "volunteer":
leiFeng = new Volunteer();
break;
}
return leiFeng;
}
}
工厂方法
图形结构
代码示例
/**
* 雷锋工厂方法
* 这里如果把大学生换成社区志愿者,虽然也要修改代码,但是只需要修改一处(针对于Gongchang2中的switch和main而言)就可以了。
* 所以Gongchang2和Gongchang3就能按场景来使用。
* 那么可以发现工厂方法克服了简单工厂违背"开放-封闭原则"的缺点,又保持了封装对象"创建过程"的优点。
* 但是简单工厂和工厂方法都是集中封装了对象的创建,使得要更换对象时,不需要做大的改动就可以实现,降低了客户程序与产品对象的耦合。
* 工厂方法模式是简单工厂模式的进一步抽象和推广。
* 由于使用了多态性,工厂方法模式保持了简单工厂模式的优点,而且克服了它的缺点。
* 但缺点是由于每增加一个产品,就需要加一个产品工厂的类,增加了额外的开发量。
* 可是这并不是最佳的做法,这里并没有避免修改客服端的代码,在以后的章节中会通过"反射"机制来解决避免分支判断的问题。
*/
public class Gongchang3 {
public static void main(String[] args) {
IFactory undergraduateFactory = new UndergraduateFactory();
undergraduateFactory.getLeifeng().sweep();
undergraduateFactory.getLeifeng().wash();
undergraduateFactory.getLeifeng().buy();
System.out.println("------------------------------");
IFactory volunterFactory = new VolunteerFactory();
}
}
interface IFactory{
LeiFeng getLeifeng();
}
class UndergraduateFactory implements IFactory{
@Override
public LeiFeng getLeifeng() {
return new Undergraduate();
}
}
class VolunteerFactory implements IFactory{
@Override
public LeiFeng getLeifeng() {
return new Volunteer();
}
}
interface LeiFeng{
void sweep();
void wash();
void buy();
}
/**
* 学雷锋的大学生
*/
class Undergraduate implements LeiFeng{
public void sweep(){
System.out.println("扫地");
}
public void wash(){
System.out.println("洗衣服");
}
public void buy(){
System.out.println("买东西");
}
}
/**
* 社区志愿者
*/
class Volunteer implements LeiFeng{
public void sweep(){
System.out.println("扫地");
}
public void wash(){
System.out.println("洗衣服");
}
public void buy(){
System.out.println("买东西");
}
}
案例 2(计算器)
简单工厂
图形结构
代码示例
public class Gongchang4 {
public static void main(String[] args) {
Operation operation = OperationFactory.createOperate("+");;
operation.getResult(1,2);
}
}
class OperationFactory{
public static Operation createOperate(String type){
Operation operation = null;
switch (type){
case "+":
operation = new OperationAdd();
break;
case "-":
operation = new OperationSub();
break;
}
return operation;
}
}
interface Operation{
void getResult(double num1,double num2);
}
class OperationAdd implements Operation{
@Override
public void getResult(double num1, double num2) {
System.out.println("--add--" + (num1 + num2));
}
}
class OperationSub implements Operation{
@Override
public void getResult(double num1, double num2) {
System.out.println("--sub--" + (num1 - num2));
}
}
工厂方法
图形结构
代码示例
public class Gongchang5 {
public static void main(String[] args) {
OperationFactory operationFactory = new OperationAddFactory();
operationFactory.createOperate().getResult(2,1);
}
}
interface OperationFactory{
Operation createOperate();
}
class OperationAddFactory implements OperationFactory{
@Override
public Operation createOperate() {
return new OperationAdd();
}
}
class OperationSubFactory implements OperationFactory{
@Override
public Operation createOperate() {
return new OperationSub();
}
}
interface Operation{
void getResult(double num1,double num2);
}
class OperationAdd implements Operation{
@Override
public void getResult(double num1, double num2) {
System.out.println("--add--" + (num1 + num2));
}
}
class OperationSub implements Operation{
@Override
public void getResult(double num1, double num2) {
System.out.println("--sub--" + (num1 - num2));
}
}
案例3(创建单例)
图形结构
代码示例
import java.lang.reflect.Constructor;
/**
* 单例模式的核心要求就是在内存中只有一个对象,通过工厂方法模式也可以只在内存中生产一个对象。
* 下述通过工厂方法模式创建了一个单例对象,该框架可以继续扩展,在一个项目中可以产生一个单例构造器,
* 所有需要产生单例的类都遵循一定的规则(构造方法是private),然后通过扩展该框架,只要输入一个类型就可以获得唯一的一个实例。
*/
public class Gongchang6 {
}
/**
* Singleton保证不能通过正常的渠道建立一个对象
*/
class Singleton{
private Singleton(){}
public void doSomething(){
}
}
/**
* SingletonFactory通过反射创建Singleton的单例对象
* 在此案例中,通过获得类构造器,然后设置访问权限,生成一个对象,然后提供外部访问,保证内存中的对象唯一。
* 当然,其他类也可以通过反射的方式建立一个单例对象。
*/
class SingletonFactory{
private static Singleton singleton;
static{
Class cl = null;
try {
cl = Class.forName(Singleton.class.getName());
//获得无参构造
Constructor constructor = cl.getDeclaredConstructor();
//设置无参构造是可访问的
constructor.setAccessible(true);
//产生一个实例对象
singleton = (Singleton)constructor.newInstance();
} catch (Exception e) {
e.printStackTrace();
}
}
public static Singleton getSingleton(){
return singleton;
}
}
案例 4(延迟初始化/参数化工厂方法)
定义
一个对象被消费完毕后,并不立刻释放,工厂类保持其初始状态,等待再次被使用。延迟初始化是工厂方法模式的一个扩展应用。
图形结构
代码示例
import java.util.HashMap;
import java.util.Map;
/**
* 延迟加载框架时可以扩展的,例如限制某一个产品类的最大实例化数量,
* 可以通过判断Map中已有的对象来实现,这样的处理是非常有意义的,
* 例如JDBC数据库,都会要求设置一个MaxConnection最大连接数量,该数量就是内存中最大实例化的数量。
* 延迟加载还可以用在对象初始化比较复杂的情况下,例如硬件访问,
* 涉及多方面的交互,则可以通过延迟加载降低对象的产生和销毁带来的复杂性。
*/
public class Gongchang7 {
}
class ProductFactory{
private static final Map<String,Product> prMap = new HashMap<String,Product>();
public static synchronized Product createProduct(String type){
Product product = null;
if(prMap.containsKey(type)){
product = prMap.get(type);
}else{
if(type.equals("Product1")){
product = new ConcreteProduct1();
}else{
product = new ConcreteProduct2();
}
}
return product;
}
}
abstract class Product{
abstract void doSomething();
}
class ConcreteProduct1 extends Product{
@Override
public void doSomething() {
}
}
class ConcreteProduct2 extends Product{
@Override
public void doSomething() {
}
}
优点
1.良好的封装性(能够封装具体类型的实例化),代码结构清晰。一个对象创建是有条件约束的,如一个调用者需要一个具体的产品对象,只要知道这个产品的类名(或约束字符串)就可以了,不用知道创建对象的艰辛过程,降低模块间的耦合。
2.工厂方法的扩展性非常优秀。在增加产品类的情况下,只要适当地修改具体的工厂类或扩展一个工厂类,就可以完成“拥抱变化”。
3.屏蔽产品类。这一特点非常重要,产品类的实现如何变化,调用者都不需要关心,它只需要关系产品的接口,只要接口保持不变,系统中的上层模块就不要发生变化。因为产品类的实例化工厂是由工厂类负责的,一个产品对象具体由哪一个产品生成是由工厂类决定的。
4.工厂方法模式克服了简单工厂模式违背的“开放-封闭原则”的缺点,又保持了封装对象创建的过程的优点。
缺点
工厂方法模式实现时,客户端需要决定实例化哪一个工厂来实现运算类,选择判断的问题还是存在的,也就是说,工厂方法把简单工厂的内部逻辑判断移动到了客户端代码来进行。如果要加功能,本来是改工厂类的,而现在是修改客户端。
工厂方法模式还给扩展性和可维护性带来了一定的影响。如果要扩展一个产品类,就需要建立一个相应的工厂类,遮掩更久增加了扩展的难度。因为工厂类和产品类的数量相同,维护时需要考虑两个对象之间的关系。
解决方法是:在复杂的应用中一般采用多工厂的方法,然后再增加一个协调类,避免调用者与各个子工厂交流,协调类的作用是封装子工厂类,对高层模块提供统一的访问接口。
标题:设计模式(二)创建型模式(对象创建型模式)简单工厂与工厂方法-FACTORY METHOD
作者:yazong
地址:https://blog.llyweb.com/articles/2020/04/16/1587051290502.html