工厂模式(Factory Pattern)是 Java 中最常用的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象。简单工厂模式是由一个工厂对象决定创建出哪一种产品类的实例。简单工厂模式是工厂模式家族中最简单使用的模式。简单工厂模式定义了一个创建对象的类,由这个类来封装实例化对象的行为。在软件开发中,当我们会用到大量的创建某种、某类或者某批量对象时,就会使用到工厂模式。
一、简单工厂模式案例
需求:用户可以通过一个披萨工厂类来获取自己想要的披萨。
工厂模式设计方案:首先将披萨项目的实例抽象成抽象方法,在不同的口味子类中具体实现。同时定义一个披萨的工厂类 (SimplePizzaFactory)来获取想要的披萨对象。它向工厂类中传递信息(GREEK 等),以便获取它所需要的对象类型。
类图:通过类图,查看构建的思路:如果有新的品种只需修改提供方,应用方无需修改。
【1】、定义一个接口 Pizza
package com.yintong.principle.singleresponsibility;
//将Pizza 做成一个接口
public interface Pizza {
//准备原材料, 不同的披萨不一样,因此,我们做成抽象方法
public void prepare();
}
【2】、定义披萨的种类,实现 Pizza 接口,如下定义了 GREEK、CHEESE 两种披萨。如果后续有新的种类可以实现 Pizza 接口,继续添加新的品种。
//希腊披萨
public class GreekPizza implements Pizza{
@Override
public void prepare() {
System.out.println(" 给希腊披萨 准备原材料 ");
}
}
//奶酪披萨
public class CheesePizza implements Pizza{
@Override
public void prepare() {
System.out.println(" 给制作奶酪披萨 准备原材料 ");
}
}
【3】、 创建一个简单的工厂类对象 SimpleFactory
public class SimpleFactory {
//传入 orderType(种类) 返回对应的 Pizza 对象
public Pizza createPizza(String orderType) {
Pizza pizza = null;
//使用简单工厂模式
if (orderType.equals("greek")) {
//希腊披萨
pizza = new GreekPizza();
} else if (orderType.equals("cheese")) {
//奶酪披萨
pizza = new CheesePizza();
}
return pizza;
}
}
【4】、在应用代码 OrderPizza 中调用工厂类,获取用户需要的披萨对象。
public class OrderPizza {
//定义用户要获取的披萨类型
private static final String pizza = "greek";
public static void main(String[] args) {
//通过工厂获取希腊披萨
SimpleFactory simpleFactory = new SimpleFactory();
//greekPizza 就是我们需要获取的目标披萨
Pizza greekPizza = simpleFactory.createPizza(pizza);
//调用目标披萨的方法
greekPizza.prepare();
}
}
二、工厂模式的优缺点
优点:1)、一个调用者想创建一个对象,只要知道其名称就可以了。
2)、扩展性高,如果想增加一个产品,只要扩展一个工厂类就可以。
3)、屏蔽产品的具体实现,调用者只关心产品接口。
缺点:每增加一个产品时,都需要增加一个具体实现类和对象实现工厂,使得系统中类的个数成倍增加,在一定程度上增加了系统的复杂度,同时也增加了系统具体类的依赖。这并不是什么好事。
注意事项:作为一种创建类模式,在任何需要生成复杂对象的地方,都可以使用工厂方法模式。有一点需要注意的地方就是复杂对象适合使用工厂模式,而简单对象,特别只需要通过 new 就可以完成创建的对象,无需工厂模式。如果使用工厂模式就需要使用工厂类,会增加系统的复杂度。
三、抽象工厂方法模式
需求:在上述的基础上,添加了地区。例如:用户可以点北京的奶酪披萨,北京的胡椒披萨,或者伦敦的奶酪披萨,伦敦的胡椒披萨。
工厂方法设计模式:1)、定义一个 interface(工厂的接口) 用于创建相关或者依赖关系的对象簇,而无需指明具体对象类。
2)、从设计层面来看,抽象工厂模式就是简单工厂模式的改进(或者称为进一步抽象)。
3)、将工厂抽象成两层,AbsFactory(抽象工厂)和具体实现的工厂子类。程序员可以根据创建对象类型使用对应的工厂子类。这样将单个的简单工厂类变成了工厂簇,更有利于代码的维护和扩展。
类图:将简单工厂模式中的工厂类抽象成了一个接口,易于后期扩展。
【1】、定义工厂类的接口(AbsFactory)
//一个抽象工厂模式的抽象层(接口)
public interface AbsFactory {
//让下面的工厂子类来 具体实现
public Pizza createPizza(String orderType);
}
【2】、让北京和伦敦的工厂类实现此接口(与简单工厂类的区别就是实现了 AbsFactory 接口,伦敦略)
public class BJFactory implements AbsFactory{
//更加orderType 返回对应的Pizza 对象
public Pizza createPizza(String orderType) {
Pizza pizza = null;
//使用简单工厂模式
if (orderType.equals("greek")) {
//希腊披萨
pizza = new GreekPizza();
} else if (orderType.equals("cheese")) {
//奶酪披萨
pizza = new CheesePizza();
}
return pizza;
}
}
【3】、在 Order 订单应用中,与接口 AbsFactory 进行组合。
public class OrderPizza {
AbsFactory factory;
//定义构造器
public OrderPizza(AbsFactory factory){
this.factory = factory;
}
//定义用户要获取的披萨类型,可以定义一个变量从客户端传入
private static final String pizza = "greek";
public void main(String[] args) {
//greekPizza 就是我们需要获取的目标披萨
Pizza greekPizza = factory.createPizza(pizza);
//调用目标披萨的方法
greekPizza.prepare();
}
}
四、工厂模式在 JDK-Calendar 类中的应用
//1、调用 Calendar 对象
Calendar instance = Calendar.getInstance();
//2、进入 Calendar.getInstance() 方法
public static Calendar getInstance()
{
return createCalendar(TimeZone.getDefault(),Locale.getDefault(Locale.Category.FORMAT));
}
//3、进入 createCalendar 方法
private static Calendar createCalendar(TimeZone zone,Locale aLocale){
CalendarProvider provider =
LocaleProviderAdapter.getAdapter(CalendarProvider.class, aLocale)
.getCalendarProvider();
if (provider != null) {
try {
return provider.getInstance(zone, aLocale);
} catch (IllegalArgumentException iae) {
// fall back to the default instantiation
}
}
Calendar cal = null;
if (aLocale.hasExtensions()) {
String caltype = aLocale.getUnicodeLocaleType("ca");
if (caltype != null) {
//该出就是我们要重点查看的部分,根据用户需求,创建Calendar 对象
switch (caltype) {
case "buddhist":
cal = new BuddhistCalendar(zone, aLocale);
break;
case "japanese":
cal = new JapaneseImperialCalendar(zone, aLocale);
break;
case "gregory":
cal = new GregorianCalendar(zone, aLocale);
break;
}
}
}
return cal;
}
五、工厂模式小结
1)、工厂模式的意义:将实例化对象的代码提取出来,放到一个类中统一管理和维护,达到和主项目的依赖关系的解耦。从而提高项目的扩展和维护性。
2)、设计模式依赖抽象原则:创建对象实例时,不要直接 new 类,而是把这个 new 类的动作放在一个工厂的方法中,并返回。不要让类继承具体类,而是继承抽象类或者实现 interface(接口)。不要覆盖基类中已经实现的方法。