工厂模式
工厂方法模式的定义
定义一个用于创建对象的接口,让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到其子类。
在工厂模式中,其中Product负责抽象具体对象的共性,实现对事务最抽象的定义。Creator为抽象创建类,也就是抽象工厂,具体如何创建产品类是由具体的实现工厂 ConcreteCreator完成的。
工厂类的实现
我们来看一个女娲造人的类图:
其中AbstractHumanFactory是一个抽象类:
public abstract class AbstractHumanFactory {
public abstract <T extends Human> T createHuman(Class<T> c);
}
我们可以看到在抽象方法中接收Human的子类,并返回对应的Human的子类。
Human为接口,提供人类的规范:
public interface Human {
//每个人种的皮肤都有相应的颜色
public void getColor();
//人类会说话
public void talk();
}
白种人、黄种人、黑种人分别实现Human接口,提供相关的实现。
具体的工厂类,为HumanFactory 继承自AbstractHumanFactory 并实现抽象方法。
public class HumanFactory extends AbstractHumanFactory {
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;
}
在createHuman方法中接收Human的子类对象类,采用反射的方法创建对象的实例。
这样一个简单的工厂类就被创建出来,我们采用工厂类,可以每次创建出不同的对象。并在抽象的过程中,采用抽象类依赖于接口类,介绍了类之间的耦合,从而工厂类不再耦合具体对象,可以创建出不同Human的子类对象。
工厂模式的优点
- 具有良好的封装,调用者只需要知道类名即可,不需要知道对象创建的具体过程,降低模块之间的耦合
- 工厂方法的拓展性比较强,比如我们在上面再加一个BrownHuman类,工厂类不需要做任何修改。
- 屏蔽类的创建过程,只关心产品的上层接口,只要接口保持不变系统中的其他模块就不需要发生变化。
- 典型的解耦框架,高层模块负责产品的抽象,其他实现类不需要关心,符合符合迪米特法则,我不需要的就不要去交流;也符合依赖倒置原则,只依赖产品类的抽象;当然也符合里氏替换原则,使用产品子类替换产品父类。
工厂模式的使用场景
工厂模式就是new对象的一个替代品,所有在生成对象的地方都可以用,但是要考虑编码的复杂性。
需要可扩展,灵活的框架时,比如在发送邮件的时候有三个协议POP3、IMAP、HTTP,那么我们可以将他们抽象为一个接口,然后三个协议实现接口,定义工厂类,然后根据传入的协议信息返回不同协议的实现。这样设计比如我们邮件服务器提供了WebService接口,我们只需要在增加一个产品类实现接口接口。
工厂模式的拓展
缩小为简单工厂模式
简单模式也称为静态工厂模式,比如在上面生产人类的实例上面,我们可以把生产工厂的实现,去掉AbstractHumanFactory类,并将具体实现改为static方法,这样也可,但是这样做工厂类的扩展就比较困难,不符合开闭原则.
升级为多个工厂类
如果我们将所有的产品类都放到一个工厂方法中进行初始化,或许会导致代码的结构不清晰,。例如,一个产品类有5 个具体实现,每个实现类的初始化(不仅仅是new,初始化包括new一个对象,并对对象设置一定的初始值)方法都不相同,如果写在一个工厂方法中,势必会导致该方法巨大无比。
对此我们可以将工厂方法进行细化,每个工厂发送负责一类对象的创建。
至此,每个人种都有一个单独的工厂类,这种方式每个工厂创建的职责非常清晰,而且结构简单,但是可扩展性,降低比如我们在添加BrownHuman类,那么我们不仅仅要实现底层的实现,而且还要在编码一个具体BrownHuman的工厂类。
在复杂的应用中,大多采用多工厂的方法,然后在增加一个协调类,将各个子工厂进行封装,然后提供统一的外部接口。
- 替换单例模式
采用工厂方法替换单例模式:
我们将单例的构造方法私有化。
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; } }
当工厂加载到JVM时,触发静态代码块,然后利用反射将对象的构造方法设置为可访问的,然后创建对象实例。
该框架仍可以进行扩展,任何单例都可以在此工厂方法中进行扩展。
- 延迟初始化
延迟初始化是指,一个对象被消费完毕之后,并不立即释放,工厂类保持初始状态,等待再次被调用。
ProductFactory负责产品类对象的创建工作,并且通过prMap变量产生一个缓存,对需要 再次被重用的对象保留。
延迟加载同样是可以拓展的,比如限制对象实例的最大数量,我们可以通过判断Map的size进行判断。
延迟加载还可以用在对象初始化比较复杂的情况下,例如硬件访问,涉及多方面的交互,则可以通过延迟加载降低对象的产生和销毁带来的复杂性