23种设计模式 —— 工厂模式【简单工厂、工厂方法、抽象工厂】

系列文章

23种设计模式 —— 设计模式目的以及遵循的七大原则
23种设计模式 —— 单例模式【饿汉式、懒汉式、双重检查、静态内部类、枚举】
23种设计模式 —— 工厂模式【简单工厂、工厂方法、抽象工厂】
23种设计模式 —— 原型模式【克隆羊、浅拷贝、深拷贝】


2、工厂模式

有这么一个披萨项目需求:

  1. 披萨种类很多,比如GreekPizza、CheessPizza
  2. 披萨的制作有prepare、bake、cut、box
  3. 现完成披萨店订购功能

按照传统的方式,肯定先将Pizza做成抽象类,Pizza中包含prepare、bake、cut、box方法,而不同披萨的准备肯定不一样,因此prepare是一个抽象方法。GreekPizza和CheessPizza继承Pizza类,实现自己的方法,而订购披萨的商店则会依赖于Pizza、CheessPizza和GreekPizza。

但这样显然是不符合OCP(开闭原则)的,当我们新增一种披萨时,会去修改使用方,因为OrderPizza1会依赖于披萨,并且一般OrderPizza不止一个。所以我们需要对其进行优化。

1615211902392

2.1、简单工厂模式

  • 简单工厂模式属于创建型模式,是工厂模式的一种。简单工厂模式是由一个工厂对象决定创建出哪一种产品类的实例,它是工厂模式中最简单使用的模式。

  • 简单工厂模式:定义了一个创建对象的类,由这个类来封装实例化对象的行为i。

  • 在开发中,当我们会用到大量创建某种、某类或谋批对象时,就要用到工厂模式。

    1615251451851

增加一个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由具体类实现。

1615253421538

披萨类

//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,这样就不是单一的简单工厂了。

1615

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。

在这里插入图片描述
1615258508474

2.5、小结

  • 工厂模式的意义:

    将实例化对象的代码提取出来,放到一个工厂类中统一进行管理和维护,达到主项目依赖关系的解耦,从而提高代码的扩展性和可维护性。

    1615258947650
  • 三种工厂模式:简单工厂模式、工厂方法模式、抽象工厂模式

  • 设计模式的依赖抽象原则:

    • 创建对象时,不要直接用new创建,而是把new类的动作放在工厂类的方法里,由这个方法返回对象。
    • 不要让类继承具体的类,而是继承抽象类或实现接口。
    • 不要重新基类中已经实现的方法。

猜你喜欢

转载自blog.csdn.net/qq_39763246/article/details/114578285