工厂方法模式和抽象工厂方法模式的battle
1.案例直接上手
在这个案例里面,我们会实现这个普通的工厂方法,并且对比这个普通工厂方法和我们直接创建对象的差别在哪里,为什么需要一个工厂:
下面的这个是我们的这个案例里面涉及到的接口和对应的实现类:
两个发送的方式,一个实现类是使用的邮件的形式,一个实现类使用的短信的方式
下面的这个是我们的工厂,里面有一个方法,负责生产我们的对象:我们使用上面定义的接口进行返回值的接受,因为我们的这个返回值可能是不同的实现类,我们使用他们的共同实现的接口作为返回值的类型;
下面的这个代码的逻辑其实也是非常的简单,就是我们的这个输入的参数类型和我们的邮件一样,这个时候我们创建一个邮件的对象,如果和消息一样,我们创建的就是消息类型的这个对象,否则我们直接返回null即可;
我们写一个测试类进行测试:传入这个mail参数,这个时候返回的就是我们的mail类型对象,调用里面的send方法;
下面的这个是输出的结果,相信大家是很容易理解的:
2.总结思考
上面的这个案例里面,我们定义了一个工厂,但是这个实现的效果显的很low,为什么这样说,因为这个过程我们两行代码就可以搞定;
我们可以直接创建一个mailSender对象,直接调用方法,岂不是更爽???
实现的这个输出的效果完全一致,没有任何的区别:
那么这样的话,我就有问题了,我们的这个工厂方法的优势体现在哪里呢,我们是不是不需要学习这个设计模式了呢,答案肯定是否定的,下面我们开始进行延伸,展示一下这个工厂的优势:
3.工厂的优势
如果这个时候创建200个对象,上面的两个做法的区别其实也不是很大,工厂是produce创建对象,而普通方法是直接new对象即可,调用对应的方法;
可见,这个情境下,我们的工厂方法仍然没有凸显自己的优势,但是如果我告诉你,每一次创建对象之前,我们需要打印“我喜欢学习设计模式”,阁下又应该如何应对呢;
使用普通的方法创建对象,当需要打印这句话的需求提出之后,我们每一次创建对象,都需要去打印这句话,200个对象需要打印200次,这样写的代码就非常的不美观,因为这个重复的内容太多了;
这个时候,我们的工厂方法就凸显了自己的优势:我们只需要在这个工厂方法里面添加一句话,这个时候我们的这个所有的主函数里面的对象调用这个produce的时候就可以打印结果了,而不需要重复200次,这就是提升;从200遍到1遍,这个就提升了不少;
如果这个时候客户又让你把那句话删除掉,这个时候工厂方法只需要删除一个地方,而我们的普通方法全部需要删除;
工厂方法:需要大量的创建对象的时候,使用工厂方法,提高代码的可维护性;
如果我们需要更多的实现类,我们的这个工厂方法也是会凸显优势的,方便对于这个实现类的管理;
4.多个工厂方法的改进
即使如此,我们使用了工厂模式,上面的这个代码依然是可以进行优化和改进的,如下所示,如果我们在这个测试类里面把这个参数传递错误了,对吧,我们的这个用户不小心在这个参数里面mail后面多加上了一个字符,这个时候就会出现无法对应的情况;
下面的这个就是出现的异常,因为我们使用null调用这个send方法了,我们没有找到对应的mail或者是这个sms,这个时候s接收到的就是null,然后使用这个null调用我们的方法肯定就是不可取的了;
这个时候,就不得不提我们的这个多个工厂方法了,如下所示:增加工厂方法,意味着我们可以直接调用这个增加的工厂方法:
这样的话,我们的测试类里面不需要进行这个参数的传递的过程,而是可以直接进行我们的新的方法的调用,这样就可以杜绝上面的那个参数传递错误的情况,而是直接调用对应的方法即可:
5.静态工厂方法
上面的这个例子,我们需要先创建对象,然后去调用里面的相关的方法,这个时候我们可以继续优化,把原来的这个换成我们的静态的方法,这样就可以直接使用我们的类调用相关的方法即可,不需要进行这个new对象的操作;
然后我们的这个测试类里面可以直接调用方法,不用创建对象,这个就是我们的静态方法的好处:
6.抽象工厂方法模式
接下来,我们继续扩展,这个时候,如果我们需要在原来的发送邮件,发送信息的基础上面,添加这个发送包裹,我们会如何修改;
如果使用原来的方法,就需要在我们的工厂里面增加方法,然后去定义一个包裹类,实现我们原来定义的接口,但是这样做就违反了我们的开闭原则(测试之后不要再原来的基础上进行修改,可以增加,但是不要动原来的代码);
因此,我们引入了下面的这个抽象工厂方法:
通过下面的这个类图你可以是可以看出来的,我们的这个抽象工厂方法模式在原来的基础上进行了二次封装,定义了新的接口,两个工厂:
1)原来是一个工厂里面两个方法,我们需要增加包裹发送方式的时候,需要在工厂里面添加方法,违反了开闭原则;
2)抽象工厂方法里面,我们定义两个工厂,一个发送方式对应一个工厂,我们需要增加这个新的发送方式的时候,原来的代码都不需要变化,直接添加新的工厂里面实现这个方法即可;
3)这样的话,我们的抽象工厂方法就解决了我们的简单工厂方法的这个违反了开闭原则的问题,提高了代码的封装性和可扩展性;
样的话,我们的抽象工厂方法就解决了我们的简单工厂方法的这个违反了开闭原则的问题,提高了代码的封装性和可扩展性;