工厂
“工厂”主要是用于解决对象的创建的方法。在一般的工程代码中,如果要创建一个类的对象,往往会这样写:
ClassA a = new ClassA();
对于简单的类来说,上面的方法并无不妥,但在比较复杂的项目中,有可能会出现下面几种情况:
1、进行类的改动时,比如修改类ClassA增加了入参,那么在引用该类的地方就要进行修改为
classA a = new ClassA(param1,param2…)
如果项目中依赖该类的地方较多,那么这个改动则需要耗费许多开发测试成本。
2、该类的构建过程复杂,每次新增引用都要重复构建,累积导致项目代码冗余。
3、该类构建过程中依赖其他类,每次引用该类都需要同步定义依赖对象,类之间的依赖越多,结合问题1,项目的维护修改成本会如同滚雪球般越来越大。
顾名思义,工厂用于生产产品,而工厂模式则用于解决创建对象的问题,因此工厂类即是用于生产类的对象的类。
简单工厂模式
简单工厂模式其实不属于23种设计模式,但三种工厂模式中存在一定的递进关系,所以我们需要先从了解简单工厂模式开始。
简单工厂模式将类的实例化和使用分离开来,在定义一个产品类时,同时创建一个该产品的工厂Factory类,后续对该产品类的实例化都通过工厂类来操作。
以产品类Phone的使用为例,Phone类下有两个子类HwPhone和MiPhone:
Phone.java
public abstract class Phone{
public abstract void call();
}
HwPhone.java
public class HwPhone extends Phone{
@Override
public void call(){
System.out.println("calling with HwPhone...");
}
}
MiPhone.java
public class MiPhone extends Phone{
@Override
public void call(){
System.out.println("calling with MiPhone...");
}
}
当需要使用到上面产品类时,不同于以往直接使用new一个对象的方法,我们使用一个工厂类SimplePhoneFactory来完成产品类的实例化。在工厂类中使用一个静态方法来判断、控制哪个子类被执行,并返回一个产品类的实例。所以简单工厂模式也称为静态工厂模式。
后续如果产品类有变化,那么只需要修改工厂类中进行产品类实例化的静态方法,而不用在每个依赖产品类的地方都进行修改。
public class SimplePhoneFactory {
public static Phone makePhone(String type){
if("hw".equals(type)){
return new HwPhone();
}
if("mi".equals(type)){
return new MiPhone();
}
return null;
}
}
在依赖产品类的代码中,直接使用工厂类SimplePhoneFactory得到产品类实例:
public class Call {
public static void main(String[] args){
Phone hwPhone = SimplePhoneFactory.makePhone("hw");
Phone miPhone = SimplePhoneFactory.makePhone("mi");
hwPhone.call();
miPhone.call();
}
}
执行结果:
calling with HwPhone…
calling with MiPhone…
使用简单工厂模式,避免了在代码中直接实例化依赖对象,降低了耦合度。但是每次需要新增一个产品类时,仍然需要对存量工厂类方法进行修改,增加返回新产品类实例,这样项目仍存在一定的维护成本。
工厂方法模式
相较于简单工厂模式,工厂方法模式对每一个产品都单独创建一个对应的工厂类,这样每次新增产品时,可以直接新增一个工厂类,而不需要修改存量工厂类。
仍然以简单工厂中的产品类Phone作为示例,工厂方法模式中仍然定义一个工厂类PhoneFactory用于生产其实例,但是此处我们把PhoneFactory定义为一个接口,在决定哪个子类被执行时,不再使用同一个静态方法控制实例化对象,而是对每一个产品子类都定义一个实现了PhoneFactory接口的工厂类,每个产品子类都在其对应的工厂类中进行实例化。
public interface PhoneFactory {
Phone makePhone();
}
public class HwPhoneFactory implements PhoneFactory {
public Phone makePhone() {
return new HwPhone();
}
}
public class MiPhoneFactory implements PhoneFactory {
public Phone makePhone() {
return new MiPhone();
}
}
在依赖产品类的代码中,只要使用其对应的工厂类实例化依赖对象即可:
public class Call {
public static void main(String[] args){
Phone hwPhone = new HwPhoneFactory().makePhone();
Phone miPhone = new MiPhoneFactory().makePhone();
hwPhone.call();
miPhone.call();
}
}
运行结果:
calling with HwPhone…
calling with MiPhone…
在工厂方法模式中定义的都是具体某一产品的工厂类,具体工厂负责生产具体的产品,每一个具体工厂对应一种具体产品,工厂方法也具有唯一性,一般情况下,一个具体工厂中只有一个工厂方法或者一组重载的工厂方法。但是有时候我们需要一个工厂可以提供多个产品对象,而不是单一的产品对象,这时候就会需要用到抽象工厂模式。
抽象工厂模式
在前面的示例中,产品类Phone下存在两个子类HwPhone和MiPhone,这几个产品之间的继承结构称为产品等级结构;如果PhoneFactory工厂类提出需要增加一个能够创建产品类Television对象的方法,实现了该接口的HwPhoneFactory和MiPhoneFactory工厂类分别能够创建并返回HwTelevision和MiTelevision的实例化对象,此时HwTelevision和MiTelevision之间构成了一个产品等级结构,而HwPhone和HwTelevision是位于不同产品等级结构中的一组产品,称为产品族。
但是PhoneFactory工厂类是对应产品类Phone的具体工厂类(ConcreteFactory),不能用于实例化产品类Television,因此我们使用一个抽象工厂类(AbstractFactory)来实现同一产品族的生产,在抽象工厂类中会定义多个方法用于创建同一产品族的不同产品。
抽象工厂模式包含如下角色:
- AbstractFactory:抽象工厂
- ConcreteFactory:具体工厂
- AbstractProduct:抽象产品
- Product:具体产品
定义产品类Phone,Television及其子类:
public abstract class Phone {
public abstract void call();
}
public class HwPhone extends Phone {
@Override
public void call(){
System.out.println("calling with HwPhone...");
}
}
public class MiPhone extends Phone {
@Override
public void call(){
System.out.println("calling with MiPhone...");
}
}
public abstract class Television {
public abstract void watch();
}
public class HwTelevision extends Television {
@Override
public void watch() {
System.out.println("watching TV with HwTelevision...");
}
}
public class MiTelevision extends Television {
@Override
public void watch() {
System.out.println("watching TV with MiTelevision...");
}
}
定义工厂类:首先定义一个抽象工厂类,抽象工厂类并不对应具体的产品类,类中定义了两个用于创建归属同一产品族的产品对象的方法。
HwFactory和MiFactory工厂子类实现了AbstractFactory接口及其方法,示例中HwPhone与HwTelevision归属同一产品族,而MiPhone和MiTelevision则同归属于另一产品族。
public interface AbstractFactory {
Phone makePhone();
Television makeTelevision();
}
public class HwFactory implements AbstractFactory {
public Phone makePhone() {
return new HwPhone();
}
public Television makeTelevision() {
return new HwTelevision();
}
}
public class MiFactory implements AbstractFactory {
public Phone makePhone() {
return new MiPhone();
}
public Television makeTelevision() {
return new MiTelevision();
}
}
在依赖产品对象代码处使用产品族工厂类进行产品类实例化:
public class TakeaRest {
public static void main(String[] args){
AbstractFactory hwFactory = new HwFactory();
hwFactory.makePhone().call();
hwFactory.makeTelevision().watch();
AbstractFactory miFactory = new MiFactory();
miFactory.makePhone().call();
miFactory.makeTelevision().watch();
}
}
运行结果:
calling with HwPhone…
watching TV with HwTelevision…
calling with MiPhone…
watching TV with MiTelevision…
抽象工厂模式与工厂方法模式最大的区别在于,工厂方法模式针对的是一个产品等级结构,而抽象工厂模式则需要面对多个产品等级结构,一个工厂等级结构可以负责多个不同产品等级结构中的产品对象的创建 。
同属于一个产品族的多个产品类,如果系统设计约束这些产品类需要同时依赖使用,此时需要一个工厂等级结构可以创建出分属于不同产品等级结构的一个产品族中的所有对象,这种情况下抽象工厂模式比工厂方法模式更为简单高效。
抽象工厂模式的“开闭原则”倾斜性
开闭原则:要求系统对扩展开放,对修改封闭,通过扩展达到增强其功能的目的。
对于涉及到多个产品族与多个产品等级结构的系统,其功能增强包括两方面:
1、增加产品族
对于增加新的产品族,抽象工厂模式很好的支持了“开闭原则”,对于新增加的产品族,只需要对应增加一个新的具体工厂即可,对已有代码无须做任何修改。
2、增加新的产品等级结构
对于增加新的产品等级结构,需要修改所有的工厂角色,包括抽象工厂类,在所有的工厂类中都需要增加生产新产品的方法,不能很好地支持“开闭原则”。
抽象工厂模式的这种性质称为“开闭原则”的倾斜性,抽象工厂模式以一种倾斜的方式支持增加新的产品:它为新产品族的增加提供方便,但不能为新的产品等级结构的增加提供这样的方便。
工厂模式的退化
当抽象工厂模式中每一个具体工厂类只创建一个产品对象,也就是只存在一个产品等级结构时,抽象工厂模式退化成工厂方法模式;当工厂方法模式中抽象工厂与具体工厂合并,提供一个统一的工厂来创建产品对象,并将创建对象的工厂方法设计为静态方法时,工厂方法模式退化成简单工厂模式。