淘客网站如何做,合肥智能建站模板,策划活动方案,万网域名注册电话文章系列目录(持续更新中):【设计模式】第一篇#xff1a;概述、耦合、UML、七大原则#xff0c;详细分析总结(基于Java)【设计模式】第二篇#xff1a;单例模式的几种实现And反射对其的破坏一 为什么要用工厂模式之前讲解 Spring 的依赖注入的文章时#xff0c;我们就已经… 文章系列目录(持续更新中):【设计模式】第一篇概述、耦合、UML、七大原则详细分析总结(基于Java)【设计模式】第二篇单例模式的几种实现And反射对其的破坏一 为什么要用工厂模式之前讲解 Spring 的依赖注入的文章时我们就已经有提到过工厂这种设计模式我们直接先通过一个例子来看一下究竟工厂模式能用来做什么【万字长文】Spring框架 层层递进轻松入门 (IOC和DI)首先我们简单的模拟一个对账户进行添加的操作我们先采用我们以前常常使用的方式进行模拟然后再给出改进方案(一) 举一个模拟 Spring IOC 的例子 (1) 以前的程序首先按照我们常规的方式先模拟我们先将一套基本流程走下来AService 层/** * 账户业务层接口 */public interface AccountService { void addAccount();}/** * 账户业务层实现类 */public class AccountServiceImpl implements AccountService { private AccountDao accountDao new AccountDaoImpl(); public void addAccount() { accountDao.addAccount(); }}BDao 层/** * 账户持久层接口 */public interface AccountDao { void addAccount();}/** * 账户持久层实现类 */public class AccountDaoImpl implements AccountDao { public void addAccount() { System.out.println(添加用户成功); }}C调用由于我们创建的Maven工程并不是一个web工程我们也只是为了简单模拟所以在这里创建了一个 Client 类作为客户端来测试我们的方法public class Client { public static void main(String[] args) { AccountService as new AccountServiceImpl(); as.addAccount(); }}运行的结果就是在屏幕上输出一个添加用户成功的字样D分析new 的问题上面的这段代码应该是比较简单也容易想到的一种实现方式了但是它的耦合性却是很高的其中这两句代码就是造成耦合性高的根由因为业务层(service)调用持久层(dao)这个时候业务层将很大的依赖于持久层的接口(AccountDao)和实现类(AccountDaoImpl)private AccountDao accountDao new AccountDaoImpl();AccountService as new AccountServiceImpl();这种通过 new 对象的方式使得不同类之间的依赖性大大增强其中一个类的问题就会直接导致出现全局的问题如果我们将被调用的方法进行错误的修改或者说删掉某一个类执行的结果就是在编译期就出现了错误而我们作为一个开发者我们应该努力让程序在编译期不依赖而运行时才可以有一些必要的依赖(依赖是不可能完全消除的)所以我们应该想办法进行解耦要解耦就要使调用者和被调用者之间没有什么直接的联系那么工厂模式就可以帮助我们很好的解决这个问题(2) 工厂模式改进ABeanFactory具体怎么实现呢在这里可以将 serivice 和 dao 均配置到配置文件中去(xml/properties)通过一个类读取配置文件中的内容并使用反射技术创建对象然后存起来完成这个操作的类就是我们的工厂注在这里我们使用了 properties 主要是为了实现方便xml还涉及到解析的一些代码相对麻烦一些不过我们下面要说的 Spring 就是使用了 xml做配置文件bean.properties先写好配置文件将 service 和 dao 以 keyvalue 的格式配置好accountServicecn.ideal.service.impl.AccountServiceImplaccountDaocn.ideal.dao.impl.AccountDaoImplBeanFactorypublic class BeanFactory { //定义一个Properties对象 private static Properties properties; //使用静态代码块为Properties对象赋值 static { try{ //实例化对象 properties new Properties(); //获取properties文件的流对象 InputStream in BeanFactory.class.getClassLoader().getResourceAsStream(bean.properties); properties.load(in); }catch (Exception e){ throw new ExceptionInInitializerError(初始化properties失败); } } }简单的解释一下这部分代码(当然还没写完)首先就是要将配置文件中的内容读入这里通过类加载器的方式操作读入一个流文件然后从中读取键值对由于只需要执一次所以放在静态代码块中又因为 properties 对象在后面的方法中还要用所以写在成员的位置接着在 BeanFactory 中继续编写一个 getBean 方法其中有两句核心代码的意义就是通过方法参数中传入的字符串找到对应的全类名路径实际上也就是通过刚才获取到的配置内容通过key 找到 value值下一句就是通过 Class 的加载方法加载这个类实例化后返回public static Object getBean(String beanName){ Object bean null; try { //根据key获取value String beanPath properties.getProperty(beanName); bean Class.forName(beanPath).newInstance(); }catch (Exception e){ e.printStackTrace(); } return bean;}B测试代码public class Client { public static void main(String[] args) { AccountService as (AccountService)BeanFactory.getBean(accountService); as.addAccount(); }}C执行效果当我们按照同样的操作删除掉被调用的 dao 的实现类可以看到这时候编译期错误已经消失了而报出来的只是一个运行时异常这样就解决了前面所思考的问题我们应该努力让程序在编译期不依赖而运行时才可以有一些必要的依赖(依赖是不可能完全消除的)(3) 小总结为什么使用工厂模式替代了 new 的方式打个比方在你的程序中如果一段时间后你发现在你 new 的这个对象中存在着bug或者不合理的地方或者说你甚至想换一个持久层的框架这种情况下没办法只能修改源码了然后重新编译部署但是如果你使用工厂模式你只需要重新将想修改的类单独写好编译后放到文件中去只需要修改一下配置文件就可以了我分享下我个人精简下的理解就是【new 对象依赖的是具体事物而不 new 则是依赖抽象事物】Break it down依赖具体事物这个很好理解你依赖的是一个具体的实实在在内容它与你系相关所以有什么问题都是连环的可能为了某个点我们需要修改 N 个地方绝望依赖抽象事物你所调用的并不是一个直接就可以触手可及的东西是一个抽象的概念所以不存在上面那种情况下的连环反应二 三种工厂模式看完前面的例子我想大家已经已经对工厂模式有了一个非常直观的认识了说白了工厂模式就是使用一种手段代替了 new 这个操作以往想要获取一个实例的时候要 new 出来但是这种方式耦合性就会很高我们要尽量的减少这种可避免的耦合负担所以工厂模式就来了工厂就是在调用者和被调用者之间起一个连接枢纽的作用调用者和被调用者都只与工厂进行联系从而减少了两者之间直接的依赖工厂模式一共有三种 ① 简单工厂模式② 工厂方法模式 ③ 抽象工厂模式下面我们一个一个来说(一) 简单工厂模式 (1) 实现下面我们以一个车的例子来讲首先我们有一个抽象的 Car 类public abstract class Car { // 任何汽车都会跑 public abstract void run();}接着就是它的子类我们先来两个一个宝马类一个奔驰类(为阅读方便写成了拼音命名请勿模仿不建议)public class BaoMa extends Car { Override public void run() { System.out.println(【宝马】在路上跑); }}public class BenChi extends Car { Override public void run() { System.out.println(【奔驰】在路上跑); }}那如果我想要实例化这个类实际上最原始的写法可以这样(也就是直接 new 出来)public class Test { public static void main(String[] args) { Car baoMa new BaoMa(); baoMa.run(); Car benChi new BenChi(); benChi.run(); }}如果使用简单工厂模式就需要创建一个专门的工厂类用来实例化对象public class CarFactory { public static Car createCar(String type) { if (宝马.equals(type)) { return new BaoMa(); } else if (奔驰.equals(type)) { return new BenChi(); } else { return null; } }}真正去调用的时候我只需要传入一个正确的参数通过 CarFactory 创建出想要的东西就可以了具体怎么去创建就不需要调用者操心了public class Test { public static void main(String[] args) { Car baoMa CarFactory.createCar(宝马); baoMa.run(); Car benChi CarFactory.createCar(奔驰); benChi.run(); }}(2) 优缺点先说一下优点简单工厂模式的优点就在于其工厂类中含有必要的逻辑判断(例如 CarFactory 中判断是宝马还是奔驰)客户端只需要通过传入参数(例如传入 “宝马”)动态的实例化想要的类客户端就免去了直接创建产品的职责去除了与具体产品的依赖(都不需要知道具体类名了反正我不负责创建)但是其缺点也很明显简单工厂模式的工厂类职责过于繁重违背了高聚合原则同时其内容多的情况下逻辑太复杂。最关键的是当我想要增加一个新的内容的时候例如增加一个保时捷我就不得不去修改 CarFactory 工厂类中的代码这很显然违背了 “开闭原则”所以工厂模式他就来了(二) 工厂模式 (1) 实现依旧是一个汽车抽象类一个宝马类和一个奔驰类是其子类public abstract class Car { // 任何汽车都会跑 public abstract void run();}public class BaoMa extends Car { Override public void run() { System.out.println(【宝马】在路上跑); }}public class BenChi extends Car { Override public void run() { System.out.println(【奔驰】在路上跑); }}如果是简单工厂类就会 有一个总的工厂类来实例化对象为了解决其缺点工厂类首先需要创建一个汽车工厂接口类public interface CarFactory { // 可以获取任何车 Car createCar();}然后宝马和奔驰类分别实现它内容就是创建一个对应宝马或者奔驰(实例化宝马类或者奔驰类)public class BaoMaFactory implements CarFactory { Override public Car createCar() { return new BaoMa(); }}public class BenChiFactory implements CarFactory { Override public Car createCar() { return new BenChi(); }}想要获取车的时候只需要通过多态创建出想要获得的那种车的工厂然后通过工厂再创建出对应的车例如我分别拿到奔驰和宝马就可以这样做public class Test { public static void main(String[] args) { // 先去奔驰工厂拿到一台奔驰 CarFactory benChiFactory new BenChiFactory(); // 4S店拿到一台奔驰给了你 Car benChi benChiFactory.createCar(); benChi.run(); // 先去宝马工厂拿到一台宝马 CarFactory baoMaFactory new BaoMaFactory(); // 4S店拿到一台宝马给了你 Car baoMa baoMaFactory.createCar(); baoMa.run(); }}这种情况下如果我还想要增加一台保时捷类型的车创建出对应的保时捷类(继承 Car)以及对应保时捷工厂类后后仍只需要通过以上方法调用即可// 先去保时捷工厂拿到一台保时捷CarFactory baoShiJieFactory new BaoShiJieFactory();// 4S店拿到一台保时捷给了你Car baoShiJie baoShiJieFactory.createCar();baoShiJie.run();(2) 定义工厂方法模式定义一个用于创建对象的接口让子类决定实例化哪一个类工厂方法使一个类的实例化延迟到其子类看其结构图(3) 优缺点优点对象的创建被明确到了各个子工厂类中不再需要在客户端中考虑新内容增加非常方便只需要增加一个想生成的类和创建其的工厂类不违背 “开闭原则”后期维护扩展方便缺点代码量显著增加(三) 抽象工厂模式 抽象工厂模式是一种比较复杂的工厂模式下面先直接通过代码了解一下还是说车我们将车分为两种一种是普通轿车一种是卡车前面的工厂方法模式中如果不断的增加车的类型这势必会造成工厂过多但是对于常见的车来说还可以寻找可抽取的特点来进行抽象所以在此基础之上我们又分别设定了自动挡和手动挡两种类型所以两两搭配就有四种情况了(eg自动挡卡车手动挡轿车等等)(1) 创建抽象产品首先分别创建普通轿车和卡车的抽象类然后定义两个方法(这里我就写成一样的了可以根据轿车和卡车的特点写不同的方法)public abstract class CommonCar { // 所有车都能停车 abstract void parking(); // 所有车都能换挡 abstract void shiftGear();}public abstract class Truck { // 所有车都能停车 abstract void parking(); // 所有车都能换挡 abstract void shiftGear();}(2) 实现抽象产品说明A是自动的意思H是手动的意思egCommonCarA 代表普通自动挡轿车实现抽象产品——小轿车(自动挡)public class CommonCarA extends CommonCar{ Override void parking() { System.out.println(自动挡轿车A停车挂P档); } Override void shiftGear() { System.out.println(自动挡轿车A可换挡 P N D R); }}实现抽象产品——小轿车(手动挡)public class CommonCarH extends CommonCar { Override void parking() { System.out.println(手动挡轿车H停车挂空挡拉手刹); } Override void shiftGear() { System.out.println(手动挡轿车H可换挡 空 1 2 3 4 5 R); }}实现抽象产品——货车(自动挡)public class TruckA extends Truck { Override void parking() { System.out.println(自动挡货车A停车挂P档); } Override void shiftGear() { System.out.println(自动挡货车A可换挡 P N D R); }}实现抽象产品——货车(手动挡)public class TruckH extends Truck { Override void parking() { System.out.println(手动档货车H停车挂空挡拉手刹); } Override void shiftGear() { System.out.println(手动档货车H可换挡 空 1 2 3 4 5 R); }}(3) 创建抽象工厂public interface CarFactory { // 创建普通轿车 CommonCar createCommonCar(); // 创建货车 Truck createTruckCar();}(4) 实现抽象工厂通过自动挡手动挡这两个抽象概念创建出这两个工厂创建具有特定实现类的产品对象自动挡汽车工厂类public class AutomaticCarFactory implements CarFactory { Override public CommonCarA createCommonCar() { return new CommonCarA(); } Override public TruckA createTruckCar() { return new TruckA(); }}手动挡汽车工厂类public class HandShiftCarFactory implements CarFactory { Override public CommonCarH createCommonCar() { return new CommonCarH(); } Override public TruckH createTruckCar() { return new TruckH(); }}(5) 测试一下public class Test { public static void main(String[] args) { // 自动挡车工厂类 CarFactory automaticCarFactory new AutomaticCarFactory(); // 手动挡车工厂类 CarFactory handShiftCarFactory new HandShiftCarFactory(); System.out.println(自动挡轿车系列); CommonCar commonCarA automaticCarFactory.createCommonCar(); commonCarA.parking(); commonCarA.shiftGear(); System.out.println(自动挡货车系列); Truck truckA automaticCarFactory.createTruckCar(); truckA.parking(); truckA.shiftGear(); System.out.println(手动挡轿车系列); CommonCar commonCarH handShiftCarFactory.createCommonCar(); commonCarH.parking(); commonCarH.shiftGear(); System.out.println(手动挡货车系列); Truck truckH handShiftCarFactory.createTruckCar(); truckH.parking(); truckH.shiftGear(); }}运行结果自动挡轿车系列自动挡轿车A停车挂P档自动挡轿车A可换挡 P N D R自动挡货车系列自动挡货车A停车挂P档自动挡货车A可换挡 P N D R手动挡轿车系列手动挡轿车H停车挂空挡拉手刹手动挡轿车H可换挡 空 1 2 3 4 5 R手动挡货车系列手动档货车H停车挂空挡拉手刹手动档货车H可换挡 空 1 2 3 4 5 R补充两个概念产品等级结构产品的等级结构就是其继承结构例如上述代码中CommonCar(普通轿车) 是一个抽象类其子类有 CommonCarA (自动挡轿车)和 CommonCarH(手动挡轿车)则 普通轿车抽象类就与具体自动挡或者手动挡的轿车构成一个产品等级结构。产品族产品族是同一个工厂生产位于不同产品等级结构中的一组产品例如上述代码中CommonCarA(自动挡轿车)和 TruckA(自动挡货车)都是AutomaticCarFactory(自动挡汽车工厂)这个工厂生成的(6) 结构图看着结构图我们再捋一下首先 AbstractProductA 和 AbstractProductB 是两个抽象产品分别对应我们上述代码中的 CommonCar 和 Truck为什么是抽象的因为它们可以都有两种不同的实现即自动挡轿车和自动货车手动挡轿车和手动挡卡车ProductA1 和 ProductA2 和 ProductB1 和 ProductB2 就是具体的实现代表 CommonCarA 和 CommonCarH 和 TruckA 和 TruckH抽象工厂 AbstractFactory 里包含了所有产品创建的抽象方法ConcreteFactory1 和 ConcreteFactory2 就是具体的工厂通常是在运行时再创建一个 ConcreteFactory 的实例这个工厂再创建具有特定实现的产品对象也就是说为了创建不同的产品对象客户端应该使用不同的具体工厂(7) 反射配置文件实现优化抽象工厂说白了就是通过内容抽象的方式减少了工厂的数量同时在具体工厂我们可以这么用CarFactory factory new AutomaticCarFactory();具体工厂只需要在初始化的时候出现一次这也使得修改一个具体工厂也是比较容易的但是缺点也是非常明显当我想扩展一比如加一个拖拉机类型我就需要修改 CarFactory接口AutomaticCarFactory 类 HandShiftCarFactory 类(当然拖拉机貌似没有什么自动挡我只是为了举例子)还需要增加拖拉机对应的内容也就是说增加的基础上我还需要修改原先的三个类这是一个非常显著的缺点除此之外还有一个问题如果很多地方都声明了CarFactory factory new AutomaticCarFactory();并且进行了调用如果我更换了这个工厂就需要大量的进行修改很显然这一点是有问题的我们下面来使用反射优化一下public class Test { public static void main(String[] args) throws Exception { Properties properties new Properties(); // 使用ClassLoader加载properties配置文件生成对应的输入流 InputStream in Test.class.getClassLoader().getResourceAsStream(config.properties); // 使用properties对象加载输入流 properties.load(in); //获取key对应的value值 String factory properties.getProperty(factory); CarFactory automaticCarFactory (CarFactory) Class.forName(factory).newInstance(); System.out.println(轿车系列); CommonCar commonCarA automaticCarFactory.createCommonCar(); commonCarA.parking(); commonCarA.shiftGear(); System.out.println(货车系列); Truck truckA automaticCarFactory.createTruckCar(); truckA.parking(); truckA.shiftGear(); }}config.propertiesfactorycn.ideal.factory.abstractFactory.AutomaticCarFactory#factorycn.ideal.factory.abstractFactory.HandShiftCarFactory运行结果轿车系列自动挡轿车A停车挂P档自动挡轿车A可换挡 P N D R货车系列自动挡货车A停车挂P档自动挡货车A可换挡 P N D R通过反射配置文件我们就可以使得使用配置文件中的键值对(字符串)来实例化对象而变量是可以更换的也就是说程序由编译时转为运行时增大了灵活性去除了判断的麻烦回到前面的问题如果我们现在要增加一个新的内容内容的增加没什么好说的这是必须的这是扩展但是对于修改我们却要尽量关闭现在我们可以通过修改配置文件来达到实例化不同具体工厂的方式但是还需要修改三个类以添加新内容这里还可以通过简单工厂来进行优化也就是去掉这几个工厂使用一个简单工厂其中写入createCommonCar(); 等这些方法 再配合反射配置文件也能实现刚才的效果这样如果新增内容的时候只需要修改配置文件后再修改这一个类就可以即增加一个 createXXX 方法就不需要修改多个内容了七 结尾邮箱ideal_bwh163.com如果帮到你的话那就来关注我吧如果您更喜欢微信文章的阅读方式可以关注我的公众号如果您更加喜欢PC端的阅读方式可以访问我的个人博客域名www.ideal-20.cn在这里的我们素不相识却都在为了自己的梦而努力 ❤一个坚持推送原创开发技术文章的公众号理想二旬不止