【Java设计模式】——工厂模式

简介: 在Java应用程序中对象无处不在,这些对象都需要进行创建,如果创建的时候直接new对象,那么如果我们要更换对象,所有new对象的地方都需要进行更改。违背了软件设计原则中的开闭原则。如果我们使用工厂生产对象,只需要在工厂中关注对象的改变即可,达到了与对象解耦的目的,工厂模式最大的特点就是解耦合

🍊工厂模式

在Java应用程序中对象无处不在,这些对象都需要进行创建,如果创建的时候直接new对象,那么如果我们要更换对象,所有new对象的地方都需要进行更改。违背了软件设计原则中的开闭原则。如果我们使用工厂生产对象,只需要在工厂中关注对象的改变即可,达到了与对象解耦的目的,工厂模式最大的特点就是解耦合

补充:开闭原则
对扩展开放,对修改关闭。在程序需要进行扩展的时候,不能去修改原有的代码,实现一个热插拔的效果。是为了使程序的扩展性好,易于维护和升级。

🍋1.简单工厂

🍉1.1结构

  • 抽象产品:定义了产品的规范,描述了产品的主要特性和功能。
  • 具体产品:实现或者继承抽象产品的子类
  • 具体工厂:提供了创建产品的方法,调用者通过该方法来创建产品。

🌰1.2实现

以点咖啡为例:

咖啡抽象类

public abstract class Coffee {
    /**
     * 获取咖啡类型
     * @return
     */
    public abstract  String getName();

    /**
     *加糖
     */
    public  void addSugar(){
        System.out.println("加糖");
    }
    /**
     *加奶
     */
    public void addMilk(){
        System.out.println("加奶");
    }
}

美式咖啡类

public class AmericanCoffee  extends Coffee{

@Override
    public String getName(){
        return "美式咖啡";
    }
}

拿铁咖啡类

public class LatteCoffee extends Coffee {

    @Override
    public String getName(){
        return "拿铁咖啡";
    }
}

咖啡工厂类

public class CoffeeFactory {

    public Coffee createCoffee(String type){
      Coffee coffee = null;
      if("american".equals(type)){
          coffee = new AmericanCoffee();
      }else if("latten".equals(type)){
          coffee = new LatteCoffee();
      }else{
          throw new RuntimeException("没有此类型的咖啡");
      }
      return coffee;
    }
}

咖啡店类

public class CoffeeStore {

    public Coffee orderCoffee(String type){
        CoffeeFactory factory = new CoffeeFactory();

      //调用生产咖啡的方法
        Coffee coffee = factory.createCoffee(type);
        coffee.addMilk();
        coffee.addSugar();

        return  coffee;
    }
}

测试类

public class Test {
    public static void main(String[] args) {
        CoffeeStore coffeeStore = new CoffeeStore();
        Coffee coffee = coffeeStore.orderCoffee("latten");
        System.out.println(coffee.getName());
    }
}

类图
image.png

咖啡工厂负责生产咖啡(具体工厂),咖啡店通过咖啡工厂选取咖啡

其实简单工厂是大家在实际写代码的时候经常用到的,虽然简单工厂实现了咖啡店与咖啡的耦合,但是可以明显看到咖啡与咖啡工厂又耦合起来了,后期如果增加咖啡的新品种,我们需要修改咖啡工厂的代码,这又违背了“开闭原则”。

🍉1.3优缺点

优点:

封装了创建对象的过程,可以通过参数直接获取对象,把对象的创建和业务逻辑层分开,这样可以避免之后修改客户代码,如果需要实现新产品直接修改工厂类,更容易扩展。

缺点:

增加新产品时还需要修改工厂类的代码,违背了“开闭原则”。

🍉1.4扩展

静态工厂,将工厂类中创建对象的功能定义为静态的,这样不需要再创建工厂类,直接通过类名调用静态方法,类似于工具类

public class CoffeeFactory {
  //静态方法
    public static Coffee createCoffee(String type){
      Coffee coffee = null;
      if("american".equals(type)){
          coffee = new AmericanCoffee();
      }else if("latten".equals(type)){
          coffee = new LatteCoffee();
      }else{
          throw new RuntimeException("没有此类型的咖啡");
      }
      return coffee;
    }
}

🍋2.工厂方法

对工厂进行抽象,每一种产品对应一个具体工厂,新增产品只需要再新增对应的具体工厂,符合”开闭原则“

🍉2.1结构

  • 抽象工厂:提供创建产品的接口,调用者通过它访问具体工厂的工厂方法来创建产品
  • 具体工厂:主要是实现抽象工厂中的抽象方法,完成具体产品的创建
  • 抽象产品:定义了产品的规范,描述了产品的主要特性和功能
  • 具体产品:实现抽象产品所定义的接口,由具体工厂来创建,与具体工厂之间一一对应

🌰2.2实现

抽象咖啡来和具体咖啡类不变

咖啡工厂(抽象工厂)

public interface CoffeeFactory {
    /**
     * 创建咖啡
     * @return
     */
    Coffee createCoffee();
}

美式咖啡工厂(具体工厂)

public class AmericanCoffeeFactory implements CoffeeFactory{
    //美式咖啡工厂对象,专门生产美式咖啡
    @Override
    public Coffee createCoffee() {
        return new AmericanCoffee();
    }
}

拿铁咖啡工厂(具体工厂)

public class LatteCoffeeFactory implements CoffeeFactory{
    //拿铁咖啡工厂对象,专门生产拿铁咖啡
    @Override
    public Coffee createCoffee() {
        return new LatteCoffee();
    }
}

咖啡店

public class CoffeeStore {

    private CoffeeFactory factory;

    public void setFactory(CoffeeFactory factory) {
        this.factory = factory;
    }

    /**
     * 点咖啡
     */
    public Coffee orderCoffee() {
        Coffee coffee = factory.createCoffee();
        coffee.addSugar();
        coffee.addMilk();
        return coffee;
    }
}

测试类

public class Test {
    public static void main(String[] args) {
        //创建咖啡店对象
        CoffeeStore coffeeStore = new CoffeeStore();
        //创建工厂对象
        CoffeeFactory factory = new AmericanCoffeeFactory();
        coffeeStore.setFactory(factory);

        //点咖啡
        Coffee coffee = coffeeStore.orderCoffee();

        System.out.println(coffee.getName());
    }
}

类图

image.png

我们只需要知道所点咖啡具体对应的工厂对象,通过咖啡店调用对应的工厂,由工厂创建咖啡对象实现点咖啡的过程

🍉2.3优缺点

优点:

  • 用户只需要知道具体工厂就可以获得所需产品,无需知道产品的具体创建过程
  • 在系统新增产品时只需要添加具体产品类和对应的具体工厂类,无需对原工厂进行修改符合“开闭原则”

缺点:

每增加一个产品就要增加一个对应的具体工厂类,增加的系统的复杂性。如果具体产品种类过多,那么大量的工厂类不仅难以管理,而且也会造成程序中创建的对象过多,严重影响内存性能

🍋3.抽象工厂

🍉3.1结构

  • 抽象工厂:提供创建产品的接口,包含多个创建产品的方法,可以创建多个不同等级的产品
  • 具体工厂:主要是实现抽象工厂中的多个抽象方法,完成具体产品的创建
  • 抽象产品:定义了产品的规范,描述了产品的主要特性和功能,抽象工厂模式有多个抽象产品
  • 具体产品:实现抽象产品所定义的接口,由具体工厂来创建,与具体工厂是多对一关系

🌰3.2实现

咖啡抽象类

public abstract class Coffee {
    /**
     * 获取咖啡类型
     * @return
     */
    public abstract  String getName();

    /**
     *加糖
     */
    public  void addSugar(){
        System.out.println("加糖");
    }
    /**
     *加奶
     */
    public void addMilk(){
        System.out.println("加奶");
    }
}

美式咖啡类

public class AmericanCoffee  extends Coffee{

@Override
    public String getName(){
        return "美式咖啡";
    }
}

拿铁咖啡类

public class LatteCoffee extends Coffee {

    @Override
    public String getName(){
        return "拿铁咖啡";
    }
}

甜品抽象类

public abstract class Dessert {
 //甜品抽象类

    public abstract void show();
}

抹茶慕斯类

public class MatchaMousse extends Dessert{

//抹茶慕斯类
    @Override
    public void show() {
        System.out.println("抹茶慕斯");
    }


}

提拉米苏类

public class Tiramisu  extends Dessert{
 //提拉米苏类
    @Override
    public void show() {
        System.out.println("提拉米苏");
    }
}

甜品工厂

public  interface DessertFactory {

    /**
     * 生产咖啡
     * @return
     */
    Coffee createCoffee();

    /**
     * 生产甜品
     * @return
     */
    Dessert createDessert();

}

美式风味甜品工厂类

public class AmericanDessertFactory  implements DessertFactory{
    /**
     *美式风味甜品工厂
     * 可以生产美式咖啡和抹茶慕斯
     */
    @Override
    public Coffee createCoffee() {
        return new AmericanCoffee();
    }

    @Override
    public Dessert createDessert() {
        return new MatchaMousse();
    }
}

意大利风味甜品工厂类

public class ItalyDessertFactory implements DessertFactory {

    /**
     *意大利风味甜品工厂
     * 可以生产拿铁咖啡和提拉米苏
     */
    @Override
    public Coffee createCoffee() {
        return new LatteCoffee();
    }

    @Override
    public Dessert createDessert() {
        return new Tiramisu();
    }
}

测试类

public class Test {
    public static void main(String[] args) {
        //ItalyDessertFactory factory = new ItalyDessertFactory();
           AmericanDessertFactory factory = new AmericanDessertFactory();
 
          Coffee coffee = factory.createCoffee()
        Dessert dessert = factory.createDessert();

        System.out.println(coffee.getName());

        dessert.show();

    }
}

类图

image.png

由类图可见,抽象工厂不再是一个具体工厂对应一个产品,而是一个具体工厂对应一个产品族。如果需要增加一个产品族只需加对应的工厂类,符合”开闭原则“

🍉3.3优缺点

优点:

在工厂方法的基础上减少了部分对象的创建,适合于每次只使用同一产品族的对象这类应用场景

缺点:

当产品族中需要增加一个产品时,所有工厂都要修改

🍋4.模式扩展

配置文件+简单工厂

通过工厂模式+配置文件的方式解除工厂对象和产品对象的耦合。在工厂类中加载配置文件的全类名,通过反射创建对象并存储在容器中,如果需要直接从容器中获取(Spring IOC原理)

🌰4.1实现

1.定义配置文件

american = com.xue.config_factory.AmericanCoffee
latten = com.xue.config_factory.LatteCoffee

2.改进工厂类

public class CoffeeFactory {

    /**
     * 加载配置文件,获取配置文件中配置的全类名,并创建该类的对象进行存储
     */

    //1.定义容器对象存储咖啡对象
    private static HashMap<String, Coffee> map = new HashMap<>();


    //2.加载配置文件
    static {
        //创建 Properties对象
        Properties properties = new Properties();
        //调用properties对象中的load方法进行配置文件的加载
        InputStream is = CoffeeFactory.class.getClassLoader().getResourceAsStream("bean.properties");
        try {
            properties.load(is);
            //从properties中获取全类名
            Set<Object> keys = properties.keySet();

            for (Object key : keys) {
                String className = properties.getProperty((String) key);
                //通过反射创建对象
                Class class1 = Class.forName(className);
                Coffee coffee = (Coffee) class1.newInstance();

                //将名称和对象存储在容器中
                map.put((String) key,coffee);

            }
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    //根据名称获取对象
    public static Coffee createCoffee(String name) {


        return map.get(name);
    }
}

静态成员变量用来存储创建的对象(键存储的是名称,值存储的是对应的对象),而读取配置文件和创建对象写在静态代码块中只需要执行一次

测试类

public class Test {
    public static void main(String[] args) {
        Coffee coffee = CoffeeFactory.createCoffee("american");
        System.out.println(coffee.getName());

        System.out.println("------------");

        Coffee latten = CoffeeFactory.createCoffee("latten");
        System.out.println(latten.getName());

    }

}

成功!!!

image.png

相关文章
|
28天前
|
设计模式 消息中间件 搜索推荐
Java 设计模式——观察者模式:从优衣库不使用新疆棉事件看系统的动态响应
【11月更文挑战第17天】观察者模式是一种行为设计模式,定义了一对多的依赖关系,使多个观察者对象能直接监听并响应某一主题对象的状态变化。本文介绍了观察者模式的基本概念、商业系统中的应用实例,如优衣库事件中各相关方的动态响应,以及模式的优势和实际系统设计中的应用建议,包括事件驱动架构和消息队列的使用。
|
1月前
|
设计模式 Java 数据库连接
Java编程中的设计模式:单例模式的深度剖析
【10月更文挑战第41天】本文深入探讨了Java中广泛使用的单例设计模式,旨在通过简明扼要的语言和实际示例,帮助读者理解其核心原理和应用。文章将介绍单例模式的重要性、实现方式以及在实际应用中如何优雅地处理多线程问题。
39 4
|
2月前
|
设计模式 Java 程序员
[Java]23种设计模式
本文介绍了设计模式的概念及其七大原则,强调了设计模式在提高代码重用性、可读性、可扩展性和可靠性方面的作用。文章还简要概述了23种设计模式,并提供了进一步学习的资源链接。
54 0
[Java]23种设计模式
|
1月前
|
设计模式 JavaScript Java
Java设计模式:建造者模式详解
建造者模式是一种创建型设计模式,通过将复杂对象的构建过程与表示分离,使得相同的构建过程可以创建不同的表示。本文详细介绍了建造者模式的原理、背景、应用场景及实际Demo,帮助读者更好地理解和应用这一模式。
|
2月前
|
设计模式 监控 算法
Java设计模式梳理:行为型模式(策略,观察者等)
本文详细介绍了Java设计模式中的行为型模式,包括策略模式、观察者模式、责任链模式、模板方法模式和状态模式。通过具体示例代码,深入浅出地讲解了每种模式的应用场景与实现方式。例如,策略模式通过定义一系列算法让客户端在运行时选择所需算法;观察者模式则让多个观察者对象同时监听某一个主题对象,实现松耦合的消息传递机制。此外,还探讨了这些模式与实际开发中的联系,帮助读者更好地理解和应用设计模式,提升代码质量。
Java设计模式梳理:行为型模式(策略,观察者等)
|
3月前
|
存储 设计模式 安全
Java设计模式-备忘录模式(23)
Java设计模式-备忘录模式(23)
|
3月前
|
设计模式 存储 算法
Java设计模式-命令模式(16)
Java设计模式-命令模式(16)
|
3月前
|
设计模式 存储 缓存
Java设计模式 - 解释器模式(24)
Java设计模式 - 解释器模式(24)
|
3月前
|
设计模式 安全 Java
Java设计模式-迭代器模式(21)
Java设计模式-迭代器模式(21)
|
3月前
|
设计模式 缓存 监控
Java设计模式-责任链模式(17)
Java设计模式-责任链模式(17)