案例分析
先梳理一个需要:披萨->要求便于披萨种类的扩展与维护
1、披萨的种类有很多(CheesePizza、BeefPizza…)。2、Pizza的制作有:prepare、bake、cut、box
先梳理设计类图
代码实现
package com.example.pattern.factory.pizza;
/**
* @author zjt
* @date 2020-12-02
*/
public abstract class Pizza {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
// 先忽略其它共有属性
public abstract void prepare();
public void bake() {
System.out.println(this.name + " baking");
}
public void cut() {
System.out.println(this.name + " cutting");
}
public void box() {
System.out.println(this.name + " boxing");
}
}
package com.example.pattern.factory.pizza;
/**
* @author zjt
* @date 2020-12-02
*/
public class BeefPizza extends Pizza {
public BeefPizza(String name) {
super.setName(name);
}
@Override
public void prepare() {
System.out.println(super.getName() + " 准备原材料");
}
}
package com.example.pattern.factory.pizza;
/**
* @author zjt
* @date 2020-12-02
*/
public class CheesePizza extends Pizza {
public CheesePizza(String name) {
super.setName(name);
}
@Override
public void prepare() {
System.out.println(super.getName() + " 准备原材料");
}
}
public class OrderPizza {
public static void main(String[] args) {
OrderPizza order = new OrderPizza();
order.testTradition();
//order.testSimpleFactory();
//order.testFactoryMethod();
//order.testFactoryMethod1()
}
public void testTradition() { // type B 和 C
Scanner scan = new Scanner(System.in);
do {
System.out.println("请输入想要购买的 Pizza :");
String type = scan.next();
Pizza pizza = this.createByType(type, str -> {
switch (str) {
case "B":
return new BeefPizza("牛肉pizza");
case "C":
return new CheesePizza("芝士pizza");
default:
return null;
}
});
if (null == pizza) {
System.out.println(" Pizza 种类错误 ");
break;
}
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
} while (true);
}
public Pizza createByType(String type, Function<String, Pizza> fin) {
return fin.apply(type);
}
}
优缺点分析:这种方式的优点是写法简单;缺点是违反了设计模式的OCP原则,即对扩展开开放、对修改关闭。我们希望给类增加新的功能的时候,尽量不修改或尽量少修改代码。比如我们给Pizza 新增一个种类,我们不仅需要新增类,还要在使用方 order中添加相关的方法 case XXX …
改进思路
分析:修改代码可以接受,但是如果我们在其它地方也有创建Pizza的代码,也是需要修改的,如果有遗漏,会带来程序错误,可维护性差
思路: 把创建Pizza对象封装到一个类中,这样我们有新的种类时,只需要修改该类即可,其它创建Pizza对象的代码就不需要修改了
简单工厂模式
简单工厂模式的基本介绍
简单工厂模式是属于创建型的模式,是工厂模式的一种,简单工厂模式是由一个工厂对象决定创建出那种产品类的实例,简单工厂模式是工厂模式家族最简单实用的模式。
简单工厂模式:定义一个创建对象的类,由这个类来封装实例化对象的行为(代码)。 在软件开发中,当我们会用到大量创建某种、某类或者某批对象时,就会使用到工厂模式。
package com.example.pattern.factory.pizza;
/**
* @author zjt
* @date 2020-12-02
*/
public class SimplePizzaFactory {
// 结合单例模式使用 不希望SimplePizzaFactory这个工厂每次使用的时候都去 new SimplePizzaFactory 对象
public static volatile SimplePizzaFactory simplePizzaFactory;
private SimplePizzaFactory() {
}
public static SimplePizzaFactory getInstance() {
if (null == simplePizzaFactory) {
synchronized (SimplePizzaFactory.class) {
if (null == simplePizzaFactory) {
simplePizzaFactory = new SimplePizzaFactory();
}
}
}
return simplePizzaFactory;
}
public Pizza createPizza(String type) {
switch (type) {
case "B":
return new BeefPizza("牛肉披萨");
case "C":
return new CheesePizza("芝士披萨");
case "D":
return new DurianPizza("榴莲披萨");
default:
return null;
}
}
}
public class DurianPizza extends Pizza {
public DurianPizza(String name) {
super.setName(name);
}
@Override
public void prepare() {
System.out.println(super.getName() + " 准备原材料");
}
}
OrderPizza类中新增方法
public void testSimpleFactory() {
Scanner scan = new Scanner(System.in);
SimplePizzaFactory instance = SimplePizzaFactory.getInstance();
Pizza pizza;
do {
System.out.println("请输入想要购买的 Pizza :");
String type = scan.next();
pizza = instance.createPizza(type);
if (null == pizza) {
System.out.println(" Pizza 种类错误 ");
break;
}
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
} while (true);
}
工厂方法模式
增加了一个新的需求 :客户点披萨的时候可以选择尺寸。比如7存的、10寸的披萨 希望不同的尺寸的pizza由不同的工厂制作出来
思路1:直接解决 创建多个简单工厂类,比如 SimpleSize7PizzaFactory,但是考虑到代码的扩展性和可维护性,我们可以使用工厂方法模式
工厂方法模式设计方案:将具体实例化的功能作为抽象方法,在不同尺寸的子类中具体实现
工厂方法模式:定义了一个创建对象的抽象方法,由子类决定要实例化的类。工厂方法模式将对象的实例化推迟到子类
public abstract class AbstractPizzaFactory {
// 工厂方法模式
// 增加了一个新的需求 :客户点披萨的时候可以选择尺寸。比如7存的、10寸的披萨 希望不同的尺寸的pizza由不同的工厂制作出来
// 思路1:直接解决 创建多个简单工厂类,比如 SimpleSize7PizzaFactory,
// 但是考虑到代码的扩展性和可维护性,我们可以使用工厂方法模式
// 工厂方法模式设计方案:将具体实例化的功能作为抽象方法,在不同尺寸的子类中具体实现
// 工厂方法模式:定义了一个创建对象的抽象方法,由子类决定要实例化的类。工厂方法模式将对象的实例化推迟到子类
public abstract Pizza createPizzaBySize(String type);
}
public class Size7PizzaFactory extends AbstractPizzaFactory {
private Size7PizzaFactory() {
}
public static Size7PizzaFactory getInstance() {
return Size7PizzaFactoryInstance.FACTORY;
}
@Override
public Pizza createPizzaBySize(String type) {
switch (type) {
case "B":
return new BeefPizza("7寸的牛肉披萨");
case "C":
return new CheesePizza("7寸的芝士披萨");
case "D":
return new DurianPizza("7寸的榴莲披萨");
default:
return null;
}
}
private static class Size7PizzaFactoryInstance {
private static final Size7PizzaFactory FACTORY = new Size7PizzaFactory();
}
}
public class Size10PizzaFactory extends AbstractPizzaFactory {
private Size10PizzaFactory() {
}
public static Size10PizzaFactory getInstance() {
return Size10PizzaFactory.Size10PizzaFactoryInstance.FACTORY;
}
@Override
public Pizza createPizzaBySize(String type) {
switch (type) {
case "B":
return new BeefPizza("10寸的牛肉披萨");
case "C":
return new CheesePizza("10寸的芝士披萨");
case "D":
return new DurianPizza("10寸的榴莲披萨");
default:
return null;
}
}
private static class Size10PizzaFactoryInstance {
private static final Size10PizzaFactory FACTORY = new Size10PizzaFactory();
}
}
抽象工厂模式
基本介绍
抽象工厂模式:定义一个interface用于创建相关或者有依赖关系的对象簇,无需指明具体的类。
抽象工厂模式可以将简单工厂和工厂方法模式进行整合。从设计层面上看,抽象工厂模式就是对简单工厂和工厂方法模式的进一步整改(或者说进一步抽象)。
将工厂抽象为两层,AbstractFactory(抽象工厂)和具体实现的工厂子类。我们可以根据创建对象类型使用对应的工厂子类。这样将单个简单工厂变成了工厂簇,更利于代码的维护和扩展。
public interface AbstractInterfaceFactory {
Pizza createByFactory(String type);
}
public class Size7Factory implements AbstractInterfaceFactory {
@Override
public Pizza createByFactory(String type) {
System.out.println("抽象工厂类");
switch (type) {
case "B":
return new BeefPizza("7寸的牛肉披萨");
case "C":
return new CheesePizza("7寸的芝士披萨");
case "D":
return new DurianPizza("7寸的榴莲披萨");
default:
return null;
}
}
}
public class Size10Factory implements AbstractInterfaceFactory{
@Override
public Pizza createByFactory(String type) {
System.out.println("抽象工厂类");
switch (type) {
case "B":
return new BeefPizza("10寸的牛肉披萨");
case "C":
return new CheesePizza("10寸的芝士披萨");
case "D":
return new DurianPizza("10寸的榴莲披萨");
default:
return null;
}
}
}
OrderPizza 新增测试方法
public void testFactoryMethod1() {
Scanner scan = new Scanner(System.in);
do {
System.out.println("请输入想要购买的 Pizza 尺寸 :");
String size = scan.next();
System.out.println("请输入想要购买的 Pizza 种类:");
String type = scan.next();
AbstractInterfaceFactory factory;
if ("7".equalsIgnoreCase(size)){
factory = new Size7Factory();
}else {
factory = new Size10Factory();
}
Pizza pizza = factory.createByFactory(type);
if (null == pizza) {
System.out.println(" Pizza 种类错误 ");
break;
}
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
} while (true);
}
JDK使用工厂模式的源码分析
JDK中的Calendar类使用到了简单工厂模式,源码追踪如下。
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; // 捕捉到了异常,往下走准备构造Calendar实例
if (aLocale.hasExtensions()) {
String caltype = aLocale.getUnicodeLocaleType("ca");
if (caltype != null) { // 根据传入的不同的 caltype 返回不同的实例
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;
}
工厂模式总结
工厂模式的意义:将实例化对象的代码提取出来,放到统一的类中去管理和维护,达到和主项目的依赖关系的解耦。从而提高项目的扩展性和维护性。3种工厂模式:简单工厂模式、工厂方法模式、抽象工厂模式。设计模式的依赖抽象原则。
创建实例时,不要直接new类,而是把这个new类的动作放在一个工厂的方法中并返回,变量不要直接持有具体类的引用。不要让类直接继承具体类,而是继承抽象类或者实现接口,不要覆盖基类中已经实现的方法。