系列文章
23种设计模式 —— 设计模式目的以及遵循的七大原则
23种设计模式 —— 单例模式【饿汉式、懒汉式、双重检查、静态内部类、枚举】
23种设计模式 —— 工厂模式【简单工厂、工厂方法、抽象工厂】
23种设计模式 —— 原型模式【克隆羊、浅拷贝、深拷贝】
2、工厂模式
有这么一个披萨项目需求:
- 披萨种类很多,比如GreekPizza、CheessPizza
- 披萨的制作有prepare、bake、cut、box
- 现完成披萨店订购功能
按照传统的方式,肯定先将Pizza做成抽象类,Pizza中包含prepare、bake、cut、box方法,而不同披萨的准备肯定不一样,因此prepare是一个抽象方法。GreekPizza和CheessPizza继承Pizza类,实现自己的方法,而订购披萨的商店则会依赖于Pizza、CheessPizza和GreekPizza。
但这样显然是不符合OCP(开闭原则)的,当我们新增一种披萨时,会去修改使用方,因为OrderPizza1会依赖于披萨,并且一般OrderPizza不止一个。所以我们需要对其进行优化。
2.1、简单工厂模式
-
简单工厂模式属于创建型模式,是工厂模式的一种。简单工厂模式是由一个工厂对象决定创建出哪一种产品类的实例,它是工厂模式中最简单使用的模式。
-
简单工厂模式:定义了一个创建对象的类,由这个类来封装实例化对象的行为i。
-
在开发中,当我们会用到大量创建某种、某类或谋批对象时,就要用到工厂模式。
增加一个SimpleFactory,统一由它来生产披萨,其他订单只需交给工厂而不用直接依赖于Pizza,工厂根据订单的不同能返回不同类型的披萨,这样就满足了开闭原则。
Pizza类
public abstract class Pizza {
public abstract void prepare();
public void bake(){
System.out.println("烤披萨");
}
public void cut(){
System.out.println("切披萨");
}
public void box(){
System.out.println("装盒");
}
}
public class CheesePizza extends Pizza {
@Override
public void prepare() {
System.out.println("准备奶酪披萨材料");
}
}
public class GreekPizza extends Pizza {
@Override
public void prepare() {
System.out.println("准备希腊披萨材料");
}
}
简单工厂
public class SimpleFactory {
//根据不同订单,返回不同的披萨
public static Pizza createPizza(String orderType){
Pizza pizza = null;
System.out.println("使用简单工厂模式");
if (orderType.equals("greek")){
System.out.println("希腊披萨");
pizza = new GreekPizza();
}
else if(orderType.equals("cheese")){
System.out.println("奶酪披萨");
pizza = new CheesePizza();
}
return pizza;
}
}
订单类
public class OrderPizza {
Pizza pizza = null;
public OrderPizza(){
String orderType = "";
do{
orderType = getType();
pizza = SimpleFactory.createPizza(orderType);
if (pizza != null){
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.bake();
}
else{
System.out.println("订购披萨失败!");
break;
}
}while (true);
}
private String getType(){
Scanner scanner = new Scanner(System.in);
System.out.println("输入披萨类型:");
String string = scanner.nextLine();
if (string != null)
return string;
return "";
}
public static void main(String[] args) {
OrderPizza orderPizza = new OrderPizza();
}
}
2.2、工厂方法模式
现在披萨项目有新的需求:客户点披萨时,可以点不同口味的披萨,比如北京的奶酪披萨、北京的希腊披萨或者伦敦的奶酪披萨。
思路一:我们仍然使用简单工厂模式,但地区的不同需要我们创建多个工厂,比如BJPizzaSimpleFactory、LDPizzaSimpleFactory。不过,考虑到项目的规模、软件的可维护性和可扩展性,这种方式并不是特别好。
思路二:使用工厂方法模式——将对象的实例化推迟到子类实现。在披萨项目中,就是将披萨项目的实例化功能抽象为抽象方法,在不同口味的订单子类种具体实现。
四种披萨都继承于Pizza类,BJOrder和LDOrder继承于抽象类OrderPizza,而createPizza由具体类实现。
披萨类
//Pizza抽象类
public abstract class Pizza {
public abstract void prepare();
public void bake(){
System.out.println("烤披萨");
}
public void cut(){
System.out.println("切披萨");
}
public void box(){
System.out.println("装盒");
}
}
//北京奶酪披萨
public class BJCheesePizza extends Pizza {
@Override
public void prepare() {
System.out.println("准备北京奶酪披萨原材料");
}
}
//北京希腊披萨
public class BJGreekPizza extends Pizza {
@Override
public void prepare() {
System.out.println("准备北京希腊披萨原材料\"");
}
}
//伦敦奶酪披萨
public class LDCheesePizza extends Pizza {
@Override
public void prepare() {
System.out.println("准备伦敦奶酪披萨原材料");
}
}
//伦敦希腊披萨
public class LDGreekPizza extends Pizza {
@Override
public void prepare() {
System.out.println("准备伦敦希腊披萨原材料\"");
}
}
抽象工厂:订单抽象类
public abstract class OrderPizza {
//抽象方法,具体由子类工厂实现
public abstract Pizza createPizza(String orderType);
//抽象类的构造方法,在子类实例化时因为有super(),所以会先被执行。
public OrderPizza(){
Pizza pizza = null;
String orderType = "";
do{
orderType = getType();
pizza = createPizza(orderType);//根据子类的不同,返回不同披萨
if (pizza != null){
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.bake();
}
else{
System.out.println("订购披萨失败!");
break;
}
}while (true);
}
//获得用户输入的披萨类型
private String getType(){
Scanner scanner = new Scanner(System.in);
System.out.println("输入披萨类型:");
String string = scanner.nextLine();
if (string != null)
return string;
return "";
}
}
伦敦和北京的具体工厂
//北京的订单工厂
public class BJOrderPizza extends OrderPizza {
@Override
public Pizza createPizza(String orderType) {
Pizza pizza = null;
if (orderType.equals("cheese"))
pizza = new BJCheesePizza();
else if (orderType.equals("greek"))
pizza = new BJGreekPizza();
return pizza;
}
}
//伦敦的订单工厂
public class LDOrderPizza extends OrderPizza {
@Override
public Pizza createPizza(String orderType) {
Pizza pizza = null;
if (orderType.equals("cheese"))
pizza = new LDCheesePizza();
else if (orderType.equals("greek"))
pizza = new LDGreekPizza();
return pizza;
}
}
测试
public class Test {
public static void main(String[] args) {
//订购北京的披萨
BJOrderPizza bjOrderPizza = new BJOrderPizza();
//订购伦敦的披萨
LDOrderPizza ldOrderPizza = new LDOrderPizza();
}
}
2.3、抽象工厂模式
- 抽象工厂模式:定义一个接口用于创建相关或有依赖关系的对象簇,而无需指明具体的类。
- 抽象工厂模式可以把简单工厂和工厂方法模式进行整合,它就是简单工厂模式的改进。
- 将工厂抽象为两层,AbsFactory(抽象工厂)和具体实现类工厂,程序员根据创建对象类型来使用相应的工厂,这样就将单个的简单工厂类变成了工厂簇,利于代码维护和扩展。
接口AbsFactory中有一个生产披萨的方法,而BJFactory和LDFactory实现该接口,去实现具体的生产各自披萨的方法,其他订单类只需于接口AbsFactory聚合,就能使用实现类BJFactory和LDFactory,这样就不是单一的简单工厂了。
Pizza类(和前面相同)
抽象工厂和具体工厂
//一个抽象工厂模式的抽象层(接口)
public interface AbsFactory {
//由具体的工厂实现类完成
Pizza creatPizza(String orderType);
}
//工厂子类
public class BJFactory implements AbsFactory {
@Override
public Pizza creatPizza(String orderType) {
Pizza pizza = null;
if (orderType.equals("cheese"))
pizza = new BJCheesePizza();
else if (orderType.equals("greek"))
pizza = new BJGreekPizza();
return null;
}
}
//工厂子类
public class LDFactory implements AbsFactory {
@Override
public Pizza creatPizza(String orderType) {
Pizza pizza = null;
if (orderType.equals("cheese"))
pizza = new LDCheesePizza();
else if (orderType.equals("greek"))
pizza = new LDGreekPizza();
return null;
}
}
订单类
package design_partten.factory.absfactory;
import java.util.Scanner;
public class OrderPizza {
//聚合的方式使用工厂
AbsFactory absFactory;
public OrderPizza(AbsFactory absFactory){
setAbsFactory(absFactory);
}
//设置工厂,订购披萨
private void setAbsFactory(AbsFactory absFactory){
Pizza pizza = null;
String orderType = "";
this.absFactory = absFactory;
do {
orderType = getType();
//absFactory可能是北京的工厂子类,也可能是伦敦的工厂子类
pizza = absFactory.creatPizza(orderType);
if (pizza != null){
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.bake();
}
else{
System.out.println("订购披萨失败!");
break;
}
}while (true);
}
//获取用户输入类型
private String getType(){
Scanner scanner = new Scanner(System.in);
System.out.println("输入披萨类型:");
String string = scanner.nextLine();
if (string != null)
return string;
return "";
}
}
测试
public class Test {
public static void main(String[] args) {
//北京披萨
new OrderPizza(new BJFactory());
//伦敦披萨
new OrderPizza(new LDFactory());
}
}
2.4、工厂模式在JDK-Calendar的源码
通过Calendar的静态方法 getInstance()
来获得一个Calendar实例,由于我们没有传入时区和地区,因此在getInstance()
中会调用createCalendar()
来使用默认的时区和地区。
在这个方法中,就用到了我们的简单工厂模式,类似于前面的披萨订单,我们给订单类不同的类型,它就会返回不同的披萨。createCalendar()
根据不同的时区地区来返回不同的Calendar。
2.5、小结
-
工厂模式的意义:
将实例化对象的代码提取出来,放到一个工厂类中统一进行管理和维护,达到主项目依赖关系的解耦,从而提高代码的扩展性和可维护性。
-
三种工厂模式:简单工厂模式、工厂方法模式、抽象工厂模式
-
设计模式的依赖抽象原则:
- 创建对象时,不要直接用new创建,而是把new类的动作放在工厂类的方法里,由这个方法返回对象。
- 不要让类继承具体的类,而是继承抽象类或实现接口。
- 不要重新基类中已经实现的方法。