五、工厂模式
需求示例
看一个披萨的项目:要便于披萨种类的扩展,要便于维护
-
披萨的种类很多(比如 GreekPizz、CheesePizz 等)
-
披萨的制作过程有 prepare,bake, cut, box。
-
完成披萨店订购功能。
使用传统的方法(不使用设计模式)
披萨类及其子类:
/**
* @author cVzhanshi
* @create 2023-04-17 16:45
*/
@Slf4j
public abstract class Pizza {
public abstract void prepare();
public void bake() {
log.info("bake()");
}
public void cut() {
log.info("cut()");
}
public void box() {
log.info("box()");
}
}
@Slf4j
public class GreekPizza extends Pizza {
@Override
public void prepare() {
log.info("prepare() ---> GreekPizza");
}
}
...
订单类
@Slf4j
public class OrderPizza {
public void generateOrder(String type) {
Pizza pizza;
if (type.equals("greek")) {
pizza = new GreekPizza();
} else if (type.equals("cheese")) {
pizza = new CheesePizza();
} else {
log.info("选择错误");
return;
}
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
}
}
分析:
- 代码比较好理解
- 违反了开闭原则,即对扩展开放,对修改关闭。如果新增一种披萨,需要新增一个类,然后在订单业务中添加if分支。而订单业务也许会部署在多个商店中,订单业务相对于说是使用者,使用者应该对应的关闭,即不允许修改,要修改只能在扩展修改。
5.1 简单工厂模式
-
简单工厂模式(静态工厂模式)是属于创建型模式,是工厂模式的一种。简单工厂模式是由一 个工厂对象决定创建出哪一种产品类的实例。简单工厂模式是工厂模式家族 中最简单实用的模式。
-
简单工厂模式:定义了一个创建对象的类,由这个类来封装实例化对象的行为(代码)
-
在软件开发中,当我们会用到大量的创建某种、某类或者某批对象时,就会 使用到工厂模式
简单工厂模式实现上述例子
思路:把创建Pizza对象封装到一个类中,这样我们有新的Pizza种类时,只需要修改该类就可,其它有创建到Pizza对象的代码就不需要修改了。
工厂类:
/**
* @author cVzhanshi
* @create 2023-04-17 17:14
*/
public class SimplePizzaFactory {
public static Pizza createPizza(String type) {
Pizza pizza = null;
if (type.equals("greek")) {
pizza = new GreekPizza();
} else if (type.equals("cheese")) {
pizza = new CheesePizza();
}
return pizza;
}
}
订单类修改:
@Slf4j
public class OrderPizza {
public void generateOrder(String type) {
Pizza pizza = SimplePizzaFactory.createPizza(type);
if (pizza != null) {
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
}
}
}
5.2 工厂方法模式
针对上诉案例增加新需求:
披萨项目新的需求:客户在点披萨时,可以点不同口味的披萨,比如 北京的奶酪pizza、 北京的胡椒pizza 或者是伦敦的奶酪pizza、伦敦的胡椒pizza。
对于新需求,使用简单工厂模式也能实现,但是考虑到项目的 规模,以及软件的可维护性、可扩展性并不是特别好。
思路:使用工厂方法模式
工厂方法模式介绍
定义了一个创建对象的抽象方法,由子类决定要实例化的类。工厂方法模式将对象的实例化推迟到子类。
代码示例:
-
首先抽象创建披萨类创造步骤为方法
/** * @author cVzhanshi * @create 2023-04-17 16:48 */ @Slf4j public abstract class OrderPizza { public abstract Pizza createPizza(String type); public void generateOrder(String type) { Pizza pizza = createPizza(type); if (pizza != null) { pizza.prepare(); pizza.bake(); pizza.cut(); pizza.box(); } } }
-
根据不同口味创建不同的实现类
@Slf4j public class BJOrderPizza extends OrderPizza{ @Override public Pizza createPizza(String type) { Pizza pizza = null; if (type.equals("greek")) { pizza = new BJGreekPizza(); } else if (type.equals("cheese")) { pizza = new BJCheesePizza(); } return pizza; } } @Slf4j public class LDOrderPizza extends OrderPizza{ @Override public Pizza createPizza(String type) { Pizza pizza = null; if (type.equals("greek")) { pizza = new LDGreekPizza(); } else if (type.equals("cheese")) { pizza = new LDCheesePizza(); } return pizza; } }
5.3 抽象工厂模式
-
抽象工厂模式:定义了一个interface用于创建相关或有依赖关系的对象簇,而无需指明具体的类
-
抽象工厂模式可以将简单工厂模式和工厂方法模式进行整合
-
将工厂抽象成两层,AbsFactory(抽象工厂) 和具体实现的工厂子类。程序员可以根据创建对象类型使用对应的工厂子类。这样将单个的简单工厂类变成了工厂簇, 更利于代码的维护和扩展。
代码示例:
-
一个抽象工厂模式的抽象层(接口)
/** * @author cVzhanshi * @create 2023-04-18 14:53 */ public interface AbsFactory { Pizza createPizza(String orderType); }
-
抽象子类
/** * @author cVzhanshi * @create 2023-04-18 14:55 */ public class LDFactory implements AbsFactory { @Override public Pizza createPizza(String orderType) { Pizza pizza = new BJCheesePizza(); if (orderType.equals("cheese")) { pizza = new LDCheesePizza(); } else if (orderType.equals("greek")) { pizza = new LDGreekPizza(); } return pizza; } } /** * @author cVzhanshi * @create 2023-04-18 14:55 */ public class BJFactory implements AbsFactory { @Override public Pizza createPizza(String orderType) { Pizza pizza = new BJCheesePizza(); if (orderType.equals("cheese")) { pizza = new BJCheesePizza(); } else if (orderType.equals("greek")) { pizza = new BJGreekPizza(); } return pizza; } }
-
订单类,可以忽视底层使用的哪个工厂类
/** * @author cVzhanshi * @create 2023-04-18 15:06 */ public class OrderPizza { private AbsFactory absFactory; public OrderPizza(AbsFactory absFactory, String ordertype) { this.absFactory = absFactory; Pizza pizza; pizza = absFactory.createPizza(ordertype); if (pizza != null) { pizza.prepare(); pizza.bake(); pizza.cut(); pizza.box(); } } }
5.4 jdk代码中的体现
Calendar.getInstance();就是很好的一个体现,如代码:
public static Calendar getInstance(Locale aLocale)
{
return createCalendar(TimeZone.getDefault(), aLocale);
}
public static Calendar getInstance(TimeZone zone,
Locale aLocale)
{
return createCalendar(zone, aLocale);
}
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) {
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;
}
}
}
if (cal == null) {
if (aLocale.getLanguage() == "th" && aLocale.getCountry() == "TH") {
cal = new BuddhistCalendar(zone, aLocale);
} else if (aLocale.getVariant() == "JP" && aLocale.getLanguage() == "ja"
&& aLocale.getCountry() == "JP") {
cal = new JapaneseImperialCalendar(zone, aLocale);
} else {
cal = new GregorianCalendar(zone, aLocale);
}
}
return cal;
}
5.5 小结
- 工厂模式的意义是将实例化对象的代码提取出来,放到一个类中统一管理和维护,达到和主项目的依赖关系的解耦。从而提高项目的扩展和维护性。
- 创建对象实例时,不要直接 new 类, 而是把这个new 类的动作放在一个工厂的方法 中,并返回。有的书上说,变量不要直接持有具体类的引用。
- 不要让类继承具体类,而是继承抽象类或者是实现interface(接口)。
- 不要覆盖基类中已经实现的方法。