关于一个工厂的故事
话说有一个生产电视机的工厂,可以生产海信电视机、海尔电视机。程序设计如下:
/**
* 电视机工厂
* @author zherop
*/
public class TVFactory {
/**
* 生产海尔电视机
*
* @return
*/
public HaierTV createHaierTV() {
return new HaierTV();
}
/**
* 生产海信电视机
*
* @return
*/
public HisenseTV createHisenseTV() {
return new HisenseTV();
}
}
/**
* 海尔电视机
* @author zherop
*/
public class HaierTV {
public void play() {
System.out.println("海尔电视机播放...");
}
}
/**
* 海信电视机
* @author zherop
*/
public class HisenseTV {
public void play() {
System.out.println("海信电视机播放...");
}
}
一切都是那么美好,随着工厂业务的发展,现在又需要生产华为电视机。这个时候,厂长找到工程师小曾,让他给出个设计方案。
于是小曾稍作思考,并想到如下方案:
新增 HuaweiTV,然后修改 TVFactory
/**
* 华为电视机
* @author zherop
*/
public class HuaweiTV {
public void play() {
System.out.println("华为电视机播放...");
}
}
public class TVFactory {
/**
* 生产华为电视机
* @return
*/
public HuaweiTV createHuaweiTV() {
return new HuaweiTV();
}
}
咋一看,这么设计还挺自然的。小曾又想了想,似乎不太对,新增了一个品牌的电视机,就需要去整改一直运作良好的工厂,心想,万一整改的过程中,不小心影响了其他产品线咋办?
于是,小曾陷入沉思,电视机要定一个标准,然后针对这个标准,工厂产品线就可以生产出来,于是乎,方案变成了这样:
电视机标准:
public interface TV {
public void play();
}
所有品牌电视机都遵循这个标准:
/**
* 海尔电视机
* @author zherop
*/
public class HaierTV implements TV {
public void play() {
System.out.println("海尔电视机播放...");
}
}
/**
* 海信电视机
* @author zherop
*/
public class HisenseTV implements TV {
public void play() {
System.out.println("海信电视机播放...");
}
}
/**
* 华为电视机
* @author zherop
*/
public class HuaweiTV implements TV {
public void play() {
System.out.println("华为电视机播放...");
}
}
工厂产品线改造:
/**
* 电视机工厂
* @author zherop
*/
public class TVFactory {
/**
* 生产电视机
*
* @return
*/
public <T extends TV> TV createTV(Class<T> tv) {
try {
return tv.newInstance();
} catch (Exception e) {
e.printStackTrace();
System.out.println("生产过程中发生故障!");
}
return null;
}
}
工厂生产测试:
public class Client {
public static void main(String[] args) {
TVFactory tvFactory = new TVFactory();
// 生产海尔电视机
TV tv = tvFactory.createTV(HaierTV.class);
// 测试电视机是否正常工作
tv.play();
}
}
这样,即使以后再接其他品牌的单,工厂也不用改造了。小曾顿时心情愉悦,很有成就感。
就这样,靠着这个万能的产品线,工厂稳步发展…
随着科技的进步,开始出现了新型的电视机——智慧屏。智慧屏的生产比普通电视机要复杂得多,工厂现在的设计已经不能满足需求了,厂长又开始着急了,不接这个单?那显示是不可能,如果不更新换代,工厂迟早会淘汰。于是厂长又找到了小曾。
小曾思考了下,很自然地想到了把生成电视机的产品线改造下:
/**
* 电视机工厂
* @author zherop
*/
public class TVFactory {
/**
* 生产电视机
*
* @return
*/
public <T extends TV> TV createTV(Class<T> tv) {
try {
T product = tv.newInstance();
// 如果是智慧屏,特殊加工处理
if (tv.getSimpleName().equals("HonorSmartTV")) {
processSmartTV(product);
}
return product;
} catch (Exception e) {
e.printStackTrace();
System.out.println("生产过程中发生故障!");
}
return null;
}
private void processSmartTV(TV product) {
System.out.println("智慧屏加工!");
}
}
/**
* 荣耀智慧屏
* @author zherop
*/
public class HonorSmartTV implements TV {
public void play() {
System.out.println("荣耀智慧屏播放...");
}
}
小曾又想了想,咋又要动现有的产品线?不得行,不得行!现在虽然轻松,以后要是再出现其他新型电视机,又要改动,老是改来改去,迟早要出事!
经过一番苦想,既然电视生产的具体流程没法统一,何不建造新的工厂,由特定的工厂来针对性地生产特定的电视机呢?又想到了如下方案:
要建立多个工厂,那么就需要有工厂的标准,工厂是用来生产电视机,而不是来干其他事情的。
电视机工厂建设标准:
/**
* 电视机工厂
* @author zherop
*/
public interface TVFactory {
/**
* 生产电视机
*
* @return
*/
public TV createTV();
}
建造具体工厂:
/**
* 海尔电视机工厂
* @author zherop
*/
public class HaierTVFacatory implements TVFactory {
public TV createTV() {
return new HaierTV();
}
}
/**
* 海信电视机工厂
* @author zherop
*/
public class HisenseTVFactory implements TVFactory {
public TV createTV() {
return new HisenseTV();
}
}
/**
* 荣耀智慧屏工厂
* @author zherop
*/
public class HonorSmartTVFactory implements TVFactory {
public TV createTV() {
return new HonorSmartTV();
}
}
测试:
public class Client {
public static void main(String[] args) {
// 使用荣耀智慧屏工厂
TVFactory tvFactory = new HonorSmartTVFactory();
TV tv = tvFactory.createTV();
// 测试电视机是否正常工作
tv.play();
}
}
嘿嘿,小曾心想,这样工厂以后灵活性很高了,无论科技怎么发展,工厂都能轻松应对。
看到这儿,故事就差不多结束了,至此工厂方法模式也讲得差不多了,各种变体也蕴含其中。设计模式虽是招式,但是需要根据不同的场景,进行灵活变动,不能太死板了。
定义
工厂方法模式定义:工厂方法模式又称工厂模式,工厂父类负责定义创建产品对象的公共接口,而工程子类则负责生成具体的产品对象,这样做的目的是将产品类的实例化操作延迟到工厂子类中完成,即通过工厂子类来确定究竟应该实例化哪一个具体产品类。
涉及的角色:
-
抽象产品(Product)
抽象产品是定义产品的接口。比如故事中的TV类。 -
具体产品(ConcreteProduct)
具体产品实现了抽象产品接口,某种类型的具体产品由专门的具体工厂创建,它们之间一一对应。比如故事中的HisenseTV、HonorSmartTV、HaierTV类。 -
抽象工厂(Factory)
在抽象工厂类中,声明了工厂方法,用于返回一个产品。比如故事中的TVFactory类。 -
具体工厂(ConcreteFactory)
具体工厂类是抽象工厂类的子类,实现了抽象工厂中定义的工厂方法,并可由客户端调用,返回一个具体产品类的实现。比如故事中的HisenseTVFactory、HonorSmartTVFactory类。
类图:
工厂方法模式,扩展性非常好,符合开闭原则,是典型的解耦框架。高层模块只需要知道产品的抽象类,其他实现类都不需要关心,符合迪米特法则;只依赖于产品类的抽象,符合依赖倒置原则;也符合里氏代换原则。
理论上,工厂方法模式在所有需要生成对象的地方都可以使用,但是需要考虑是否要增加一个工厂类来进行管理,毕竟会增加代码的复杂度。