1.工厂模式

1.1 概述

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

常用的三种工厂模式

  • 简单工厂模式(不属于GOF的23种经典设计模式)

  • 工厂方法模式

  • 抽象工厂模式

1.2案例需求

需求:设计一个咖啡店点餐系统。

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

具体类的设计如下。

示例代码:

/*** 咖啡类*/
public abstract class Coffee {public abstract String getName();// 加糖public void addSugar(){System.out.println("加糖......");}// 加奶public void addMilk(){System.out.println("加奶......");}
}
/*** 美式咖啡*/
public class AmericanCoffee extends Coffee {@Overridepublic String getName() {return "美式咖啡";}
}
/*** 拿铁咖啡*/
public class LatteCoffee extends Coffee {@Overridepublic String getName() {return "拿铁咖啡";}
}
/*** 咖啡店*/
public class CoffeeStore {public Coffee orderCoffee(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("对不起,您所点的咖啡没有");}// 加配料coffee.addMilk();coffee.addSugar();return coffee;}
}
public class Client {public static void main(String[] args) {//创建咖啡店类CoffeeStore coffeeStore = new CoffeeStore();Coffee coffee = coffeeStore.orderCoffee("latte");System.out.println(coffee.getName());}
}

1.3简单工厂模式

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

1.3.1结构

简单工厂包含如下角色:

  • 抽象产品 :定义了产品的规范,描述了产品的主要特性和功能。

  • 具体产品 :实现或者继承抽象产品的子类

  • 具体工厂 :提供了创建产品的方法,调用者通过该方法来获取产品。

1.3.2 实现

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

工厂类代码:

/*** 简单工厂类*/
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;}
}
/*** 咖啡店*/
public class CoffeeStore {public Coffee orderCoffee(String type) {SimpleCoffeeFactory factory = new SimpleCoffeeFactory();// 调用生产咖啡的方法Coffee coffee = factory.createCoffee(type);// 加配料coffee.addMilk();coffee.addSugar();return coffee;}
}

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

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

1.3.3 优缺点

优点:

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

缺点:

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

1.3.4 扩展

静态工厂

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

public class SimpleCoffeeFactory {public static Coffee createCoffee(String type) {Coffee coffee = null;if("americano".equals(type)) {coffee = new AmericanoCoffee();} else if("latte".equals(type)) {coffee = new LatteCoffee();}return coffe;}
}

1.4工厂方法模式

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

1.4.1概念

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

1.4.2结构

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

  • 抽象工厂(Abstract Factory):提供了创建产品的接口,调用者通过它访问具体工厂的工厂方法来创建产品。

  • 具体工厂(ConcreteFactory):主要是实现抽象工厂中的抽象方法,完成具体产品的创建。

  • 抽象产品(Product):定义了产品的规范,描述了产品的主要特性和功能。

  • 具体产品(ConcreteProduct):实现了抽象产品角色所定义的接口,由具体工厂来创建,它同具体工厂之间一一对应。

1.4.3实现

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

抽象工厂:

/*** 咖啡工厂接口*/
public interface CoffeeFactory {// 创建咖啡对象的方法Coffee createCoffee();
}

具体工厂:

/*** 美式咖啡工厂*/
public class AmericanCoffeeFactory implements CoffeeFactory {@Overridepublic Coffee createCoffee() {return new AmericanCoffee();}
}/*** 拿铁咖啡工厂*/
public class LatteCoffeeFactory implements CoffeeFactory {@Overridepublic 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.addMilk();coffee.addSugar();return coffee;}
}
/*** 测试类*/
public class Client {public static void main(String[] args) {//创建咖啡店类CoffeeStore coffeeStore = new CoffeeStore();coffeeStore.setFactory(new AmericanCoffeeFactory());Coffee coffee = coffeeStore.orderCoffee();System.out.println(coffee.getName());}
}

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

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

1.4.4优缺点

优点:

  • 用户只需要知道具体工厂的名称就可得到所要的产品,无须知道产品的具体创建过程;

  • 在系统增加新的产品时只需要添加具体产品类和对应的具体工厂类,无须对原工厂进行任何修改,满足开闭原则;

缺点:

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

1.5抽象工厂模式

前面介绍的工厂方法模式中考虑的是一类产品的生产,如畜牧场只养动物、电视机厂只生产电视机、传智播客只培养计算机软件专业的学生等。

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

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

1.5.1概念

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

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

1.5.2结构

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

  • 抽象工厂(Abstract Factory):提供了创建产品的接口,它包含多个创建产品的方法,可以创建多个不同等级的产品。

  • 具体工厂(Concrete Factory):主要是实现抽象工厂中的多个抽象方法,完成具体产品的创建。

  • 抽象产品(Product):定义了产品的规范,描述了产品的主要特性和功能,抽象工厂模式有多个抽象产品。

  • 具体产品(ConcreteProduct):实现了抽象产品角色所定义的接口,由具体工厂来创建,它 同具体工厂之间是多对一的关系。

1.5.3 实现

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

代码如下

抽象工厂:

/*** 甜品工厂接口*/
public interface DessertFactory {// 生产咖啡Coffee createCoffee();// 生产甜品Dessert createDessert();
}

具体工厂:

/*** 意大利式风味甜品工厂*/
public class ItalyDessertFactory implements DessertFactory {@Overridepublic Coffee createCoffee() {return new LatteCoffee();}@Overridepublic Dessert createDessert() {return new Tiramisu();}
}/*** 美式风味甜品工厂*/
public class AmericanDessertFactory implements DessertFactory {@Overridepublic Coffee createCoffee() {return new AmericanCoffee();}@Overridepublic Dessert createDessert() {return new MatchaMousse();}
}
/*** 甜品抽象类*/
public abstract class Dessert {public abstract void show();
}
/*** 抹茶慕斯*/
public class MatchaMousse extends Dessert {@Overridepublic void show() {System.out.println("抹茶慕斯");}
}/*** 提拉米苏*/
public class Tiramisu extends Dessert {@Overridepublic void show() {System.out.println("提拉米苏");}
}
/*** 咖啡类*/
public abstract class Coffee {public abstract String getName();// 加糖public void addSugar(){System.out.println("加糖......");}// 加奶public void addMilk(){System.out.println("加奶......");}
}
/*** 美式咖啡*/
public class AmericanCoffee extends Coffee {@Overridepublic String getName() {return "美式咖啡";}
}/*** 拿铁咖啡*/
public class LatteCoffee extends Coffee {@Overridepublic String getName() {return "拿铁咖啡";}
}
/*** 测试类*/
public class Client {public static void main(String[] args) {// 想引用 意大利风格的咖啡和甜品ItalyDessertFactory factory = new ItalyDessertFactory();// 获取对应的拿铁咖啡和提拉米苏甜品Coffee coffee = factory.createCoffee();Dessert dessert = factory.createDessert();System.out.println(coffee.getName());dessert.show();}
}

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

1.5.4优缺点

优点:

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

缺点:

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

1.5.5使用场景

  • 当需要创建的对象是一系列相互关联或相互依赖的产品族时,如电器工厂中的电视机、洗衣机、空调等。

  • 系统中有多个产品族,但每次只使用其中的某一族产品。如有人只喜欢穿某一个品牌的衣服和鞋。

  • 系统中提供了产品的类库,且所有产品的接口相同,客户端不依赖产品实例的创建细节和内部结构。

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

1.6模式扩展

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

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

第一步:定义配置文件

为了演示方便,我们使用properties文件作为配置文件,名称为bean.properties

american=com.heima.design.factory.configfactory.AmericanCoffee
latte=com.heima.design.factory.configfactory.LatteCoffee

第二步:改进工厂类

/*** 工厂类*/
public class CoffeeFactory {// 加载配置文件,获取配置文件中配置的全雷鸣,并创建该类的对象进行存储// 1.定义容器对象存储咖啡对象private static HashMap<String, Coffee> map = new HashMap<>();// 2.加载配置文件,只需要加载一次static {try {// 2.1 创建Properties对象Properties properties = new Properties();// 2.2 调用 properties 对象中的 load 方法进行配置文件的加载InputStream inputStream = CoffeeFactory.class.getClassLoader().getResourceAsStream("bean.properties");properties.load(inputStream);// 从 properties 集合中获取全类名,并创建对象Set<Object> keys = properties.keySet();for (Object key : keys) {String className = properties.getProperty((String) key);// 通过反射 创建对象Class clazz = Class.forName(className);Coffee coffee = (Coffee) clazz.newInstance();// 将名称和对象存储到容器中map.put((String) key, coffee);}} catch (IOException | ClassNotFoundException | InstantiationException | IllegalAccessException e) {e.printStackTrace();}}// 根据名称获取对象public static Coffee createCoffee(String name) {return map.get(name);}
}

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

1.7源码解析-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()方法使用的是工厂模式;

设计模式(三)创建型模式-工厂模式相关推荐

  1. Java设计模式之创建型:原型模式

    一.什么是原型模式: 原型模式主要用于对象的创建,使用原型实例指定创建对象的种类,并通过拷贝这些原型创建新的对象.UML类图如下: 原型模式的核心是就是原型类 Prototype,Prototype ...

  2. Java设计模式之创建型:建造者模式

    一.什么是建造者模式: 建造者模式将复杂产品的创建步骤分解在在不同的方法中,使得创建过程更加清晰,从而更精确控制复杂对象的产生过程:通过隔离复杂对象的构建与使用,也就是将产品的创建与产品本身分离开来, ...

  3. 设计模式(20):创建型-抽象工厂模式(Abstract Factory)

    设计模式(Design pattern)是一套被反复使用.多数人知晓的.经过分类编目的.代码设计经验的总结.使用设计模式是为了可重用代码.让代码更容易被他人理解.保证代码可靠性. 毫无疑问,设计模式于 ...

  4. JavaScript 设计模式核⼼原理与应⽤实践 之 创建型:工厂模式·简单工厂——区分“变与不变”

    JavaScript 设计模式核⼼原理与应⽤实践 之 创建型:工厂模式·简单工厂--区分"变与不变" 先来说说构造器 在介绍工厂模式之前,为了辅助大家的理解,我想先在这儿给大家介绍 ...

  5. JavaScript 设计模式核⼼原理与应⽤实践 之 创建型:工厂模式·抽象工厂——理解“开放封闭”

    JavaScript 设计模式核⼼原理与应⽤实践 之 创建型:工厂模式·抽象工厂--理解"开放封闭" 一个不简单的简单工厂引发的命案 在实际的业务中,我们往往面对的复杂度并非数个类 ...

  6. 【创建型】工厂模式(Factory Pattern)详解

    工厂模式分为简单工厂.工厂方法.抽象工厂模式. 这一篇说一下简单工厂.工厂方法: 简单工厂 :用来生产同一等级结构中的任意产品.(不支持拓展增加产品) 工厂方法 :用来生产同一等级结构中的固定产品.( ...

  7. Java设计模式之行为型:解释器模式

    一.什么是解释器模式:         解释器模式,就是定义语言的文法,并建立一个解释器来解释该语言中的句子,通过构建解释器,解决某一频繁发生的特定类型问题实例. 这里我们将语言理解成使用规定格式和语 ...

  8. Java设计模式之行为型:访问者模式

    背景: 去医院看病时,医生会给你一个处方单要你去拿药,拿药我们可以分为两步走: (1)去柜台交钱,划价人员会根据处方单上的药进行划价,交钱. (2)去药房拿药,药房工作者同样根据处方单给你相对应的药. ...

  9. Java设计模式之行为型:状态模式

    背景: 介绍状态模式前,我们先看这样一个实例:公司力排万难终于获得某个酒店的系统开发项目,并且最终落到了你的头上.下图是他们系统的主要工作: 当第一眼看到这个系统时你就看出这是一个状态图,每个框都代表 ...

  10. Java设计模式之行为型:备忘录模式

    在开发过程中,经常需要保存对象的中间状态,当需要的时候,可以恢复到这个状态.比如,在编程时假如编写失误,例如不小心误删除了几行代码,我们希望返回删除前的状态,便可以使用 Ctrl+Z 来进行返回,这时 ...

最新文章

  1. 单片机与微处理器和微型计算机的关系,微处理器、CPU、微处理机、微机、单片机它们之间有何区别?...
  2. [Linux]C语言Linux系统编程创建进程
  3. 怎么在服务器上加端口映射,Windows服务器中命令行添加端口映射
  4. jitter 如何优化网络_网络推广如何做好网站SEO优化
  5. oracle 提取连续数字,oracle 得到连续不重复的数字序列
  6. [语言]Webassembly
  7. 华为员工利用Bug越权访问机密卖给第三方,获利1.6万元,被判有期徒刑一年...
  8. C语言解决约瑟夫环问题 详细注释
  9. 向已经存在的Kubernetes集群中添加新的worker节点
  10. 药方的量化方法笔记(学习与尝试):第一回 中药材数据库的建立 归经的统计 药方归经和功效的量化拆解
  11. ros重置后地址_RouterOS中BGP软重新配置 - ROS软路由论坛 - ROS教程 - RouterOS - ROS之家 - ROS脚本生成器 - Powered by Discuz!...
  12. 毕啸南专栏 | 对话阿里王坚:数据价值是新大陆,新技术将重构互联网
  13. 【Unity3D入门教程】Unity3D简介、安装和程序发布
  14. Element组件 Drawer 抽屉的关闭问题
  15. 喜迎B+轮融资,ThingJS母公司优锘科技成为新基建的一匹黑马
  16. wordpress企业主题门户类企业类多用途主题
  17. vue click 跳转外部链接
  18. pathon真的那么好用吗?
  19. 计算机无法对NAS硬盘操作,群晖NAS联机失败不要慌,我用经验告诉你,这样做就能完美解决...
  20. vue-element-admin 快捷导航(标签栏导航)切换不刷新问题

热门文章

  1. Stata: 蒙特卡洛模拟(Monte Carlo Simulation)没那么神秘
  2. C++费解的开关(递推,位运算)
  3. C# 实现 微软WebRequestMethods.Ftp类中的FTP操作功能
  4. linux下的rar命令,Linux下的压缩解压命令
  5. autojs脚本引擎编写的百度网盘BDUSS获取代码
  6. 遥感分类图的碎斑去除(python)
  7. Android OpenGL开发学习(二)手把手教你实现抖音分屏相机
  8. WLAN划分与子网划分的区别
  9. RK3568平台开发系列讲解(设备驱动篇)V4L2程序实现流程
  10. 400元左右的蓝牙耳机哪个牌子好?400以内的蓝牙耳机推荐