设计模式—工厂设计模式

article/2025/8/25 0:09:09

工厂设计模式

    • 1.概述
    • 2.简单工厂模式
      • 2.1 结构
      • 2.2 实现
      • 2.3 优缺点
      • 2.4 扩展
    • 3.工厂方法模式
      • 3.1 概述
      • 3.2 结构
      • 3.3 实现
      • 3.4 优缺点
    • 4.抽象工厂模式
      • 4.1 概述
      • 4.2 结构
      • 4.3 实现
      • 4.4 优缺点
      • 4.5 应用场景
    • 5.扩展模式
      • 5.1 第一步:定义配置文件
      • 5.2 改写工厂类
    • 6.JDK源码解析—Collection.iterator方法

1.概述

在java中,万物皆对象,这些对象都需要创建,如果创建的时候直接new该对象,就会对该对象耦合严重,假如我们要更换对象,所有new对象的地方都需要修改一遍,这显然违背了软件设计的开闭原则。如果我们使用工厂来生产对象,我们就只和工厂打交道就可以了,彻底和对象解耦,如果要更换对象,直接在工厂里更换该对象即可,达到了与对象解耦的目的;所以说,工厂模式最大的优点就是:解耦

例子

需求:设计一个咖啡店点餐系统。
设计一个咖啡类(Coffee),并定义其两个子类(美式咖啡【AmericanCoffee】和拿铁咖啡【LatteCoffee】);再设计一个咖啡店类(CoffeeStore),咖啡店具有点咖啡的功能。

具体类的设计如下:

在这里插入图片描述

2.简单工厂模式

简单工厂不是一种设计模式,反而比较像是一种编程习惯。

2.1 结构

简单工厂包含如下角色:

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

2.2 实现

现在使用简单工厂对上面案例进行改进,类图如下:

在这里插入图片描述

抽象类 Coffee .java

package com.lxg.pattern.factory.factory_simple;/*** 咖啡类*/
public abstract class Coffee {public abstract String getName();public void addSugar(){System.out.println("加糖");}public void addMilk(){System.out.println("加奶");}
}

具体实现类1:AmericanCoffee.java

package com.lxg.pattern.factory.factory_simple;/*** 美式咖啡*/
public class AmericanCoffee extends Coffee {public String getName() {return "美式咖啡";}
}

具体实现类2:

package com.lxg.pattern.factory.factory_simple;/*** 拿铁咖啡*/
public class LatteCoffee extends Coffee {public String getName() {return "拿铁咖啡";}
}

简单工厂类

package com.lxg.pattern.factory.factory_simple;/*** 简单咖啡工厂类,用来生产咖啡** 简单工厂处理创建对象的细节,有了此简单工厂对象,在CoffeeStore对象中如果需要Coffee对象,只需要在工厂类* 中获取就行。这样解除了Coffee类和CoffeeStore类的耦合,但是这样也产生了新的耦合,即工厂对象和CoffeeStore* 的耦合以及工厂对象跟Coffee类的耦合。** 有了工厂对象,如果后期增加了更多的咖啡,则势必对工厂对象进行改造,违反了开闭原则。** 简单工厂:* 优点:* 封装了创建对象的细节,可以通过参数直接获取对象,把对象的创建和业务逻辑层分开,这样避免以后修改客户端代码,* 如果要实现新的产品,直接修改工厂类就行了,而不需要在业务代码中修改,这样就降低了客户代码修改的可能性,更加* 容易扩展。** 缺点:* 增加新的产品的时候,还是需要修改工厂类,违背了设计原则的开闭原则。**/
public class SimpleCoffeeFactory {public Coffee createCoffee(String type){//声明Coffee类型的变量,根据不同的类型创建不同的coffee子类对象Coffee coffee = null;if("american".equals(type)){coffee = new AmericanCoffee();}else if("latte".equals(type)){coffee = new LatteCoffee();}else{throw new RuntimeException("对不起,您点的咖啡没有");}return coffee;}
}

分析

工厂(factory)处理创建对象的细节,一旦有了SimpleCoffeeFactory,CoffeeStore类中的orderCoffee()就变成此对象的客户,后期如果需要Coffee对象直接从工厂中获取即可。这样也就解除了和Coffee实现类的耦合,同时又产生了新的耦合,CoffeeStore对象和SimpleCoffeeFactory工厂对象的耦合,工厂对象和商品对象的耦合。

后期如果再加新品种的咖啡,我们势必要需求修改SimpleCoffeeFactory的代码,违反了开闭原则。工厂类的客户端可能有很多,比如创建美团外卖等,这样只需要修改工厂类的代码,省去其他的修改操作。

2.3 优缺点

优点:

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

缺点:

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

2.4 扩展

静态工厂

在开发中也有一部分人将工厂类中的创建对象的功能定义为静态的,这个就是静态工厂模式,它也不是23种设计模式中的。代码如下:

package com.lxg.pattern.factory.factory_static;/*** 静态工厂方法模式*/
public class SimpleCoffeeFactory {public static Coffee createCoffee(String type){//声明Coffee类型的变量,根据不同的类型创建不同的coffee子类对象Coffee coffee = null;if("american".equals(type)){coffee = new AmericanCoffee();}else if("latte".equals(type)){coffee = new LatteCoffee();}else{throw new RuntimeException("对不起,您点的咖啡没有");}return coffee;}
}

3.工厂方法模式

针对简单工厂中的缺点,使用工厂方法模式就可以完美的解决,完全遵循开闭原则。

3.1 概述

定义一个用于创建对象的接口,让子类决定实例化哪个产品类对象。工厂方法使一个产品类的实例化延迟到其工厂的子类。

3.2 结构

工厂方法模式的主要角色:

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

3.3 实现

使用工厂方法模式对上例进行改进,类图如下:

工厂方法模式

抽象工厂类

package com.lxg.pattern.factory.factory_method;/*** 抽象工厂:如果增加新的种类咖啡,只需要增加一个这个接口的实现类就行。** 优点:* - 用户只需要知道具体的工厂名字就能得到所需要的产品,无需知道产品的具体创建过程* - 在系统增加新的产品时只需要添加具体产品类对应的具体工厂类,无需对原工厂类进行任何修改,满足开闭原则** 缺点:* - 每增加一个产品就要增加一个具体产品类和一个对应的具体工厂类,这增加了系统的复杂性*/
public interface CoffeeFactory {//创建咖啡对象的方法Coffee createCoffee();
}

具体工厂类

package com.lxg.pattern.factory.factory_method;/*** 美式咖啡工厂对象,专门用来生产美式咖啡的*/
public class AmericanCoffeeFactory implements CoffeeFactory{public Coffee createCoffee() {return new AmericanCoffee();}
}
package com.lxg.pattern.factory.factory_method;/*** 拿铁咖啡工厂,专门用来生产拿铁咖啡*/
public class LatteCoffeeFactory implements CoffeeFactory{public Coffee createCoffee() {return new LatteCoffee();}
}

咖啡店类

package com.lxg.pattern.factory.factory_method;public class CoffeeStore {private CoffeeFactory factory;public void setFactory(CoffeeFactory factory){this.factory = factory;}public Coffee orderCoffee(){Coffee coffee = factory.createCoffee();coffee.addMilk();coffee.addSugar();return coffee;}
}

分析

从以上的编写的代码可以看到,要增加产品类时也要相应地增加工厂类,不需要修改工厂类的代码了,这样就解决了简单工厂模式的缺点。

工厂方法模式是简单工厂模式的进一步抽象。由于使用了多态性,工厂方法模式保持了简单工厂模式的优点,而且克服了它的缺点。

3.4 优缺点

优点:

  • 用户只需要知道具体工厂的名称就可得到所要的产品,无须知道产品的具体创建过程;
  • 在系统增加新的产品时只需要添加具体产品类和对应的具体工厂类,无须对原工厂进行任何修改,满足开闭原则;

缺点:

  • 每增加一个产品就要增加一个具体产品类和一个对应的具体工厂类,这增加了系统的复杂度。

4.抽象工厂模式

前面介绍的工厂方法模式中考虑的是一类产品的生产,如畜牧场只养动物、电视机厂只生产电视机等

这些工厂只生产同种类产品,同种类产品称为同等级产品,也就是说:工厂方法模式只考虑生产同等级的产品,但是在现实生活中许多工厂是综合型的工厂,能生产多等级(种类) 的产品,如电器厂既生产电视机又生产洗衣机或空调,大学既有软件专业又有生物专业等。

抽象工厂模式将考虑多等级产品的生产,将同一个具体工厂所生产的位于不同等级的一组产品称为一个产品族,下图所示横轴是产品等级,也就是同一类产品;纵轴是产品族,也就是同一品牌的产品,同一品牌的产品产自同一个工厂。

在这里插入图片描述
在这里插入图片描述

4.1 概述

是一种为访问类提供一个创建一组相关或相互依赖对象的接口,且访问类无须指定所要产品的具体类就能得到同族的不同等级的产品的模式结构。

抽象工厂模式是工厂方法模式的升级版本,工厂方法模式只生产一个等级的产品,而抽象工厂模式可生产多个等级的产品。

4.2 结构

抽象工厂模式的主要角色如下:

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

4.3 实现

现咖啡店业务发生改变,不仅要生产咖啡还要生产甜点,如提拉米苏、抹茶慕斯等,要是按照工厂方法模式,需要定义提拉米苏类、抹茶慕斯类、提拉米苏工厂、抹茶慕斯工厂、甜点工厂类,很容易发生类爆炸情况。其中拿铁咖啡、美式咖啡是一个产品等级,都是咖啡;提拉米苏、抹茶慕斯也是一个产品等级;拿铁咖啡和提拉米苏是同一产品族(也就是都属于意大利风味),美式咖啡和抹茶慕斯是同一产品族(也就是都属于美式风味)。所以这个案例可以使用抽象工厂模式实现。类图如下:

在这里插入图片描述

抽象工厂

package com.lxg.pattern.factory.factory_abstract;public interface DessertFactory {//生产咖啡的功能Coffee createCoffee();//生产甜品的功能Dessert createDessert();
}

具体工厂

package com.lxg.pattern.factory.factory_abstract;/*** 没事风味的甜品工厂,可以生产美式咖啡和抹茶慕斯*/
public class AmericanDessertFactory implements DessertFactory{public Coffee createCoffee() {return new AmericanCoffee();}public Dessert createDessert() {return new MatchaMousse();}
}
package com.lxg.pattern.factory.factory_abstract;/*** 意大利风味的甜品工厂,能生产拿铁和提拉米苏*/
public class ItalyDessertFactory implements DessertFactory{public Coffee createCoffee() {return new LatteCoffee();}public Dessert createDessert() {return new Tiramisu();}
}

分析

如果要加同一个产品族的话,只需要再加一个对应的工厂类即可,不需要修改其他的类。

4.4 优缺点

优点:

当一个产品族中的多个对象被设计成一起工作时,它能保证客户端始终只使用同一个产品族中的对象。

缺点:

当产品族中需要增加一个新的产品时,所有的工厂类都需要进行修改。

4.5 应用场景

  • 当需要创建的对象是一系列相互关联或相互依赖的产品族时,如电器工厂中的电视机、洗衣机、空调等。
  • 系统中有多个产品族,但每次只使用其中的某一族产品。如有人只喜欢穿某一个品牌的衣服和鞋。
  • 系统中提供了产品的类库,且所有产品的接口相同,客户端不依赖产品实例的创建细节和内部结构。

如:输入法换皮肤,一整套一起换。生成不同操作系统的程序。

5.扩展模式

简单工厂+配置文件解除耦合

可以通过工厂模式+配置文件的方式解除工厂对象和产品对象的耦合。在工厂类中加载配置文件中的全类名,并创建对象进行存储,客户端如果需要对象,直接进行获取即可。

5.1 第一步:定义配置文件

定义bean.properties文件,指定所需要创建类的全限定类名

american=com.lxg.pattern.factory.factory_config.AmericanCoffee
latte=com.lxg.pattern.factory.factory_config.LatteCoffee

5.2 改写工厂类

package com.lxg.pattern.factory.factory_config;import java.io.InputStream;
import java.util.HashMap;
import java.util.Properties;
import java.util.Set;public class CoffeeFactory {//加载配置文件,获取配置文件中配置的全类名,并创建该类的对象进行存储//定义容器对象存储咖啡对象private static HashMap<String,Coffee> map = new HashMap<String, Coffee>();//加载配置文件,只需要加载一次static {Properties p = new Properties();InputStream in = CoffeeFactory.class.getClassLoader().getResourceAsStream("bean.properties");try {p.load(in);//从p集合中获取全限定类名并创建对象Set<Object> keys = p.keySet();for (Object key:keys) {String className = p.getProperty((String) key);//通过反射技术创建对象Class clazz = Class.forName(className);Coffee coffee = (Coffee) clazz.newInstance();//将名称和对象存储到容器中map.put((String)key,coffee);}} catch (Exception e) {e.printStackTrace();}}public static Coffee createCoffee(String name){return  map.get(name);}
}

分析

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

6.JDK源码解析—Collection.iterator方法

public class Demo {public static void main(String[] args) {List<String> list = new ArrayList<>();list.add("令狐冲");list.add("风清扬");list.add("任我行");//获取迭代器对象Iterator<String> it = list.iterator();//使用迭代器遍历while(it.hasNext()) {String ele = it.next();System.out.println(ele);}}
}

对上面的代码大家应该很熟,使用迭代器遍历集合,获取集合中的元素。而单列集合获取迭代器的方法就使用到了工厂方法模式。我们看通过类图看看结构:

在这里插入图片描述

Collection接口是抽象工厂类,ArrayList是具体的工厂类;Iterator接口是抽象商品类,ArrayList类中的Iter内部类是具体的商品类。在具体的工厂类中iterator()方法创建具体的商品类的对象。

另:
1,DateForamt类中的getInstance()方法使用的是工厂模式;
2,Calendar类中的getInstance()方法使用的是工厂模式;


http://chatgpt.dhexx.cn/article/xyOmKo4O.shtml

相关文章

工厂设计模式 - 详解

介绍 工厂模式是Java中最常用的设计模式之一&#xff0c;这种类型的设计模式数据创建型模式。 主要就是负责创建对象的&#xff0c;当我们使用new创建对象的时候会导致对该对象的耦合性很严重&#xff0c;当我们需要修改的时候需要对所有new的对象进行修改&#xff0c;违反了软…

【iOS】—— 工厂设计模式

工厂设计模式 文章目录 工厂设计模式设计模式概念设计模式七大准则开闭原则单⼀职责原则里氏替换原则依赖倒转原则接口隔离原则迪米特法则合成复用原则 类族模式简单工厂模式优点缺点主要作用示例文件分类实现效果&#xff1a; 工厂方法模式优点缺点主要作用&#xff1a;示例&a…

【设计模式】工厂模式(Factory Pattern)

1. 概述 工厂模式&#xff08;Factory Pattern&#xff09;是最常用的设计模式之一&#xff0c;它属于创建类型的设计模式。它提供了一种创建对象的最佳方式&#xff0c;在工厂模式中&#xff0c;我们在创建对象时不会对客户端暴露创建逻辑&#xff0c;并且是通过一个共同的接…

设计模式之工厂模式,史上最强,不服来辩!

设计模式是对大家实际工作中写的各种代码进行高层次抽象的总结&#xff0c;如果设计模式没学会&#xff0c;抽象能力肯定就不会太强。常见的设计模式有 23 种&#xff0c;今天我们只聊最简单的工厂模式。 工厂模式是属于创建型模式的&#xff0c;通过工厂获取一个一个的新对象。…

工厂设计模式

1、概念 工厂模式分三种&#xff1a;简单工厂模式、工厂方法模式、抽象工厂模式 简单工厂模式(Simple Factory Pattern)&#xff1a;属于类的创新型模式&#xff0c;又叫静态工厂方法模式(Static FactoryMethod Pattern),是通过专门定义一个类来负责创建其他类的实例&#xf…

设计模式之工厂模式(factory pattern)

工厂顾名思义就是创建产品,根据产品是具体产品还是具体工厂可分为简单工厂模式和工厂方法模式,根据工厂的抽象程度可分为工厂方法模式和抽象工厂模式。该模式用于封装和管理对象的创建,是一种创建型模式。本文从一个具体的例子逐步深入分析,来体会三种工厂模式的应用场景和…

sqlserver2008的SSMS连接sqlserver2016的时候提示‘索引超出了数组界限。’

解决&#xff1a;打sp3补丁。 http://www.microsoft.com/en-us/download/details.aspx?id44271

Matlab报错 :“位置 x 处的索引超出数组边界”

经常遇到“位置 x 索引超出数组边界”的报错&#xff0c;今天突然想到一个以前一直没有太留意的奇葩问题&#xff1a;这个报错里“位置x”指的是哪里&#xff1f;为什么一会是“位置3”&#xff0c;一会是“位置1”&#xff0c;有什么神秘的规矩吗&#xff1f; 善用搜索没发现…

.net reflector 反编译失败 索引超出了数组界限问题处理方法

.net reflector 反编译失败 索引超出了数组界限问题处理方法 时间&#xff1a;9个月前 作者&#xff1a;庞顺龙 浏览&#xff1a;177 [站内原创&#xff0c;转载请注明出处] 标签&#xff1a; Reflector .net reflector 反编译失败 索引超出了数组界限问题处理方法 de…

matlab索引超出数组边界且不提示数组边界的一种处理办法

问题如下。 相关代码如下&#xff1a; 问题原因如下&#xff1a; 将excel表另存为txt时选择的保存类型是Unicode文本(*.txt)。 处理办法。 将Unicode文本(*.txt)换成文本文件&#xff08;制表符分隔&#xff09;(*.txt)。 问题的表现如下&#xff1a; 利用matlab工作区发现…

最短路径算法详细介绍

据 Drew 所知最短路经算法现在重要的应用有计算机网络路由算法&#xff0c;机器人探路&#xff0c;交通路线导航&#xff0c;人工智能&#xff0c;游戏设计等等。美国火星探测器核心的寻路算法就是采用的D*&#xff08;D Star&#xff09;算法。 最短路经计算分静态最短路计算和…

[算法]-最短路径算法总结

Dijkstra最短路径算法 按路径长度的递增次序&#xff0c;逐步产生最短路径的贪心算法 基本思想&#xff1a;首先求出长度最短的一条最短路径,再参照它求出长度次短的一条最短路径&#xff0c;依次类推&#xff0c;直到从顶点v 到其它各顶点的最短路径全部求出为止。 时间复杂…

c++实现最短路径算法(Dijkstra算法)

0简介 Dijkstra算法是一种计算某一顶点到其余顶点最短路径的算法。它采用了贪心的策略&#xff0c;实现时使用了广度优先。这个算法常学常忘&#xff0c;因此写篇博客彻底解决它。 1计算步骤介绍 Dijkstra的计算步骤不难&#xff0c;如下 (1)设置一个集合表示已经标记过的节…

最短路径算法——dijkstra

dijkstra 前提&#xff1a;在一张图里&#xff0c;不能有权值为负数的边。 给你一个出发点&#xff0c;求出发点到所有结点的最短距离是多少&#xff1f;如果无法到达某个点&#xff0c;则到这个点的距离是正无穷&#xff08;一般出现在有向图里面&#xff09;。 举个&#…

最短路径算法详解

前言&#xff1a; 最短路径算法&#xff1a;用于计算一个节点到其他所有节点的最短路径。主要特点是以起始点为中心向外层层扩展&#xff0c;直到扩展到终点为止。 例子&#xff1a; 暑期&#xff0c;你想要出门去旅游&#xff0c;但是在你出发之前&#xff0c;你想知道任意…

基于java最短路径算法的简单实现

一、我们先画一个可达路径图表&#xff1a; -1表示不可直达&#xff0c;0表示自己&#xff0c;其他整数表示长度或者权重 ABCDA012-1B-1015C-1-101D-1-1-10 用图形可表示为&#xff1a; 二、算法思路 1、先列出所有保存所有节点到可达节点的路径二维表 2、逐步寻找从起始节点到…

最短路径算法及应用

乘汽车旅行的人总希望找出到目的地的尽可能的短的行程。如果有一张地图并在图上标出每对十字路口之间的距离&#xff0c;如何找出这一最短行程&#xff1f;  一种可能的方法就是枚举出所有路径&#xff0c;并计算出每条路径的长度&#xff0c;然后选择最短的一条。那么我们很…

关于最短路径算法的理解

“最短路径算法:Dijkstra算法&#xff0c;Bellman-Ford算法&#xff0c;Floyd算法和SPFA算法等。​从某顶点出发&#xff0c;沿图的边到达另一顶点所经过的路径中&#xff0c;各边上权值之和最小的一条路径叫做最短路径。” 我们解决最短路径问题&#xff0c;常用的是Dijkstra…

最短路径算法-----Dijkstra迪杰斯特拉算法

最近巩固一下算法&#xff0c;提高自己内力&#xff0c;网上看到查看到这篇介绍很详细的《Dijkstra迪杰斯特拉算法》&#xff0c;在这里转载记录一下。 1 前言 本章介绍迪杰斯特拉算法。和以往一样&#xff0c;本文会先对迪杰斯特拉算法的理论论知识进行介绍&#xff0c;然后给…

Dijkstra 最短路径算法 Python 实现

原文链接 问题描述 使用 Dijkstra 算法求图中的任意顶点到其它顶点的最短路径&#xff08;求出需要经过那些点以及最短距离&#xff09;。 以下图为例&#xff1a; 算法思想 可以使用二维数组来存储顶点之间边的关系 首先需要用一个一维数组 dis 来存储 初始顶点到其余各个顶…