Hi,大家好呀。昨天发的文章迟迟没有通过审核,今天一天都在和审核组斗智斗勇,问题是违规的内容在文章中压根就没有(QAQ)。唉,虽然经历了不少挫折但是生活还要继续对吧o(╥﹏╥)o。今天的主题是工厂方法模式,还是老样子,让我们先构建一个应用场景:
今天一位尊敬的甲方来到你的公司,准备请你们帮助定制开发一个软件。产品经理几乎快说断了三寸不烂之舌,甲方代表终于满意的点点头,大手一挥在合同上写下大名。陪着甲方走出公司的大门后,产品经理终于有机会连灌了一瓶水下肚,紧接着就开始了对你的摧残。甲方的要求是这样:
今天的甲方是一个实业家,手下掌握有大量的工厂,生产众多不同的产品。但是随着工厂规模的扩大,生产过程中的环节为管理产品带来了许多问题,他希望你们能够开发一个软件帮助他管理产品生产的过程。你们经过详细的调研,了解到一个产品在工厂中通常会经历以下三个阶段
为产品选择合适的模具
采用不同的工艺生产产品
对产品进行质量检测
试用产品
其工厂的代表产品是小葱手机和尖叫鸡,于是你们打算以此为模板率先开发一个demo。
产品经理将这项艰巨的工作交给了你,本着能摸鱼绝对不划水的态度,你从手下的一众小弟中挑出一个浓眉大眼,看起来温顺无害的小伙子。对他进行了一套画饼充饥的连招后,成功让他斗志满满的决定晚上加班搞定这个需求,你满意的点点头,看看墙上的钟,估摸着到点了,晚上女神还等着你去帮他拎包呢,于是迈着六亲不认的步伐麻溜的润了。
第二天来到公司,小伙子盯着黑眼圈站在你的办公桌前,一脸兴奋告诉你他已经完成了这个需求,正等着你毫无保留的夸奖。你坐在椅子上看着小伙提交上来的代码,感觉脑仁一阵突突的疼,不断告诉自己要冷静,谁还没有年轻过,年轻人都会犯错。
小伙提交上来的代码是这样的
public class PhoneFactory {
public void selectMold(){
System.out.println("选择大葱手机模具");
}
public void produce(){
System.out.println("生产大葱手机");
}
public void checkout(){
System.out.println("使用大葱手机拍核桃,检查质量是否合格");
}
}
public class Phone {
public void use(){
System.out.println("欢迎使用大葱手机");
}
}
public class ScreamingChickenFactory {
public void selectMold(){
System.out.println("选择尖叫鸡橡胶模具");
}
public void produce(){
System.out.println("生产尖叫鸡");
}
public void checkout(){
System.out.println("使劲捏捏尖叫鸡,看看会不会尖叫");
}
}
public class ScreamingChicken {
public void use(){
System.out.println("啊啊啊啊啊啊~");
}
}
以及调用他们的主类
public class Main {
public static void main(String[] args) {
// 大葱手机工厂
PhoneFactory phoneFactory = new PhoneFactory();
Phone phone = phoneFactory.create();
phone.use();
System.out.println("********************************");
//尖叫鸡工厂
ScreamingChickenFactory screamingChickenFactory = new ScreamingChickenFactory();
ScreamingChicken screamingChicken = screamingChickenFactory.create();
screamingChicken.use();
}
}
程序输出:
选择大葱手机模具
生产大葱手机
使用大葱手机拍核桃,检查质量是否合格
欢迎使用大葱手机
********************************
选择尖叫鸡橡胶模具
生产尖叫鸡
使劲捏捏尖叫鸡,看看会不会尖叫
啊啊啊啊啊啊~
大家不要太过在意业务的合理性,构建一个背景的目的只是为了可以让我们更好的理解知识,而不是被业务的细节所迷惑
你伸手揉了揉太阳穴问道,我们这只是用这两个产品做个demo,那客户那些其他产品的工厂怎么办?
小伙子一脸无辜的说:那我在加班加点都写上去就好啦。
你感觉你的血压蹭的往上涨了一截,年轻人能熬,你codereview可熬不住。沉默了一会,你长长叹了一口气,决定将你的独门绝技传授一部分给这个年轻的小伙子。
在模板方法模式中,我们在父类中规定处理的流程,在子类中实现具体的处理(什么?不知道什么是模板方法模式,快来这篇文章看看)。如果我们将该模式用于生成实例,它就演变为工厂方法模式。在工厂方法模式中,由父类决定实例的生成方式,但并不决定具体要生成的类。通过将具体的生成处理交给子类负责,这样就可以将生成实例的框架和负责生成实例的类解耦。
让我们看看用工厂方法模式如何实现上面的需求
public abstract class Factory {
/**
* 抽象的选择模具过程
*/
public abstract void selectMold();
/**
* 抽象的生产过程
* @return 产品
*/
public abstract Product produce();
/**
* 抽象的测试过程
*/
public abstract void checkout();
/**
* 定义规定好的生产流程
* @return 产品
*/
public Product create(){
this.selectMold();
Product product = this.produce();
this.checkout();
return product;
}
}
public class PhoneFactory extends Factory{
/**
* 实现具体的选择模具动作
*/
public void selectMold(){
System.out.println("选择大葱手机模具");
}
/**
* 实现具体的生产产品动作
* @return 大葱手机
*/
public Product produce(){
System.out.println("生产大葱手机");
return new Phone();
}
/**
* 实现具体的质量检测动作
*/
public void checkout(){
System.out.println("使用大葱手机拍核桃,检查质量是否合格");
}
}
public class ScreamingChickenFactory extends Factory{
/**
* 具体的选择模具动作
*/
public void selectMold(){
System.out.println("选择尖叫鸡橡胶模具");
}
/**
* 具体的生产尖叫鸡
* @return 尖叫鸡
*/
public Product produce(){
System.out.println("生产尖叫鸡");
return new ScreamingChicken();
}
/**
* 具体的质量检测动作
*/
public void checkout(){
System.out.println("使劲捏捏尖叫鸡,看看会不会尖叫");
}
}
public abstract class Product {
/**
* 抽象的使用产品动作
*/
public abstract void use();
}
public class Phone extends Product{
/**
* 具体的使用手机
*/
public void use(){
System.out.println("欢迎使用大葱手机");
}
}
public class ScreamingChicken extends Product {
/**
* 具体的使用尖叫鸡
*/
public void use(){
System.out.println("啊啊啊啊啊啊~");
}
}
调用他们的主类
public class Main {
public static void main(String[] args) {
// 大葱手机工厂
Factory factory = new PhoneFactory();
Product product = factory.create();
product.use();
System.out.println("********************************");
//尖叫鸡工厂
// 只需要在这里进行修改就可以改变产品
factory = new ScreamingChickenFactory();
product = factory.create();
product.use();
}
}
程序运行结果:
选择大葱手机模具
生产大葱手机
使用大葱手机拍核桃,检查质量是否合格
欢迎使用大葱手机
********************************
选择尖叫鸡橡胶模具
生产尖叫鸡
使劲捏捏尖叫鸡,看看会不会尖叫
啊啊啊啊啊啊~
通过对比,很容易可以得出,通过工厂方法模式,接下来如果还有其他的产品需要加入,我们只需要通过对类进行简单的拓展,就可以适配新的产品。
让我么从整体上来看看工厂方法模式的类图
创建者属于框架的一方,他负责创建Product这个抽象类,但是具体的处理由其子类来决定。在上文的例子中Factory担任创建者角色。创建者角色并不知道要生成哪种实例,只需要调用生成实例的方法就可以生成Product实例。
产品角色同样属于框架的一方,是一个抽象类。在这个角色中只定义了产品需要实现的接口,而具体的处理则交给其子类。
ConcreteProduct角色属于具体的加工方,他实现了父类Creator中的方法,决定了具体生产的产品。上文的例子中PhoneFactory和ScreamingChickenFactory都扮演了这个角色。
ConcreteProduct属于具体的加工方,他负责生成具体的产品。上文的例子中Phone和ScreamingChicken都扮演了这个角色。
通过上面的例子,如果大家对模板模式有所了解的话,应该可以看出工厂模式实际上与模板模式极其类似,都是通过父类定义标准和固定的处理流程,而具体的实现方法则交给子类实现,这样的模式可以极大的增加代码的灵活度和拓展性,希望大家能够掌握其中的精髓。
当然,这两个模式也有自己的缺点,其中之一就是如果开发者没有理解在类中定义的固定流程(通常在实际开发中,业务逻辑会比上文例子中复杂数十倍),这就会导致开发者在开发其具体子类时遇到极大困难。因此我们在使用这些模式时,一定要向维护这些类的开发者准确传达这些设计模式的目的。否则将会适得其反。
一文搞懂设计模式–外传
一文搞懂设计模式–迭代器模式
一文搞懂设计模式–适配器模式
一文搞懂设计模式–模板模式