Java常用设计模式-简单工厂和抽象工厂模式

简介: Java常用设计模式-简单工厂和抽象工厂模式

众所周知,现实生活中工厂是用于生产各种产品的地方,而在程序设计中,工厂为生产实例的地方。我们平时在Java创建对象时都需要通过new来创建一个类型的实例。

不过在一个复杂的系统里面,创建的对象也需要根据外界需求来确定。

我们从一个例子开始:一个水果店供应水果,有西瓜、橙子、苹果,水果店会根据顾客的喜好来供应不同的水果。

我们首先先新建顾客口味的枚举类顾客类,表示需求参数。

顾客口味枚举:

packagecom.gitee.swsk33.simplefactory.param;
/*** 客户口味参数*/publicenumFlavor {
/*** 甜*/SWEET,
/*** 酸甜*/SWEET_ACID,
/*** 酸*/ACID}

顾客:

packagecom.gitee.swsk33.simplefactory.param;
importlombok.Getter;
importlombok.Setter;
/*** 客户类*/@Getter@SetterpublicclassCustomer {
/*** 客户口味*/privateFlavorflavor;
}

然后新建各个水果。

西瓜:

packagecom.gitee.swsk33.simplefactory.model.impl;
importlombok.Getter;
importlombok.Setter;
/*** 西瓜类*/@Getter@SetterpublicclassWatermelon {
/*** 是否保熟*/privatebooleancooked;
}

苹果:

packagecom.gitee.swsk33.simplefactory.model.impl;
importlombok.Getter;
importlombok.Setter;
/*** 苹果类*/@Getter@SetterpublicclassApple {
/*** 甜度*/privateintsweetness;
}

橙子:

packagecom.gitee.swsk33.simplefactory.model.impl;
importlombok.Getter;
importlombok.Setter;
/*** 橙子类*/@Getter@SetterpublicclassOrange {
/*** 酸度*/privateintacidity;
}

好了,现在水果店要卖水果了,建立水果店如下:

// 来了个顾客Customercustomer=newCustomer();
customer.setFlavor(Flavor.SWEET_ACID);
if (customer.getFlavor() ==Flavor.SWEET) {
Watermelonwatermelon=newWatermelon();
// ...} elseif (customer.getFlavor() ==Flavor.SWEET_ACID) {
Appleapple=newApple();
// ...} elseif (customer.getFlavor() ==Flavor.ACID) {
Orangeorange=newOrange();
// ...}

目前整体结构如下:

网络异常,图片无法展示
|

看起来是个简单的条件判断,但是这样有两个主要问题:

  1. 代码重复:其余水果店或者其余卖水果的商店也要这么写
  2. 耦合度高:水果类被很大程度地包含至水果店类,只要修改水果类,商店类也要被全部修改

耦合度:某个类的代码包含了其它类的逻辑,包含的多耦合度就高,也容易互相影响。

因此我们需要使用工厂模式来解决这个问题。

1,简单工厂模式

实现简单工厂主要分为两大步:

  1. 把我们具体的产品资源(西瓜、橙子)抽象为一种事物(水果)
  2. 新建工厂类,并把生产具体实例(具体的西瓜橙子等等水果)的工作交给工厂

在上述“水果供应”的例子中,我们可以将各种水果抽象为一个接口或者是抽象类,然后新建一个水果工厂类生产它们,做类图如下:

网络异常,图片无法展示
|

我们来改造上述的系统。首先新建水果抽象类,并让每个水果都继承这个水果抽象类。

packagecom.gitee.swsk33.simplefactory.model;
importlombok.Getter;
importlombok.Setter;
/*** 水果抽象类*/@Getter@SetterpublicabstractclassFruit {
/*** 水果名*/privateStringname;
}

然后新建水果工厂,这就是简单工厂模式最核心的部分了:

packagecom.gitee.swsk33.simplefactory.factory;
importcom.gitee.swsk33.simplefactory.model.*;
importcom.gitee.swsk33.simplefactory.model.impl.Apple;
importcom.gitee.swsk33.simplefactory.param.Customer;
importcom.gitee.swsk33.simplefactory.model.impl.Orange;
importcom.gitee.swsk33.simplefactory.model.impl.Watermelon;
/*** 水果工厂类*/publicclassFruitFactory {
/*** 根据客户的口味生产相应的水果** @param customer 客户实例* @return 对应的水果*/publicstaticFruitget(Customercustomer) {
Fruitfruit=null;
switch (customer.getFlavor()) {
caseSWEET:
Watermelonwatermelon=newWatermelon();
watermelon.setName("西瓜");
watermelon.setCooked(true);
fruit=watermelon;
break;
caseSWEET_ACID:
Appleapple=newApple();
apple.setName("苹果");
apple.setSweetness(100);
fruit=apple;
break;
caseACID:
Orangeorange=newOrange();
orange.setName("橙子");
orange.setAcidity(12);
fruit=orange;
break;
      }
returnfruit;
   }
}

然后在水果店类中,我们来试一下:

packagecom.gitee.swsk33.simplefactory;
importcom.gitee.swsk33.simplefactory.factory.FruitFactory;
importcom.gitee.swsk33.simplefactory.model.Fruit;
importcom.gitee.swsk33.simplefactory.param.Customer;
importcom.gitee.swsk33.simplefactory.param.Flavor;
/*** 水果店类,卖水果*/publicclassStore {
publicstaticvoidmain(String[] args) {
// 来了个顾客Customercustomer=newCustomer();
customer.setFlavor(Flavor.SWEET_ACID);
// 从工厂得到水果FruitgetFruit=FruitFactory.get(customer);
System.out.println("得到水果:"+getFruit.getName());
   }
}

网络异常,图片无法展示
|

可见简单工厂模式也是根据外部需求来创建对象,只不过现在职责更加明确,创建对象的任务以及需求判断,全部交给了工厂。水果店只需告诉工厂客人要吃什么,并从工厂取得相应水果给客人,而不需要知道具体要什么水果

由于所有的水果都有同一个属性“水果名”,因此水果使用抽象类表示,抽象类放不同水果共有的属性。否则可以使用接口表示水果,根据实际情况而定。

2,抽象工厂模式

上面简单工厂模式只是涉及到一类产品的生产,那多类产品怎么办呢?

譬如说水果店现在开始卖果茶了,我们还需要新建一个果茶工厂,并和水果店联系起来吗?当然不是。这会导致水果店和各个工厂耦合度太紧,不利于扩展。

顾名思义,抽象工厂模式就是把各个生成实例的工厂再进一步抽象。通俗地讲就是需要构建“生产工厂的工厂”。

在讲解抽象工厂模式之前需要了解两个概念:

  • 产品族:也就是一个系列的产品,上面的西瓜、苹果、橙子就属于一个产品族
  • 产品等级结构:一个产品可以衍生其相关的一系列产品,例如西瓜可以做成西瓜汁,那么西瓜和西瓜汁就属于西瓜这个产品结构

我们来看个图:

网络异常,图片无法展示
|

可见纵坐标是产品族,也就是我们所说的一系列产品,横坐标表示一个产品的产品等级结构。

好了,我们现在只做水果和水果饮料的生产,对上述进行修改。

首先,水果和果茶我们都称之为水果类食品(Fruit Food),因此我们要把两个工厂(水果工厂,果茶工厂)抽象为工厂接口或者抽象类(水果类食品工厂,FruitFoodFactory),并通过工厂构造类根据需求创建工厂然后生产产品。

做类图如下:

网络异常,图片无法展示
|

我们先创建需求商品类型枚举并在顾客类中加这个属性:

商品类型需求枚举:

packagecom.gitee.swsk33.abstractfactory.param;
/*** 客户需求商品类型*/publicenumType {
/*** 水果*/FRUIT,
/*** 饮料*/DRINK}

顾客类:

packagecom.gitee.swsk33.abstractfactory.param;
importlombok.Getter;
importlombok.Setter;
/*** 客户类*/@Getter@SetterpublicclassCustomer {
/*** 客户需求商品类型*/privateTypetype;
/*** 客户口味*/privateFlavorflavor;
}

然后就是抽象出每个产品族,这里就不进行演示了,和抽象水果是一样的,按照类图很简单。

然后新建水果食品工厂的接口,即为我们的抽象工厂:

packagecom.gitee.swsk33.abstractfactory.factory;
importcom.gitee.swsk33.abstractfactory.model.Drink;
importcom.gitee.swsk33.abstractfactory.model.Fruit;
importcom.gitee.swsk33.abstractfactory.param.Customer;
/*** 水果食品工厂抽象*/publicinterfaceFruitFoodFactory {
/*** 获取水果** @param customer 顾客实例* @return 根据顾客口味获得对应水果*/FruitgetFruit(Customercustomer);
/*** 获取饮料** @param customer 顾客实例* @return 根据顾客口味获得对应水果饮料*/DrinkgetDrink(Customercustomer);
}

然后建立水果工厂和水果饮料工厂实现抽象工厂:

水果工厂:

packagecom.gitee.swsk33.abstractfactory.factory.impl;
importcom.gitee.swsk33.abstractfactory.factory.FruitFoodFactory;
importcom.gitee.swsk33.abstractfactory.model.*;
importcom.gitee.swsk33.abstractfactory.model.impl.Apple;
importcom.gitee.swsk33.abstractfactory.param.Customer;
importcom.gitee.swsk33.abstractfactory.model.impl.Orange;
importcom.gitee.swsk33.abstractfactory.model.impl.Watermelon;
/*** 水果工厂类*/publicclassFruitFactoryimplementsFruitFoodFactory {
@OverridepublicFruitgetFruit(Customercustomer) {
Fruitfruit=null;
switch (customer.getFlavor()) {
caseSWEET:
Watermelonwatermelon=newWatermelon();
watermelon.setName("西瓜");
watermelon.setCooked(true);
fruit=watermelon;
break;
caseSWEET_ACID:
Appleapple=newApple();
apple.setName("苹果");
apple.setSweetness(100);
fruit=apple;
break;
caseACID:
Orangeorange=newOrange();
orange.setName("橙子");
orange.setAcidity(12);
fruit=orange;
break;
      }
returnfruit;
   }
@OverridepublicDrinkgetDrink(Customercustomer) {
returnnull;
   }
}

水果饮料工厂(果茶工厂):

packagecom.gitee.swsk33.abstractfactory.factory.impl;
importcom.gitee.swsk33.abstractfactory.factory.FruitFoodFactory;
importcom.gitee.swsk33.abstractfactory.model.Drink;
importcom.gitee.swsk33.abstractfactory.model.Fruit;
importcom.gitee.swsk33.abstractfactory.model.impl.AppleDrink;
importcom.gitee.swsk33.abstractfactory.model.impl.OrangeDrink;
importcom.gitee.swsk33.abstractfactory.model.impl.WatermelonDrink;
importcom.gitee.swsk33.abstractfactory.param.Customer;
publicclassDrinkFactoryimplementsFruitFoodFactory {
@OverridepublicFruitgetFruit(Customercustomer) {
returnnull;
   }
@OverridepublicDrinkgetDrink(Customercustomer) {
Drinkdrink=null;
switch (customer.getFlavor()) {
caseSWEET:
WatermelonDrinkwatermelonDrink=newWatermelonDrink();
watermelonDrink.setName("西瓜汁");
drink=watermelonDrink;
break;
caseSWEET_ACID:
AppleDrinkappleDrink=newAppleDrink();
appleDrink.setName("苹果汁");
drink=appleDrink;
break;
caseACID:
OrangeDrinkorangeDrink=newOrangeDrink();
orangeDrink.setName("橙汁");
drink=orangeDrink;
break;
      }
returndrink;
   }
}

因为水果工厂不生产果茶但是水果工厂实现了水果类食物工厂的接口,因此也有getDrink方法,将其留空返回null即可。

果茶工厂和水果工厂类似,同样是实现水果类食物工厂接口,并完成getDrink方法,但是把getFruit方法留空返回null

然后就是工厂构建类(生产工厂的工厂):

packagecom.gitee.swsk33.abstractfactory.builder;
importcom.gitee.swsk33.abstractfactory.factory.FruitFoodFactory;
importcom.gitee.swsk33.abstractfactory.factory.impl.DrinkFactory;
importcom.gitee.swsk33.abstractfactory.factory.impl.FruitFactory;
importcom.gitee.swsk33.abstractfactory.param.Customer;
/*** 工厂建造器*/publicclassFruitFoodFactoryBuilder {
/*** 根据客户的需求类型建造出对应的工厂** @param customer 顾客实例* @return 对应的工厂实例*/publicstaticFruitFoodFactorybuildFactory(Customercustomer) {
FruitFoodFactoryfactory=null;
switch (customer.getType()) {
caseFRUIT:
factory=newFruitFactory();
break;
caseDRINK:
factory=newDrinkFactory();
break;
      }
returnfactory;
   }
}

同样地,在水果店只需要调用工厂构造类先构建对应工厂,再调用对应工厂对应方法生成产品即可。

packagecom.gitee.swsk33.abstractfactory;
importcom.gitee.swsk33.abstractfactory.builder.FruitFoodFactoryBuilder;
importcom.gitee.swsk33.abstractfactory.factory.FruitFoodFactory;
importcom.gitee.swsk33.abstractfactory.model.Drink;
importcom.gitee.swsk33.abstractfactory.param.Customer;
importcom.gitee.swsk33.abstractfactory.param.Flavor;
importcom.gitee.swsk33.abstractfactory.param.Type;
/*** 商店类*/publicclassStore {
publicstaticvoidmain(String[] args) {
// 模拟客户来买东西Customercustomer=newCustomer();
// 客户要买饮料、口味酸customer.setType(Type.DRINK);
customer.setFlavor(Flavor.ACID);
// 首先建造对应的工厂FruitFoodFactoryfactory=FruitFoodFactoryBuilder.buildFactory(customer);
// 工厂生产物品Drinkdrink=factory.getDrink(customer);
System.out.println("顾客购买:"+drink.getName());
   }
}

结果:

网络异常,图片无法展示
|

可见抽象工厂看起来麻烦,其实逻辑很清晰,主要可以总结为以下几个大步骤:

  1. 将每个产品族进行抽象并创建对应的具体类
  2. 建立抽象工厂
  3. 实现抽象工厂,每个具体实现的工厂负责生产一个产品族
  4. 建立工厂建造器,根据需求实例化对应的工厂

可见抽象工厂模式,是先确定要生产的产品族并调用工厂建造器实例化对应的工厂,再确定要生产的具体产品利用得到的工厂生成对应的产品实例。

需要注意的是,在简单工厂模式中,由于只有一个工厂,因此工厂中的生产方法是static的,而抽象工厂模式中,由于有多个工厂,因此这时每个工厂的方法就不是static了,但是工厂建造器中建造工厂的方法可以是static的。

3,总结

可见简单工厂中,水果店和工厂相关联,因此降低了水果店和水果的耦合度,水果店无需知道需要什么水果,只需把相应水果取得即可;抽象工厂模式中水果店和工厂构造类相关联这时水果店不仅不用去知道自己需要什么水果,也不需要知道自己需要去找哪家工厂,进一步降低耦合。

可见,抽象工厂模式是基于简单工厂模式的进一步抽象。

示例仓库地址:

相关文章
|
5月前
|
设计模式 Java 关系型数据库
【设计模式】【创建型模式】抽象工厂模式(Abstract Factory)
一、入门 什么是抽象工厂模式? 抽象工厂模式是一种创建型设计模式,它提供了一个接口,用于创建相关或依赖对象的家族,而不需要指定具体的类。 简单来说,抽象工厂模式是工厂方法模式的升级版,它能够创建一组相
188 14
|
5月前
|
设计模式 缓存 安全
【高薪程序员必看】万字长文拆解Java并发编程!(8):设计模式-享元模式设计指南
🌟 ​大家好,我是摘星!​ 🌟今天为大家带来的是并发编程中的经典对象复用设计模式-享元模式,废话不多说让我们直接开始。
118 0
|
5月前
|
设计模式 算法 Java
设计模式觉醒系列(04)策略模式|简单工厂模式的升级版
本文介绍了简单工厂模式与策略模式的概念及其融合实践。简单工厂模式用于对象创建,通过隐藏实现细节简化代码;策略模式关注行为封装与切换,支持动态替换算法,增强灵活性。两者结合形成“策略工厂”,既简化对象创建又保持低耦合。文章通过支付案例演示了模式的应用,并强调实际开发中应根据需求选择合适的设计模式,避免生搬硬套。最后推荐了JVM调优、并发编程等技术专题,助力开发者提升技能。
|
7月前
|
设计模式 Java 数据安全/隐私保护
Java 设计模式:装饰者模式(Decorator Pattern)
装饰者模式属于结构型设计模式,允许通过动态包装对象的方式为对象添加新功能,提供比继承更灵活的扩展方式。该模式通过组合替代继承,遵循开闭原则(对扩展开放,对修改关闭)。
|
9月前
|
设计模式 关系型数据库
「全网最细 + 实战源码案例」设计模式——简单工厂模式
简单工厂模式是一种创建型设计模式,通过工厂类根据传入参数创建不同类型的对象,也称“静态工厂方法”模式。其结构包括工厂类、产品接口和具体产品类。优点是封装性强、代码复用性好;缺点是扩展性差,增加新产品时需修改工厂类代码,违反开闭原则。适用于对象种类较少且调用者无需关心创建细节的场景。
178 19
|
9月前
|
设计模式
「全网最细 + 实战源码案例」设计模式——抽象工厂模式
抽象工厂模式是一种创建型设计模式,提供接口用于创建一系列相关或依赖的对象,无需指定具体类。它解决了产品族问题,管理和创建一组相关产品。结构上包括抽象工厂、具体工厂、抽象产品和具体产品。适用于创建相关对象、产品族固定但种类变化的场景。优点是分离接口与实现、管理产品族方便且扩展性好;缺点是产品族扩展困难且代码复杂度增加。通过配置文件和反射机制可进一步改进,使系统更灵活易扩展。
167 17
|
11月前
|
设计模式 消息中间件 搜索推荐
Java 设计模式——观察者模式:从优衣库不使用新疆棉事件看系统的动态响应
【11月更文挑战第17天】观察者模式是一种行为设计模式,定义了一对多的依赖关系,使多个观察者对象能直接监听并响应某一主题对象的状态变化。本文介绍了观察者模式的基本概念、商业系统中的应用实例,如优衣库事件中各相关方的动态响应,以及模式的优势和实际系统设计中的应用建议,包括事件驱动架构和消息队列的使用。
180 6
|
11月前
|
设计模式 Java 数据库连接
Java编程中的设计模式:单例模式的深度剖析
【10月更文挑战第41天】本文深入探讨了Java中广泛使用的单例设计模式,旨在通过简明扼要的语言和实际示例,帮助读者理解其核心原理和应用。文章将介绍单例模式的重要性、实现方式以及在实际应用中如何优雅地处理多线程问题。
149 4
|
11月前
|
设计模式 JavaScript Java
Java设计模式:建造者模式详解
建造者模式是一种创建型设计模式,通过将复杂对象的构建过程与表示分离,使得相同的构建过程可以创建不同的表示。本文详细介绍了建造者模式的原理、背景、应用场景及实际Demo,帮助读者更好地理解和应用这一模式。
521 0
|
设计模式 缓存 安全
Java设计模式的单例模式应用场景
Java设计模式的单例模式应用场景
232 4

热门文章

最新文章