Java设计模式之工厂模式

设计模式要求我们应该针对接口编程,而不是针对实现编程,所谓的针对实现编程指的就是我们代码中利用new实例的对象,当对象有变动时,我们也需要修改对应的类,违反开闭原则,也会使我们代码的耦合度变高,工厂模式的作用就是将实例化对象的过程交由专门的工厂类来实现,我们要做的只是调用,不需要关心对象的生成过程。

设计模式可以分为三大类:

  • 创建型模式(五种):工厂方法模式,抽象工厂模式,单例模式,建造者模式,原型模式。
  • 结构型模式(七种):适配器模式,装饰器模式,代理模式,外观模式,桥接模式,组合模式,享元模式。
  • 行为型模式(十一种):策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。

其中工厂模式又包含三种模式:

  • 简单工厂模式(静态工厂模式)
  • 工厂方法模式
  • 抽象工厂模式

其中简单工厂并不属于二十三种设计模式之中。

下面用具体例子分别说明这三种模式。

简单工厂模式:

结构:

  • 工厂类角色:在java中对应具体的实现类
  • 抽象产品: 在java’中对应抽象类或者接口
  • 具体产品:java中对应具体的实现类

示例场景:
一个人有两辆车,一辆宝马,一辆奔驰,每次开车的都需要指定对应的车才能开车,用简单工厂方式进行改造。普通人只需要关注只是开车这个动作,而不关心开什么车。
按照简单工厂的结构设计,抽象出一个汽车类,然后宝马,奔驰各自具体实现这个接口,还要设计一个驾驶工厂,用来生产具体的产品对象。

UML示意图:

图片描述

具体代码:

car类:

package com.ljw.CarFactory;

/**
 * Created by liujiawei on 2018/6/20.
 */
public interface Car {
    public void drive();
}

奔驰:

package com.ljw.CarFactory;

/**
 * Created by liujiawei on 2018/6/20.
 */
public class BenzCar implements Car {
    @Override
    public void drive() {
        System.out.println("开奔驰");
    }
}

宝马:

package com.ljw.CarFactory;

/**
 * Created by liujiawei on 2018/6/20.
 */
public class BMWCar implements Car {
    @Override
    public void drive() {
        System.out.println("开宝马");
    }
}

工厂对象:(用了反射实例,避免使用new来进行对象的实例化)

package com.ljw.CarFactory;

/**
 * Created by liujiawei on 2018/6/20.
 */
public class DriveFactory {

    public  static <T extends Car> T createDriver(Class<T> clazz) {
        try {
            return (T) Class.forName(clazz.getName()).newInstance();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        return null;
    }
}

测试类:

package com.ljw.CarFactory;

/**
 * Created by liujiawei on 2018/6/20.
 */
public class TestCarFactory {
    public static void main(String[] args) {
        DriveFactory factory = new DriveFactory();
        Car car = factory.createDriver(BenzCar.class);
        car.drive();
    }
}

运行结果:

图片描述

上述代码中工厂类中的方法使用了static修饰符进行修饰,也就是我们所说的静态工厂模式。这边不使用static修饰符,代码也是完全没有问题的,那为什么要这样写呢?
使用静态工厂模式的好处有三个:(与构造器比较)
1.具名
区别与构造器只有参数不同的情况,静态工厂方法哟具体的名称,完全可以通过名称区分方法的作用;
2.环保
使用构造器每次都会有新的对象产生,静态工厂方法可以重复使用对象;
3.多子

使用构造期只能返回一种类型的对象,而静态工厂方法可以根据需要返回原返回类型的任意子对象类型。

工厂方法模式:

结构:
抽象工厂对象:对应java中的抽象类或者接口类
具体工厂对象:对应java中的具体实现类,包含具体的业务逻辑
抽象产品对象:对应java中的抽象类或者接口类
具体产品对象:对应java中具体的实现类

适用条件:
1.用户不需要了解创建的具体过程;
2.有多个具体产品产生。

换一个例子来说明工厂方法的使用场景,生产手机,三星公司生产三星手机,华为公司生产华为手机。
分析:如果还是简单工厂来实现的话,在工厂类中的逻辑处理会随着产品的增多变得越发庞大,使用工厂方法来设计可以解决这个问题。

UML示意图:

图片描述

代码示例:

抽象工厂类:

package com.ljw.CellPhoneFactory;

/**
 * Created by liujiawei on 2018/6/20.
 */
public interface PhoneFactory {
    public <T extends IPhone> T createPhone(Class<T> clazz);
}

不同产品的具体工厂实现类:

package com.ljw.CellPhoneFactory;

/**
 * Created by liujiawei on 2018/6/20.
 */
public class HuaweiPhoneFactory implements PhoneFactory {
    @Override
    public <T extends IPhone> T createPhone(Class<T> clazz) {
        try {
            return (T) Class.forName(clazz.getName()).newInstance();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        }
        return null;
    }
}
package com.ljw.CellPhoneFactory;

/**
 * Created by liujiawei on 2018/6/20.
 */
public class SumsangPhoneFactory implements PhoneFactory {
    @Override
    public <T extends IPhone> T createPhone(Class<T> clazz) {
        try {
            return (T) Class.forName(clazz.getName()).newInstance();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        }
        return null;    }
}

抽象产品类:

package com.ljw.CellPhoneFactory;

/**
 * Created by liujiawei on 2018/6/20.
 */
public interface IPhone {

    public void callin();

    public void callout();
}

不同产品的具体实现类:

package com.ljw.CellPhoneFactory;

/**
 * Created by liujiawei on 2018/6/20.
 */
public class HuweiPhone implements IPhone {
    @Override
    public void callin() {
        System.out.println("用华为手机打进");
    }

    @Override
    public void callout() {
        System.out.println("用华为手机打出");
    }
}
package com.ljw.CellPhoneFactory;

/**
 * Created by liujiawei on 2018/6/20.
 */
public class SumsangPhone implements IPhone {
    @Override
    public void callin() {
        System.out.println("用三星手机打进");
    }

    @Override
    public void callout() {
        System.out.println("用三星手机打出");
    }
}

测试类:

package com.ljw.CellPhoneFactory;

/**
 * Created by liujiawei on 2018/6/20.
 */
public class TestFactory {
    public static void main(String[] args) {
        PhoneFactory factory = new SumsangPhoneFactory();
        IPhone phone =  factory.createPhone(HuweiPhone.class);
        phone.callin();
        phone.callout();
    }
}

运行结果:

图片描述

首先可以看到上述代码是成功实现了我们的目的,手机的产生我们并不需要自己去操作,只需要通过对应的工厂就可生产出需要的手机。

大家可以注意到,上面虽然有两个具体工厂类,但是里面的代码确是一模一样的,这里面用到了反射方面的知识,通过类的反射获取实例,正常的写法则是在对应的具体工厂类中 return new xx(),通过构造器这种方式来获取对象。其实也是违背了避免面向实现编程,所以这里直接换成了反射来实现,事实上很多框架都是这样结合起来使用的。

可以看到工厂方法模式解决了简单工厂模式中上帝类的存在,如果又有了一个新的产品,比如中兴手机,我们只需要在创建一个中兴手机的具体产品类和一个生产中兴手机的具体工厂类即可,但是他也有自己的缺点。那就是没办法解决产品族和产品等级结构的问题。也就是说具体工厂类只能生产一类产品,没法生产多类产品。

*这里补充下产品族和产品等级结构的概念。(copy网上)
(1) 产品等级结构:产品等级结构即产品的继承结构,如一个抽象类是电视机,其子类有海尔电视机、海信电视机、TCL电视机,则抽象电视机与具体品牌的电视机之间构成了一个产品等级结构,抽象电视机是父类,而具体品牌的电视机是其子类。
(2) 产品族:在抽象工厂模式中,产品族是指由同一个工厂生产的,位于不同产品等级结构中的一组产品,如海尔电器工厂生产的海尔电视机、海尔电冰箱,海尔电视机位于电视机产品等级结构中,海尔电冰箱位于电冰箱产品等级结构中,海尔电视机、海尔电冰箱构成了一个产品族。

产品等级结构与产品族示意图如图所示:

图片描述
产品族与产品等级结构示意图

了解了上面两个概念,接下来在上面的场景中,增加一些条件,现在增加了路由器这个产品,原有的手机工厂中只能生产手机产品,路由器该通过什么方式生产呢?

抽象工厂模式由此诞生。抽象工厂模式的用意就是为客户端提供接口,可以创建多个产品族的对象。
结合上面的条件,也就是说不再有手机工厂,而是对应的产品族工厂,这个工厂可以生产一系列产品,手机,路由器,以后新加入的也都在这里进行生产。

抽象工厂模式

UML示意图:

图片描述
代码示例:
抽象产品类:

package com.ljw.EqpFactory;

/**
 * Created by liujiawei on 2018/6/21.
 */
public interface IPhone {
    public void callin();

    public void callout();
}
package com.ljw.EqpFactory;

/**
 * Created by liujiawei on 2018/6/21.
 */
public interface IRouter {
    public void getRoute();
}

具体产品类:

package com.ljw.EqpFactory;

/**
 * Created by liujiawei on 2018/6/21.
 */
public class SumsangPhone implements IPhone {
    @Override
    public void callin() {
        System.out.println("用三星手机打进");
    }

    @Override
    public void callout() {
        System.out.println("用三星手机打出");
    }
}
package com.ljw.EqpFactory;

/**
 * Created by liujiawei on 2018/6/21.
 */
public class HuaweiPhone implements IPhone {
    @Override
    public void callin() {
        System.out.println("用华为手机打进");
    }

    @Override
    public void callout() {
        System.out.println("用华为手机打出");
    }
}
package com.ljw.EqpFactory;

/**
 * Created by liujiawei on 2018/6/21.
 */
public class SumsangRouter implements IRouter {
    @Override
    public void getRoute() {
        System.out.println("三星路由器发射信号");
    }
}
package com.ljw.EqpFactory;

/**
 * Created by liujiawei on 2018/6/21.
 */
public class HuaweiRouter implements IRouter {
    @Override
    public void getRoute() {
        System.out.println("华为路由器发射信号");
    }
}

抽象工厂类:

package com.ljw.EqpFactory;

/**
 * Created by liujiawei on 2018/6/21.
 */
public interface EqpFactory {
    public <T extends IPhone> T createPhone(Class<T> clazz);

    public <T extends IRouter> T createRouter(Class<T> clazz);

}

具体工厂类:

package com.ljw.EqpFactory;

/**
 * Created by liujiawei on 2018/6/21.
 */
public class HuaweiFactory implements EqpFactory {
    @Override
    public <T extends IPhone> T createPhone(Class<T> clazz) {
        try {
            return (T) Class.forName(clazz.getName()).newInstance();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        }
        return null;    }

    @Override
    public <T extends IRouter> T createRouter(Class<T> clazz) {
        try {
            return (T) Class.forName(clazz.getName()).newInstance();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        }
        return null;    }
}
package com.ljw.EqpFactory;

/**
 * Created by liujiawei on 2018/6/21.
 */
public class SumsangFactory implements EqpFactory {
    @Override
    public <T extends IPhone> T createPhone(Class<T> clazz) {
        try {
            return (T) Class.forName(clazz.getName()).newInstance();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        }
        return null;
    }

    @Override
    public <T extends IRouter> T createRouter(Class<T> clazz) {
        try {
            return (T) Class.forName(clazz.getName()).newInstance();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        }
        return null;    }
}

测试类:

package com.ljw.EqpFactory;

/**
 * Created by liujiawei on 2018/6/21.
 */
public class TestEqpFactory {

    public static void main(String[] args) {
        EqpFactory sumsangFactory = new SumsangFactory();
        IPhone phone = sumsangFactory.createPhone(SumsangPhone.class);
        IRouter router = sumsangFactory.createRouter(SumsangRouter.class);
        phone.callin();
        phone.callout();
        router.getRoute();
    }
}

运行结果:

图片描述

可以看到,通过三星工厂,我们生产出了三星的一系列产品。

总结:抽象工厂和工厂方法的不同在于,工厂方法中工厂是生产一类产品,而抽象工厂是生产一系列产品(产品族),但是它的缺点在于如果要扩展产品族的话,从接口处进行改动,所有子类都需要重写。(结合反射可以进行优化,类似上面)

三种设计模式总结:

  1. 简单工厂模式,工厂方法模式和抽象工厂方法模式,实现难度递增,适用于不同复杂度的业务场景;
  2. 将对象的实例化过程交给了对应的工厂类,实体和业务解耦,符合开闭原则,方便扩展;
  3. 抽象工厂里面又使用了一个个工厂方法来具体实现工厂。
  4. 工厂方法是依赖倒置原则的经典实现模式。也就是不论高层组件和低层组件都应该依赖于抽象,而不是具体实现类。