设计模式——工厂方法模式

工厂方法模式

工厂方法模式:定义一个用于创建对象的接口,让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到其子类。

  • 抽象产品类Product负责定义产品的共性,实现对事物最抽象的定义;
  • Creator为抽象创建类,也就是抽象工厂,具体如何创建产品类是由具体的实现工厂ConcreteCreator完成的。

抽象产品类:Product

public abstract class Product 
{ 
    //产品类的公共方法
    public void method1()
    {
        //业务逻辑处理
    }
    //抽象方法
    public abstract void method2();
}

具体产品类:ConcreteProduct

public class ConcreteProduct1 extends Product { 
    public void method2() { 
        //业务逻辑处理 
    } 
}

public class ConcreteProduct2 extends Product { 
    public void method2() { 
        //业务逻辑处理 
    } 
} 

抽象工厂类:Creator

public abstract class Creator { 
    /*
    * 创建一个产品对象,其输入参数类型可以自行设置 
    * 通常为String、Enum、Class等,当然也可以为空 
    */ 
    public abstract <T extends Product> T createProduct(Class<T> c); 
}

具体工厂类:ConcreteCreator

public class ConcreteCreator extends Creator { 
    public <T extends Product> T createProduct(Class<T> c){ 
        Product product=null; 
        try { 
            product = (Product)Class.forName(c.getName()).newInstance(); 
        } catch (Exception e) { 
            //异常处理 
        }
        return (T)product; 
    } 
}

场景类:Client

public class Client { 
    public static void main(String[] args) { 
        Creator creator = new ConcreteCreator(); 
        Product product = creator.createProduct(ConcreteProduct1.class); 
        /*
        * 继续业务处理 
        */ 
    } 
} 

工厂方法模式的优点:

  • 良好的封装性,代码结构清晰。一个对象创建是有条件约束的,如一个调用者需要一个具体的产品对象,只要知道这个产品的类名(或约束字符串)就可以了,不用知道创建对象的艰辛过程,降低模块间的耦合。
  • 工厂方法模式的扩展性非常优秀。比如增加一个产品。
  • 屏蔽产品类。这一特点非常重要,产品类的实现如何变化,调用者都不需要关心,它只需要关心产品的接口,只要接口保持不变,系统中的上层模块就不要发生变化
  • 工厂方法模式是典型的解耦框架。
  • 高层模块值需要知道产品的抽象类,其他的实现类都不用关心,符合迪米特法则;
  • 符合依赖倒置原则,只依赖产品类的抽象;
  • 符合里氏替换原则,使用产品子类替换产品父类。

工厂方法模式的使用场景:

  • 工厂方法模式是new一个对象的替代品,所以在所有需要生成对象的地方都可以 使用,但是需要慎重地考虑是否要增加一个工厂类进行管理,增加代码的复杂度。
  • 需要灵活的、可扩展的框架时,可以考虑采用工厂方法模式。
  • 工厂方法模式可以用在异构项目中。
  • 可以使用在测试驱动开发的框架下。如:测试一个类A,就需要把与类A有关 联关系的类B也同时产生出来,我们可以使用工厂方法模式把类B虚拟出来,避免类A与类B 的耦合。

工厂方法模式的扩展:

  • (1)缩小为简单工厂模式(静态工厂模式)
  • (2)升级为多个工厂类
  • (3)替代单例模式
  • (4)延迟初始化

通过例子来实现工厂方法模式及其扩展

女娲造人:女娲用泥捏出人形,然后通过八卦炉烧制,最后产生了人,根据烧制的火候,产生了黑黄白三种人。如图所示。女娲可以使用场景类Client来表示,八卦炉类似于一个工厂,负责生产人,三种不同肤色的人,他们都是Human接口下的不同实现类,他们分别有不同的肤色和语言。

类图分析:

  • AbstractHumanFactory是一个抽象类,定义了一个八卦炉具有的整体功能。
  • HumanFactory是一个实现类,实现AbstractHumanFactory抽象类中具体创建人的方法createHuman()。
  • Human是人类的接口,包含了肤色和语言两个方法。
  • 通过实现Human接口,产生了BlackHuman、YellowHuman、WhiteHuman三个实现类。
  • NvWa类是一个场景类,负责模拟这个场景,执行相关的任务。

代码实现:

(1)Human接口,及BlackHuman、YellowHuman、WhiteHuman的实现。

public interface Human {
	public void getClolr();		//肤色
	public void talk();			//语言
}
import com.sfq.impl.Human;
public class BlackHuman implements Human {
	@Override
	public void getClolr() {
		System.out.println("我是黑色的。。。");
	}
	@Override
	public void talk() {
		System.out.println("我说黑话。。。");
	}
}
import com.sfq.impl.Human;
public class YelloHuman implements Human {
	@Override
	public void getClolr() {
		System.out.println("我是黄色的。。。");
	}
	@Override
	public void talk() {
		System.out.println("我说普通话。。。");
	}
}
import com.sfq.impl.Human;
public class WhiteHuman implements Human {
	@Override
	public void getClolr() {
		System.out.println("我是白色的。。。");
	}
	@Override
	public void talk() {
		System.out.println("我说白话。。。");
	}
}

(2)AbstractHumanFactory接口及HumanFactory实现

public abstract class AbstractHumanFactory {
	public abstract <T extends Human> T createHuman(Class<T> c);
}

其中的"T"表示的是,只要实现了Human接口的类都可以作为参数

泛型对createHuman的输入参数产生两层限制:

  • 必须是Class类型;
  • 必须是Human的实现类。
import com.sfq.impl.AbstractHumanFactory;
import com.sfq.impl.Human;
public class HumanFactory extends AbstractHumanFactory {
	@Override
	public <T extends Human> T createHuman(Class<T> c) {
		//定义一个生产的人种
		Human human = null;
		try {
			human = (T)Class.forName(c.getName()).newInstance();
		} catch (Exception e) {
			System.out.println("不该为人啊");
		}
		return (T)human;
	}
}
  • c.getName():获取c类的全类名。
  • Class.forName():返回与给定的字符串名称相关联类或接口的Class对象。即进行动态加载类。
  • newInstance()的功能与new相同,返回一个对象。

(3)NvWa场景类

import com.sfq.action.BlackHuman;
import com.sfq.action.HumanFactory;
import com.sfq.action.WhiteHuman;
import com.sfq.action.YelloHuman;
import com.sfq.impl.AbstractHumanFactory;
import com.sfq.impl.Human;
public class NvWa {
	public static void main(String[] args) {
		// 声明八卦炉
		AbstractHumanFactory BaGuaLu = new HumanFactory();
		//女娲第一次造人,八卦炉火轻了,白人出现了
		System.out.println("--提前批:白种人--");
		Human whiteHuman = BaGuaLu.createHuman(WhiteHuman.class);
		whiteHuman.getClolr();
		whiteHuman.talk();
		//女娲第二次造人,八卦炉火大了,黑人出现了
		System.out.println("--一批:黑种人--");
		Human blackHuman = BaGuaLu.createHuman(BlackHuman.class);
		blackHuman.getClolr();
		blackHuman.talk();
		//女娲第三次造人,八卦炉火正好,黄人出现了
		System.out.println("--二批:黄种人--");
		Human yelloHuman = BaGuaLu.createHuman(YelloHuman.class);
		yelloHuman.getClolr();
		yelloHuman.talk();
	}
}

结果
--提前批:白种人--
我是白色的。。。
我说白话。。。
--一批:黑种人--
我是黑色的。。。
我说黑话。。。
--二批:黄种人--
我是黄色的。。。
我说普通话。。。

缩小为简单工厂模式(静态工厂模式)

如果一个模块仅需要一个工厂类,没有必要把它产生出来,使用静态的方法就可以了。

在类图中去掉了AbstractHumanFactory抽象类,同时把createHuman方法设置为静态类型,简化了类的创建过程,变更的源码仅仅是HumanFactoryNvWa类。

import com.sfq.impl.Human;
public class HumanFactory {
	public static <T extends Human> T createHuman(Class<T> c) {
		//定义一个生产的人种
		Human human = null;
		try {
			human = (T)Class.forName(c.getName()).newInstance();
		} catch (Exception e) {
			System.out.println("不该为人啊");
		}
		return (T)human;
	}
}
​import com.sfq.action.BlackHuman;
import com.sfq.action.HumanFactory;
import com.sfq.action.WhiteHuman;
import com.sfq.action.YelloHuman;
import com.sfq.impl.Human;
public class NvWa {
	public static void main(String[] args) {
		//女娲第一次造人,八卦炉火轻了,白人出现了
		System.out.println("--提前批:白种人--");
		Human whiteHuman = HumanFactory.createHuman(WhiteHuman.class);
		whiteHuman.getClolr();
		whiteHuman.talk();
		//女娲第二次造人,八卦炉火大了,黑人出现了
		System.out.println("--一批:黑种人--");
		Human blackHuman = HumanFactory.createHuman(BlackHuman.class);
		blackHuman.getClolr();
		blackHuman.talk();
		//女娲第三次造人,八卦炉火正好,黄人出现了
		System.out.println("--二批:黄种人--");
		Human yelloHuman = HumanFactory.createHuman(YelloHuman.class);
		yelloHuman.getClolr();
		yelloHuman.talk();
	}
}

结果
--提前批:白种人--
我是白色的。。。
我说白话。。。
--一批:黑种人--
我是黑色的。。。
我说黑话。。。
--二批:黄种人--
我是黄色的。。。
我说普通话。。。
​

缺点是工厂类的扩展比较困难,不符合开闭原则,但它仍然是一个非常实用的设计模式


升级为多个工厂类

如果一个产品类有多个具体实现,每个实现类的初始化方法都不相同,如果写在一个工厂方法中,势必会导致该方法巨大无比。我们可以为每个产品定义一个创造者,每个创建者都独立负责创建对应的产品对象,这非常符合单一职责原则。然后由调用者自己去选择与哪个工厂方法关联。

//抽象方法中不需要传递参数,因为每一个具体的工厂都已经非常明确自己的职责:创建自己负责的产品类对象。
public abstract class AbstractHumanFactory { 
    public abstract Human createHuman(); 
}

工厂清单:

public class BlackHumanFactory extends AbstractHumanFactory { 
    public Human createHuman() { 
        return new BlackHuman(); 
    } 
}
public class YellowHumanFactory extends AbstractHumanFactory { 
    public Human createHuman() { 
        return new YellowHuman(); 
    } 
}
public class WhiteHumanFactory extends AbstractHumanFactory { 
    public Human createHuman() { 
        return new WhiteHuman(); 
    } 
}

场景类NvWa如下:

import com.sfq.action.BlackHuman;
import com.sfq.action.HumanFactory;
import com.sfq.action.WhiteHuman;
import com.sfq.action.YelloHuman;
import com.sfq.impl.Human;
public class NvWa {
	public static void main(String[] args) {
		//女娲第一次造人,八卦炉火轻了,白人出现了
		System.out.println("--提前批:白种人--");
		Human whiteHuman = (new WhiteHumanFactory()).createHuman();
		whiteHuman.getClolr();
		whiteHuman.talk();
		//女娲第二次造人,八卦炉火大了,黑人出现了
		System.out.println("--一批:黑种人--");
		Human blackHuman = (new BlackHumanFactory()).createHuman();
		blackHuman.getClolr();
		blackHuman.talk();
		//女娲第三次造人,八卦炉火正好,黄人出现了
		System.out.println("--二批:黄种人--");
		Human yelloHuman = (new YellowHumanFactory()).createHuman();
		yelloHuman.getClolr();
		yelloHuman.talk();
	}
}

结果
--提前批:白种人--
我是白色的。。。
我说白话。。。
--一批:黑种人--
我是黑色的。。。
我说黑话。。。
--二批:黄种人--
我是黄色的。。。
我说普通话。。。

复杂的应用中一般采用多工厂的方法,然后再增加一个协调类,避免调用者与各个子工厂交流,协调类的作用是封装子工厂类,对高层模块提供统一的访问接口。


替代单例模式

通过工厂方法模式只在内存中生产一个对象代替单例模式。

Singleton定义了一个private的无参构造函数,目的是不允许通过new的方式创建一个对象。

public class Singleton { 
    //不允许通过new产生一个对象
    private Singleton(){ 
    }
    public void doSomething(){ 
    //业务处理 
    } 
}
public class SingletonFactory { 
    private static Singleton singleton; 
    static{ 
        try { 
            Class cl = Class.forName(Singleton.class.getName()); 
            //获得无参构造        
            Constructor constructor = cl.getDeclaredConstructor(); 
            //设置无参构造是可访问的
            constructor.setAccessible(true); 
            //产生一个实例对象
            singleton = (Singleton)constructor.newInstance(); 
        } catch (Exception e) { 
        //异常处理 
        } 
    }
    public static Singleton getSingleton(){ 
        return singleton; 
    } 
}

通过获得类构造器,然后设置访问权限,生成一个对象,然后提供外部访问,保证内存中的对象唯一。


延迟初始化

延迟初始化(Lazy initialization):一个对象被消费完毕后,并不立刻释放,工厂类保持其初始状态,等待再次被使用。

ProductFactory负责产品类对象的创建工作,并且通过prMap变量产生一个缓存,对需要再次被重用的对象保留

public class ProductFactory { 
    private static final Map<String,Product> prMap = new HashMap(); 
    public static synchronized Product createProduct(String type) throws Exception{ 
        Product product =null; 
        //如果Map中已经有这个对象 
        if(prMap.containsKey(type)){ 
            product = prMap.get(type); 
        }else{ 
            if(type.equals("Product1")){ 
                product = new ConcreteProduct1(); 
            }else{ 
                product = new ConcreteProduct2();
            }
            //同时把对象放到缓存容器中 
            prMap.put(type,product); 
        }
        return product; 
    } 
}

延迟加载还可以用在对象初始化比较复杂的情况下,例如硬件访问,涉及多方面的交互,则可以通过延迟加载降低对象的产生和销毁带来的复杂性。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

肥羊汤

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值