一、设计模式的分类

总的来说,设计模式可以分为三大类:创建型模式结构型模式行为型模式,具体如下图:

二、工厂模式

工厂模式分为简单工厂模式工厂方法模式抽象工厂模式。其中简单工厂模式并不属于23种设计模式,但并不影响它的广泛使用。在JDK的源码当中,就存在着许多这样的例子。

2.1 简单工厂模式

我们先来看一段代码:

public static void main(String[] args) {
// 日历类
Calendar calendar = Calendar.getInstance();
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
System.out.println("当前时间为:" + simpleDateFormat.format(calendar.getTime()));
calendar.add(Calendar.HOUR,2);
System.out.println("当前时间加了两个小时后,时间是: " + simpleDateFormat.format(calendar.getTime()));
}

这段代码,大家应该比较熟悉,通过对Calendar的一系列操作,打印出当前时间和当前时间加两个小时后的时间,这里我们来看看结果:

结果正和我们想象的一样,两次打印出来的时间相隔两个小时。但我们今天的重点是Calendar calendar = Calendar.getInstance()这段代码,通过getInstance()方法拿到了Calendar 类的实例。来看看具体的源代码:

public static Calendar getInstance(){
return createCalendar(TimeZone.getDefault(), Locale.getDefault(Locale.Category.FORMAT));
} // 代码不全,有兴趣的朋友可以去看JDK源码
private static Calendar createCalendar(TimeZone zone, Locale aLocale){
// 中间的代码省略.....
Calendar cal = null;
if (aLocale.hasExtensions()) {
String caltype = aLocale.getUnicodeLocaleType("ca");
if (caltype != null) {
switch (caltype) {
case "buddhist":
cal = new BuddhistCalendar(zone, aLocale);
break;
case "japanese":
cal = new JapaneseImperialCalendar(zone, aLocale);
break;
case "gregory":
cal = new GregorianCalendar(zone, aLocale);
break;
}
}
}
// 中间的代码省略.....
return cal;
}

可以看出,getInstance()方法里面调用了createCalendar()方法来得到Calendar类的实例,最后返回给调用者。而createCalendar()方法中通过switch(){case}的判断来返回所对应的Calendar类的实例,这其实就是简单工厂模式的一种应用。

看完简单工厂模式JDK中的应用之后,我们来设计一下自己的例子:

小明家新开了一家小工厂,接了一单生意,帮助海尔(Haier)集团生产冰箱,并需要设计相应的方案。小明本身也是程序员出身,思考一会后就写出了下面的代码:

/**
* 冰箱
*/
public interface IFridge { // 生产冰箱
public void createFridge(); }
/**
* 海尔
*/
public class Haier implements IFridge {
@Override
public void createFridge() {
System.out.println("生产海尔冰箱...");
}
}

客户端调用代码:

public static void main(String[] args) {
IFridge iFridge = new Haier();
iFridge.createFridge();
}

看上面的代码,父类IFridge类指向子类Haier类的引用,应用层需要依赖于Haier。如果业务扩展,后续增加格力(Gree)甚至更多,那么客户端这里的代码会越来越臃肿。所以,我们要想办法将这种依赖减弱,将创建IFridge对象的细节隐藏掉。我们用简单工厂模式优化一下:

创建Gree格力类

/**
* 格力
*/
public class Gree implements IFridge {
@Override
public void createFridge() {
System.out.println("生产格力冰箱...");
}
}

创建FridgeFactory工厂类

/**
* 冰箱工厂
*/
public class FridgeFactory { // 创建对应的 IFridge 实例
public static IFridge createFridge(String name){
if ("haier".equals(name)){
return new Haier();
} else if ("gree".equals(name)){
return new Gree();
}
return null;
}
}

修改客户端调用的代码:

public static void main(String[] args) {
// 海尔
IFridge haier = FridgeFactory.createFridge("haier");
haier.createFridge(); // 格力
IFridge gree = FridgeFactory.createFridge("gree");
gree.createFridge();
}

这样来看,虽然代码多了,但维护起来以及扩展起来就方便很多,来看一看类图:

当然,上面的FridgeFactory代码中依旧有些问题,如果我们需要增加生产美的(Midea)冰箱,那么我们就需要去修改createFridge()方法的代码,显然违背了开闭原则,我们来改造一下:

修改FridgeFactory工厂类

/**
* 冰箱工厂
*/
public class FridgeFactory { // 创建对应的 IFridge 实例
public static IFridge createFridge(String className){
try {
if (null != className && !"".equals(className)){
// 反射
return (IFridge)Class.forName(className).newInstance();
}
}catch (Exception e){
e.printStackTrace();
}
return null;
}
}

修改客户端调用的代码

public static void main(String[] args) {
// com.xxx.Haier 换成 自己 项目中 Haier 所在的位置 海尔
IFridge haier = FridgeFactory.createFridge("com.xxx.Haier");
haier.createFridge(); // com.xxx.Gree 换成 自己 项目中 Gree 所在的位置 格力
IFridge gree = FridgeFactory.createFridge("com.xxx.Gree");
gree.createFridge();
}

优化之后,我们再也不需要随着业务的提升而去修改FridgeFactory类中的代码了。但是依旧有一个问题,createFridge()方法中的参数是字符串,如果有人乱填怎么办,那不就报错了,所以再来优化一下:

修改FridgeFactory工厂类

/**
* 冰箱工厂
*/
public class FridgeFactory { // 创建对应的 IFridge 实例
public static IFridge createFridge(Class<? extends IFridge> clazz){
try {
if (clazz != null){
return clazz.newInstance();
}
}catch (Exception e){
e.printStackTrace();
}
return null;
}
}

修改客户端调用的代码

public static void main(String[] args) {
// 海尔
FridgeFactory.createFridge(Haier.class).createFridge(); // 格力
FridgeFactory.createFridge(Gree.class).createFridge();
}

再来看一下类图:

简单工厂模式虽然好用,但也有它的局限性:工厂类的职责过重,不利于扩展更为复杂产品结构。

2.2 工厂方法模式

定义一个创建对象的接口,但让实现这个接口的类来决定实例化哪个类,工厂方法让类的实例化推迟到子类中进行。

在工厂方法模式中用户只需要关心所需产品对应的工厂,无须关心创建细节,而且加入新的产品符合开闭原则

随着小明家新工厂的生意火爆,各类的订单都纷涌而至,各个牌子的厂家都想让小明家的工厂生产冰箱,小明无奈只能开了分工厂,并根据客户的品牌名给工厂取了对应的名字,其中海尔工厂生产海尔的冰箱,格力工厂生产格力的冰箱,美的工厂生产美的的冰箱。用代码演化就是下面这般:

IFridgeFactory类接口

public interface IFridgeFactory {
public IFridge createIFridge();
}

海尔

// 海尔  工厂
public class HaierFactory implements IFridgeFactory {
@Override
public IFridge createIFridge() {
return new Haier();
}
}

格力

// 格力  工厂
public class GreeFactory implements IFridgeFactory {
@Override
public IFridge createIFridge() {
return new Gree();
}
}

美的

/**
* 美的
*/
public class Midea implements IFridge {
@Override
public void createFridge() {
System.out.println("生产美的冰箱...");
}
}
// 美的
public class MideaFactory implements IFridgeFactory {
@Override
public IFridge createIFridge() {
return new Midea();
}
}

客户端调用:

public static void main(String[] args) {
// 格力
new GreeFactory().createIFridge().createFridge(); // 海尔
new HaierFactory().createIFridge().createFridge(); // 美的
new MideaFactory().createIFridge().createFridge();
}

这里其实就是细化了工厂,将业务拆分,利用了设计模式原则中的单一职责原则,让每个品牌对应工厂只干一件事,不去掺和其他品牌的事情。来看一看类图:

工厂方法模式适用于一下场景:

  • 创建对象需要大量重复的代码
  • 客户端(应用层)不依赖于产品类实例如何被创建、实现等细节
  • 一个类通过其子类来指定创建哪个对象

工厂方法模式也有缺点:

  • 类的个数容易过多,增加复杂度
  • 增加了系统的抽象性和理解难度

2.3 抽象工厂模式

定义:提供一个创建一系列相关或者相互依赖对象的接口,无需指定他们具体的类。

这个定义读起来相当的拗口,很抽象,不好理解。还是和上面的例子结合来说明:

在生产完一批冰箱并上市售卖之后,美的、格力、海尔等公司非常满意,慢慢的将自己家的空调、热水器也交给小明家的工厂去生产了。小明为此在对应的品牌工厂有开辟了对应的生产设备的空间(这里为了大家看的方便,我将所有的代码都放上去):

冰箱、空调、热水器接口

// 冰箱
public interface IFridge {
// 生产冰箱
public void createFridge();
} // 空调
public interface IAirConditioner {
// 生产空调
public void createAirConditioner();
} // 热水器
public interface IWaterHeater {
// 生产热水器
public void createWaterHeater();
}

海尔

/**
* 海尔 冰箱
*/
public class HaierFridge implements IFridge{
@Override
public void createFridge() {
System.out.println("生产海尔冰箱...");
}
} // 海尔 空调
public class HaierAirConditioner implements IAirConditioner {
@Override
public void createAirConditioner() {
System.out.println("生产海尔空调...");
}
} // 海尔热水器
public class HaierWaterHeater implements IWaterHeater { @Override
public void createWaterHeater() {
System.out.println("生产海尔热水器...");
}
}

格力

/**
* 格力 冰箱
*/
public class GreeFridge implements IFridge {
@Override
public void createFridge() {
System.out.println("生产格力冰箱...");
}
} // 格力 空调
public class GreeAirConditioner implements IAirConditioner {
@Override
public void createAirConditioner() {
System.out.println("生产格力空调...");
}
} // 格力热水器
public class GreeWaterHeater implements IWaterHeater {
@Override
public void createWaterHeater() {
System.out.println("生产格力热水器...");
}
}

美的

/**
* 美的 冰箱
*/
public class MideaFridge implements IFridge{
@Override
public void createFridge() {
System.out.println("生产美的冰箱...");
}
} // 美的 空调
public class MideaAirConditioner implements IAirConditioner {
@Override
public void createAirConditioner() {
System.out.println("生产美的空调...");
}
} // 美的热水器
public class MideaWaterHeater implements IWaterHeater {
@Override
public void createWaterHeater() {
System.out.println("生产美的热水器...");
}
}

工厂接口

public interface IFactory {
// 冰箱
public IFridge createIFridge();
// 空调
public IAirConditioner createIConditioner();
// 热水器
public IWaterHeater createIWaterHeater();
}

海尔工厂

// 海尔  工厂
public class HaierFactory implements IFactory {
// 冰箱
@Override
public IFridge createIFridge() {
return new HaierFridge();
} // 空调
@Override
public IAirConditioner createIConditioner() {
return new HaierAirConditioner();
} // 热水器
@Override
public IWaterHeater createIWaterHeater() {
return new HaierWaterHeater();
}
}

格力工厂

// 格力
public class GreeFactory implements IFactory {
// 冰箱
@Override
public IFridge createIFridge() {
return new GreeFridge();
}
// 空调
@Override
public IAirConditioner createIConditioner() {
return new GreeAirConditioner();
}
// 热水器
@Override
public IWaterHeater createIWaterHeater() {
return new GreeWaterHeater();
}
}

美的工厂

// 美的
public class MideaFactory implements IFactory {
// 冰箱
@Override
public IFridge createIFridge() {
return new MideaFridge();
}
// 空调
@Override
public IAirConditioner createIConditioner() {
return new MideaAirConditioner();
}
// 热水器
@Override
public IWaterHeater createIWaterHeater() {
return new MideaWaterHeater();
}
}

客户端调用

public static void main(String[] args) {
// 海尔工厂
HaierFactory haierFactory = new HaierFactory();
haierFactory.createIFridge().createFridge();
haierFactory.createIConditioner().createAirConditioner();
haierFactory.createIWaterHeater().createWaterHeater(); // 格力工厂
GreeFactory greeFactory = new GreeFactory();
greeFactory.createIFridge().createFridge();
greeFactory.createIConditioner().createAirConditioner();
greeFactory.createIWaterHeater().createWaterHeater(); // 美的工厂
MideaFactory mideaFactory = new MideaFactory();
mideaFactory.createIFridge().createFridge();
mideaFactory.createIConditioner().createAirConditioner();
mideaFactory.createIWaterHeater().createWaterHeater();
}

类图

从上面一大堆的代码,尤其是类图,我们可以很明显的感觉到,抽象工厂可以完美清晰的描述海尔、格力、美的三个品牌的冰箱、空调、热水器的庞大体系。但也正因为如此,抽象工厂给我们的视觉冲击有些大,能很明显的感觉到系统的复杂性抽象性以及系统的极难扩展性;并且这里还隐藏着一个违背开闭原则的问题:

在工厂接口IFactory类中,如果在日后的产品升级当中,需要增加生产洗衣机的业务,那这里修改之后,所有实现IFactory接口的类都需要变动,很大程度增加了系统的不稳定性。

也正因为如此,在实际的业务开发中,我们不应该有着强烈的强迫症和洁癖,认为一个系统的结构设计必须要完美的符合各种原则。要结合实际的业务去思考,如果系统结构的等级更新不频繁的话,不遵守某些原则也是有必要性的,毕竟所有的技术都是为业务而服务的

Java设计模式(5:设计模式的分类及工厂模式详解)的更多相关文章

  1. JAVA 设计模式之 工厂模式详解

    一.简单工厂模式 简单工厂模式(Simple Factory Pattern)是指由一个工厂对象决定创建出哪一种产品类 的实例.属于创建型模式,但它不属于 GOF,23 种设计模式 (参考资料: ht ...

  2. JS设计模式——工厂模式详解

    它的领域中同其它模式的不同之处在于它并没有明确要求我们使用一个构造器.取而代之,一个工厂能提供一个创建对象的公共接口,我们可以在其中指定我们希望被创建的工厂对象的类型. 简单工厂模式:使用一个类(通常 ...

  3. 抽象工厂模式详解 —— head first 设计模式

    项目实例 假设你有一家 pizza 店,你有很多种 pizza,要在系统中显示你所有 pizza 种类.实现这个功能并不难,使用普通方式实现: public class PizzaStore { Pi ...

  4. java之简单工厂模式详解

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

  5. 设计模式之第2章-抽象工厂模式(Java实现)

    设计模式之第2章-抽象工厂模式(Java实现) “上次是我的不对,贿赂作者让我先讲来着,不过老婆大人大人有大量,不与我计较,这次还让我先把上次未讲完的应用场景部分给补充上去,有妻如此,夫复何求.”(说 ...

  6. 《设计模式面试小炒》策略和工厂模式替代业务场景中复杂的ifelse

    <设计模式面试小炒>策略和工厂模式替代业务场景中复杂的ifelse 我是肥哥,一名不专业的面试官! 我是囧囧,一名积极找工作的小菜鸟! 囧囧表示:小白面试最怕的就是面试官问的知识点太笼统, ...

  7. Java开源生鲜电商平台-盈利模式详解(源码可下载)

    Java开源生鲜电商平台-盈利模式详解(源码可下载) 该平台提供一个联合买家与卖家的一个平台.(类似淘宝购物,这里指的是食材的购买.) 平台有以下的盈利模式:(类似的平台有美菜网,食材网等) 1. 订 ...

  8. Java和Ibatis调用存储过程并取得返回值详解

    Java和Ibatis调用存储过程并取得返回值详解 2011-07-19 17:33 jiandanfeng2 CSDN博客 字号:T | T 本文主要介绍了Java和Ibatis调用存储过程的方法, ...

  9. [转]JAVA环境变量JAVA_HOME、CLASSPATH、PATH设置详解

    [转] JAVA环境变量JAVA_HOME.CLASSPATH.PATH设置详解 - dreamman的日志 - 网易博客http://blog.163.com/dreamman_yx/blog/st ...

随机推荐

  1. 中国石油大学(华东)数学实验(MATLAB)复习

    作者:张世琛 函数的导数 $$ 求函数y=log(x+\sqrt{1+x^2})的一阶和二阶导数 $$ syms x; y=log(x+sqrt(1+x^2)); dydx=diff(y,x); dy ...

  2. 【JavaScript】JS从入门到深入(复习查漏向

    [JavaScript]JS从入门到深入(复习查漏向 pre 精细得学过一遍JS后才发现,原来之前CTF中有些nodejs的题目以及一些游戏题的payload就变得很好理解了. 基础知识 ECMASc ...

  3. Java安全之Fastjson反序列化漏洞分析

    Java安全之Fastjson反序列化漏洞分析 首发:先知论坛 0x00 前言 在前面的RMI和JNDI注入学习里面为本次的Fastjson打了一个比较好的基础.利于后面的漏洞分析. 0x01 Fas ...

  4. 按照自己的思路研究Spring AOP源码【2】

    目录 问题的提出 哪一步导致了顺序的改变 AbstractAdvisorAutoProxyCreator.sortAdvisors()方法 总结 问题的提出 上面这篇文章介绍了Spring AOP源码 ...

  5. [bug] springboot 静态资源 layui.css 404

    目录结构 引用路径 <link rel="stylesheet" href="../static/layui/css/layui.css" type=&q ...

  6. shell应用之简单计算器

    1 #!/bin/bash 2 while : 3 do 4 read -p "请输入计算规则:" JS 5 if [ -z $JS ];then 6 exit 7 else 8 ...

  7. 【Azure 环境】Azure Key Vault (密钥保管库)中所保管的Keys, Secrets,Certificates是否可以实现数据粒度的权限控制呢?

    问题描述 Key Vault (密钥保管库) 能不能针对用户授权实现指定用户只能访问某个或某些特定的key? 如当前有两个用户(User1, User2),在Key Vault中有10个Key,Use ...

  8. AtCoder Regular Contest 119 C - ARC Wrecker 2(同余定理+思维)

    Problem Statement There are NN buildings along the AtCoder Street, numbered 11 through NN from west ...

  9. python发送丁丁消息

    import requests import time import hashlib import hmac import base64 import re def SendMessage(messa ...

  10. nginx的请求处理

      nginx的请求处理¶ nginx使用一个多进程模型来对外提供服务,其中一个master进程,多个worker进程.master进程负责管理nginx本身和其他worker进程. 所有实际上的业务 ...