设计模式(五)--工厂模式汇总

  LZ想把简单工厂模式、工厂方法模式和抽象工厂模式整理到一篇博文当中,由浅入深,应该能方便理解和记忆,话不多说,进入正题。

一、简单工厂模式

  定义:从设计模式的类型上来说,简单工厂模式是属于创建型模式,又叫做静态工厂方法(Static Factory Method)模式,但不属于23种GOF设计模式之一。简单工厂模式是由一个工厂对象决定创建出哪一种产品类的实例。简单工厂模式是工厂模式家族中最简单实用的模式,可以理解为是不同工厂模式的一个特殊实现。

  总结成一句话就是,由一个工厂对象决定创建出哪一种产品类的实例。下面是百度百科中简单工厂模式的类图。

  可以看出,上面总共有三种类,一个是工厂类Creator,一个是产品接口IProduct,一个是具体的产品类,例如产品A和产品B,这之中,工厂类负责整个创建产品的逻辑判断,所以为了使工厂类能够知道我们需要哪一种产品,我们需要在创建产品时传递一个参数给工厂类,去表明我们想要创建哪种产品。下面我们将类图翻译成java代码。

  首先是产品接口。

public interface IProduct {public void method();
}

  接下来是具体的产品类。

public class ProductA implements IProduct{public void method() {System.out.println("产品A方法");}}
public class ProductB implements IProduct{public void method() {System.out.println("产品B方法");}}

  最后是工厂类。

public class Creator {private Creator(){}public static IProduct createProduct(String productName){if (productName == null) {return null;}if (productName.equals("A")) {return new ProductA();}else if (productName.equals("B")) {return new ProductB();}else {return null;}}
}

  我们测试一下。

public class Client {public static void main(String[] args) {IProduct product1 = Creator.createProduct("A");product1.method();IProduct product2 = Creator.createProduct("B");product2.method();}
}

   测试结果。

  以上就是简单工厂模式的样子,这个模式有个缺点,如果新加一种产品类,则Creator类的代码要相应改动,而工厂方法模式就很好的遵守了开闭原则,即对修改关闭,对扩展开放。

 

二、工厂方法模式

  定义:工厂方法(Factory Method)模式的意义是定义一个创建产品对象的工厂接口,将实际创建工作推迟到子类当中。核心工厂类不再负责产品的创建,这样核心类成为一个抽象工厂角色,仅负责具体工厂子类必须实现的接口,这样进一步抽象化的好处是使得工厂方法模式可以使系统在不修改具体工厂角色的情况下引进新的产品。

  可以看到工厂方法模式中定义了一个工厂接口,而具体的创建工作推迟到具体的工厂类,它是对简单工厂模式中的工厂类进一步抽象化,从而产生一个工厂类的抽象和实现体系,从而弥补简单工厂模式对修改开放的诟病。

  下面是百度百科中给出的该模式的类图。

  可以看到,上面右半部分是产品抽象和实现体系,左半部分是工厂抽象和实现体系,其中工厂体系依赖于产品体系,每一个工厂负责创造一种产品,这就省去了简单工厂中的elseif判断,又客户端决定实例化一个特定的工厂去创建相应的产品。

  下面我们将类图翻译成代码,首先是抽象产品接口。

public interface Light {public void turnOn();public void turnOff();}

   下面是具体的产品。

public class BuldLight implements Light{public void turnOn() {System.out.println("BuldLight On");    }public void turnOff() {System.out.println("BuldLight Off");    }}
public class TubeLight implements Light{public void turnOn() {System.out.println("TubeLight On");    }public void turnOff() {System.out.println("TubeLight Off");    }}

  下面是抽象的工厂接口。

public interface Creator {public Light createLight();
}

  下面是具体的工厂类。

public class BuldCreator implements Creator{public Light createLight() {return new BuldLight();}}
public class TubeCreator implements Creator{public Light createLight() {return new TubeLight();}}

   测试一下。

public class Client {public static void main(String[] args) {Creator creator = new BuldCreator();Light light = creator.createLight();light.turnOn();light.turnOff();creator = new TubeCreator();light = creator.createLight();light.turnOn();light.turnOff();}
}

   测试结果。

  可以看到,如果新增产品,只需要添加一个产品类,一个该产品的工厂类即可,不需要修改任何代码。LZ在看这个模式的时候看到别人给出的例子是JDBC API的设计,觉得非常贴切。LZ也把这个例子记录在这里。

  众所周知,为了统一各个数据库操作的标准,于是有了JDBC的API,它提供了一系列统一的,标准化的操作数据库的接口,我们平时操作数据库,依赖的就是这些抽象,而不是具体的数据库的实现,那sun公司是怎么做到的呢?用的就是工厂设计模式。

  JDBC是如何统一了数据库世界的呢?其实最主要的就是靠两个接口。第一个接口是Driver,我们大体看下源码。

package java.sql;import java.sql.DriverPropertyInfo;
import java.sql.SQLException;/*** The interface that every driver class must implement.*/
public interface Driver {Connection connect(String url, java.util.Properties info)throws SQLException;
}

  connect方法即创造一个数据库连接,也就是说,Driver对象就是工厂模式中的Creator接口,即工厂类的抽象。

  这个类除了connect方法以外,还有很多其他方法,篇幅原因,就不一一展开了,我们只关心核心方法,接口上有一句注释,翻译过来是这是一个任何驱动类都必须实现的接口。也就是说,sun公司明确规定,所有数据库厂商都必须实现这个接口来提供JDBC服务,即java数据库连接服务。

   第二个接口是connect方法的返回抽象Connection对象,我们看一下源码,仍然只关心核心方法就可以。

package java.sql;import java.sql.PreparedStatement;
import java.sql.SQLException;/*** <P>A connection (session) with a specific* database. SQL statements are executed and results are returned* within the context of a connection.* <P>*/
public interface Connection  extends Wrapper {Statement createStatement() throws SQLException;PreparedStatement prepareStatement(String sql) throws SQLException;}

  以上两个接口作为JDBC API的一部分,它们相当于告诉了数据库生产厂商两个要求。

       第一,数据库厂商要提供一个数据库驱动类,它的作用可以是可以创造数据库连接,而这个数据库连接向上转型为我们JDBC的Connection。

       第二,数据库厂商要提供一个数据库连接的实现类,这个实现类可以执行具体数据库的各个操作,比如帮我们执行SQL,返回执行结果,关闭连接等等。

  LZ把类图画了一下,UML类图对设计模式这块非常重要,我个人的经验是,永远不要记代码,要记设计思想,记UML类图,记应用场景,所谓用抽象构建框架,用细节扩展实现

  多标准的工厂方法设计模式啊,sun公司正是用这个模式统一了数据库世界。工厂方法模式就是提供一个抽象的工厂,一个抽象的产品,在上述当中相当于Driver(数据库连接工厂)和Connection(抽象产品),实现的一方需要提供一个具体的工厂类(比如mysql驱动)和一个具体的产品(比如mysql数据库连接)。

  客户端调用时不依赖于具体工厂和产品(即到底是mysql驱动,mysql数据库连接还是oracle驱动,oracle连接,我们程序猿不需要管的,我们只管使用抽象的driver和connection,对吧?),而是依赖于抽象工厂和抽象产品完成工作。

  类图里还有个DriverMananger,DriverMananger在这个设计当中扮演者一个管理者的角色,它帮我们管理数据库驱动,让我们不需要直接接触驱动接口,我们获取连接只需要和DriverManager打交道就可以,也就是说客户端依赖于DriverManager和Connection就可以完成工作,不再需要与Driver关联,所以上述说我们依赖于Driver和Connection,现在DriverManager帮我们管理Driver,那我们只需要依赖于DriverManager和Connection就可以了。回想我们刚开始学习JDBC的时候,是不是只要让数据库厂商提供的具体数据库连接类加载,就可以直接从DriverManager里取连接了,所以这是sun公司为了方便编码给我们提供的一个管理类。

 

 三、抽象工厂模式

  抽象工厂模式算是工厂相关模式的终极形,基于上面的理解,我们不难理解抽象工厂模式,它与工厂方法唯一的区别就是工厂的接口里是一系列创造抽象产品的方法,而不再是一个,而相应的,抽象产品也不再是一个了,而是一系列相关的产品。这其实是工厂方法模式的一种扩展不是吗?

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

  我们看下百度百科给出的类图。

 

  我们把类图翻译成代码看一下。首先是产品族,也就是类图右边部分。

package net;interface ProductA {void methodA();
}interface ProductB {void methodB();
}class ProductA1 implements ProductA{public void methodA() {System.out.println("产品A系列中1型号产品的方法");}}class ProductA2 implements ProductA{public void methodA() {System.out.println("产品A系列中2型号产品的方法");}}class ProductB1 implements ProductB{public void methodB() {System.out.println("产品B系列中1型号产品的方法");}}class ProductB2 implements ProductB{public void methodB() {System.out.println("产品B系列中2型号产品的方法");}}

   左半部分。

package net;public interface Creator {ProductA createProductA();ProductB createProductB();}
package net;public class ConcreteCreator1 implements Creator{public ProductA createProductA() {return new ProductA1();}public ProductB createProductB() {return new ProductB1();}}
package net;public class ConcreteCreator2 implements Creator{public ProductA createProductA() {return new ProductA2();}public ProductB createProductB() {return new ProductB2();}}

  测试一下。

package net;public class Client {public static void main(String[] args) throws Exception {Creator creator = new ConcreteCreator1();ProductA productA = creator.createProductA();ProductB productB = creator.createProductB();productA.methodA();productB.methodB();creator = new ConcreteCreator2();productA = creator.createProductA();productB = creator.createProductB();productA.methodA();productB.methodB();}
}

   综上所述,简单工厂→工厂方法→抽象工厂,是一步步进化的过程。

  1,首先从简单工厂进化到工厂方法,是因为工厂方法弥补了简单工厂对修改开放的弊端,即简单工厂违背了开闭原则。

  2,从工厂方法进化到抽象工厂,是因为抽象工厂弥补了工厂方法只能创造一个的产品的弊端。

  工厂设计模式可能对像LZ这样平时只针对业务编码的程序猿来说用到的机会少一点,但是我们在看源码的过程中一定会看到这个模式,比如前面提到的JDBC,现在相信再回头看JDBC的源码,就能看懂当年sun公司为什么要这么去设计代码,大牛们牛X的地方,我们才能真的体会到。

转载于:https://www.cnblogs.com/peterxiao/p/10207598.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/365994.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

如何估算内存消耗?

这个故事可以追溯到至少十年之前&#xff0c;当时我第一次接触PHB时遇到一个问题&#xff1a;“在生产部署中&#xff0c;我们需要购买多大服务器”。 我们正在构建的新的&#xff0c;闪亮的系统距离生产开始还有9个月的时间&#xff0c;显然该公司已承诺提供包括硬件在内的整个…

python爬取b站403_Python如何爬取b站热门视频并导入Excel

代码如下 #encoding:utf-8 import requests from lxml import etree import xlwt import os # 爬取b站热门视频信息 def spider(): video_list [] url "https://www.bilibili.com/ranking?spm_id_from333.851.b_7072696d61727950616765546162.3" html requests.g…

url文件的格式

[DEFAULT]BASEURL[InternetShortcut]URLWorkingDirectoryShowCommandIconIndexIconFileModifiedHotKey  其中BASEURL、URL和WorkingDirectory这3项的含义是不言而明的。ShowCommand规定Internet Explorer启动后窗口的初始状态&#xff1a;7表示最小化&#xff0c;3表示最大化…

vscode 编辑器常用快捷键

最近&#xff0c;打算换个编辑器&#xff0c;而 vscode 是一个不错的选择。大部分快捷键和 sublime 还是很像的&#xff0c;但有些也不一样。特此整理一份小笔记。 参考&#xff1a; vscode: Visual Studio Code 常用快捷键非常全的VsCode快捷键常用快捷键 主命令&#xff1a;c…

linux sudo永久免密码,linux 免密码 使用sudo 直接使用root权限执行命令

1.切换到root用户下,怎么切换就不用说了吧,不会的本身百度去.百度2.添加sudo文件的写权限,命令是:权限chmod uw /etc/sudoers密码3.编辑sudoers文件文件vi /etc/sudoersvi找到这行 root ALL(ALL) ALL,在他下面添加xxx ALL(ALL) ALL (这里的xxx是你的用户名)sudops:这里说下你能…

Derek解读Bytom源码-创世区块

作者&#xff1a;Derek 简介 Github地址&#xff1a;https://github.com/Bytom/bytom Gitee地址&#xff1a;https://gitee.com/BytomBlockchain/bytom 本章介绍Derek解读-Bytom源码分析-创世区块 作者使用MacOS操作系统&#xff0c;其他平台也大同小异 Golang Version: 1.8 创…

使用调试器进行事后跟踪

我最近一直在使用的大多数调试器的好功能是能够在断点上记录信息。 这对理解代码而无需修改是非常有用的&#xff0c;它涉及字节码修改。 让我们考虑一下这种非常琐碎且效率低下的函数实现&#xff0c;以返回斐波那契数列中的第n个数字。 public class Fib {public long fib(…

链表排序c++代码_[链表面试算法](一) 链表的删除-相关题型总结(6题)

在数据结构的最高层抽象里&#xff0c;只有两种结构&#xff0c;数组和链表。这两种结构&#xff0c;是所有其他数据结构实现的基础。队列和栈&#xff0c;可以用链表和数组来实现。图&#xff0c;可以用邻接表和邻接矩阵来实现&#xff0c;其中&#xff0c;邻接表就是链表&…

JS原生方法实现jQuery的ready()

浏览器加载页面的顺序&#xff1a; 1、 解析HTML结构 2、 加载外部脚本和样式表文件 3、 解析并执行脚本代码 4、 构造HTML DOM模型ready() 5、 加载图片等组件 6、 页面加载完毕onload() ready事件是在DOM模型构造完毕时触发 load事件是在页面加载完毕后触发 function…

程序员高效工具列表

FQ必备 *** 文件管理器 wox 或者 Listary , everything 截图软件 Snipaste 下载器 Fish 冰点 Markdown 工具 Typora 图床工具 PicGo 思维导图 Xmind 抓包工具 Wireshark 协议工具 Fiddler 接口测试工具 PostMan 剪切板工具 Ditto 害怕截图丢失&#xff1f; 博客园…

c语言如何空格键返回主菜单,C语言中scanf函数与空格回车的用法说明

众所周知&#xff0c;C语言中的scanf函数的作用是从标准输入设备(通常是键盘)读取输入值&#xff0c;并存储到参数列表中指针所指向的内存单元。下面从几个方面说一下一些稍微细节的东西。下面的实验都在vc6.0中通过。1、scanf的返回值scanf通常返回的是成功赋值(从标准输入设备…

Linear_algebra_03_矩阵

1. 矩阵的线性运算&#xff1a; 2.1 矩阵的乘法&#xff1a;Xik * Ykj Zij 2.2 矩阵乘法性质&#xff1a; 3.1 矩阵的幂次方运算 3.2 矩阵转置的运算律 3.3 方阵运算 4 分块矩阵的运算 5. 矩阵的初等变换 5.1 单位矩阵I经过一次初等变换所得到的矩阵称为初等矩阵. 5.2 初等矩…

在Activiti中执行自定义查询

&#xff08;这可能最终会出现在Activiti 5.15版本的用户指南中&#xff0c;但是我已经想共享它了&#xff09; Activiti API允许使用高级API与数据库进行交互。 例如&#xff0c;对于检索数据&#xff0c;查询API和本机查询API的用法很强大。 但是&#xff0c;对于某些用例&a…

jquery ready方法实现原理

先看这两句代码&#xff1a;window.addEventListener(load,loaded,false);document.addEventListener(DOMContentLoaded,loaded,false);总结&#xff1a;load事件是在页面所有元素都加载完后触发;DOMContentLoaded&#xff0c;它是指dom tree加载完就触发;防止了页面加载被堵塞…

js转json工具_菜鸟丨Egert3D微信小游戏发布与Unity工具使用

本次教程将会为大家介绍Egret3D工具导出Unity场景对象的使用&#xff0c;以及发布微信小游戏流程。让大家对Egret 3D有更加熟悉的了解。需求工具&#xff1a;1、Unity场景导出插件&#xff1b;2、微信开发者工具。导出插件的使用一、打开需要导出的Unity场景&#xff0c;并且把…

MySQL----示例知识点整理

示例语句&#xff1a; select count(0),hour(c.created_at) from behavior_client_view c join behavior_share son c.share_uuids.uuidwhere s.agent_uuid(select uuid from user where mobile12606666333 and deleted0)and DATE_FORMAT(c.created_at,%Y-%m) >DATE_FORMAT(…

c语言c1变成e并输出,【图片】(原创)用纯C变了个变色输出字符的程序。。。【c语言吧】_百度贴吧...

该楼层疑似违规已被系统折叠 隐藏此楼查看此楼#include#include#includemain(){char c;int i,j,k,l,m,n,o;int x,y;char c1;int a,b,d,e;x35;y8;textcolor(13);gotoxy(35,10),cprintf("photoshop2014");textcolor(11);gotoxy(20,11);cprintf(" My name is zhou …

OI杂记

从今天开始记录一下为数不多天的OI历程 8.25 上 今天举行了难得的五校联考&#xff0c;模拟noip&#xff0c;题目的解压密码竟然是$aKnoIp2o18$&#xff0c;对你没有看错&#xff01;&#xff01;&#xff01; 7:50老师&#xff1f;啊啊啊啊&#xff0c;收不到题目啊&#xff0…

ebpf基础篇(二) ----- ebpf前世今生

bpf 要追述ebpf的历史,就不得不提bpf. bpf(Berkeley Packet Filter)从早(1992年)诞生于类Unix系统中,用于数据包分析. 它提供了数据链路层的接口,可以在数据链路层发送和接收数据.如果网卡支持混杂模式,所有的数据包都可以被接收,即使这些数据包的目的地址是其它主机. BPF最为…

Java,Steam控制器和我

您是否想过是否可以将现有的东西用于新的东西&#xff1f; 我看了一些所谓的“蒸汽控制器”&#xff08;从现在开始为SC&#xff09;的镜头&#xff0c;并看着我的游戏手柄。 问我自己是否有可能以类似蒸汽的方式使用它&#xff0c;我找到了一些Java库并创建了一个项目&#xf…