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,总结

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

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

示例仓库地址:

相关文章
|
23天前
|
设计模式 Java 程序员
[Java]23种设计模式
本文介绍了设计模式的概念及其七大原则,强调了设计模式在提高代码重用性、可读性、可扩展性和可靠性方面的作用。文章还简要概述了23种设计模式,并提供了进一步学习的资源链接。
36 0
[Java]23种设计模式
|
7天前
|
设计模式 JavaScript Java
Java设计模式:建造者模式详解
建造者模式是一种创建型设计模式,通过将复杂对象的构建过程与表示分离,使得相同的构建过程可以创建不同的表示。本文详细介绍了建造者模式的原理、背景、应用场景及实际Demo,帮助读者更好地理解和应用这一模式。
|
1月前
|
设计模式 监控 算法
Java设计模式梳理:行为型模式(策略,观察者等)
本文详细介绍了Java设计模式中的行为型模式,包括策略模式、观察者模式、责任链模式、模板方法模式和状态模式。通过具体示例代码,深入浅出地讲解了每种模式的应用场景与实现方式。例如,策略模式通过定义一系列算法让客户端在运行时选择所需算法;观察者模式则让多个观察者对象同时监听某一个主题对象,实现松耦合的消息传递机制。此外,还探讨了这些模式与实际开发中的联系,帮助读者更好地理解和应用设计模式,提升代码质量。
Java设计模式梳理:行为型模式(策略,观察者等)
|
1月前
|
设计模式 Java
Java设计模式
Java设计模式
28 0
|
1月前
|
设计模式 Java
Java设计模式之外观模式
这篇文章详细解释了Java设计模式之外观模式的原理及其应用场景,并通过具体代码示例展示了如何通过外观模式简化子系统的使用。
29 0
|
1月前
|
设计模式 Java
Java设计模式之桥接模式
这篇文章介绍了Java设计模式中的桥接模式,包括桥接模式的目的、实现方式,并通过具体代码示例展示了如何分离抽象与实现,使得两者可以独立变化。
43 0
|
1月前
|
设计模式 Java
Java设计模式之适配器模式
这篇文章详细讲解了Java设计模式中的适配器模式,包括其应用场景、实现方式及代码示例。
42 0
|
16天前
|
设计模式 安全 Java
Kotlin教程笔记(51) - 改良设计模式 - 构建者模式
Kotlin教程笔记(51) - 改良设计模式 - 构建者模式
|
2月前
|
设计模式 数据库连接 PHP
PHP中的设计模式:提升代码的可维护性与扩展性在软件开发过程中,设计模式是开发者们经常用到的工具之一。它们提供了经过验证的解决方案,可以帮助我们解决常见的软件设计问题。本文将介绍PHP中常用的设计模式,以及如何利用这些模式来提高代码的可维护性和扩展性。我们将从基础的设计模式入手,逐步深入到更复杂的应用场景。通过实际案例分析,读者可以更好地理解如何在PHP开发中应用这些设计模式,从而写出更加高效、灵活和易于维护的代码。
本文探讨了PHP中常用的设计模式及其在实际项目中的应用。内容涵盖设计模式的基本概念、分类和具体使用场景,重点介绍了单例模式、工厂模式和观察者模式等常见模式。通过具体的代码示例,展示了如何在PHP项目中有效利用设计模式来提升代码的可维护性和扩展性。文章还讨论了设计模式的选择原则和注意事项,帮助开发者在不同情境下做出最佳决策。
|
18天前
|
设计模式 开发者 Python
Python编程中的设计模式:工厂方法模式###
本文深入浅出地探讨了Python编程中的一种重要设计模式——工厂方法模式。通过具体案例和代码示例,我们将了解工厂方法模式的定义、应用场景、实现步骤以及其优势与潜在缺点。无论你是Python新手还是有经验的开发者,都能从本文中获得关于如何在实际项目中有效应用工厂方法模式的启发。 ###