工厂模式:关于一个工厂的故事

1,234 阅读5分钟

关于一个工厂的故事

话说有一个生产电视机的工厂,可以生产海信电视机、海尔电视机。程序设计如下:

/**
 * 电视机工厂
 * @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();
	}
}

嘿嘿,小曾心想,这样工厂以后灵活性很高了,无论科技怎么发展,工厂都能轻松应对。

看到这儿,故事就差不多结束了,至此工厂方法模式也讲得差不多了,各种变体也蕴含其中。设计模式虽是招式,但是需要根据不同的场景,进行灵活变动,不能太死板了。

定义

工厂方法模式定义:工厂方法模式又称工厂模式,工厂父类负责定义创建产品对象的公共接口,而工程子类则负责生成具体的产品对象,这样做的目的是将产品类的实例化操作延迟到工厂子类中完成,即通过工厂子类来确定究竟应该实例化哪一个具体产品类。

涉及的角色:

  1. 抽象产品(Product):抽象产品是定义产品的接口。比如故事中的TV类。
  2. 具体产品(ConcreteProduct):具体产品实现了抽象产品接口,某种类型的具体产品由专门的具体工厂创建,它们之间一一对应。比如故事中的HisenseTV、HonorSmartTV、HaierTV类。
  3. 抽象工厂(Factory):在抽象工厂类中,声明了工厂方法,用于返回一个产品。比如故事中的TVFactory类。
  4. 具体工厂(ConcreteFactory):具体工厂类是抽象工厂类的子类,实现了抽象工厂中定义的工厂方法,并可由客户端调用,返回一个具体产品类的实现。比如故事中的HisenseTVFactory、HonorSmartTVFactory类。

类图:

工厂方法模式类图

工厂方法模式,扩展性非常好,符合开闭原则,是典型的解耦框架。高层模块只需要知道产品的抽象类,其他实现类都不需要关心,符合迪米特法则;只依赖于产品类的抽象,符合依赖倒置原则;也符合里氏代换原则。

理论上,工厂方法模式在所有需要生成对象的地方都可以使用,但是需要考虑是否要增加一个工厂类来进行管理,毕竟会增加代码的复杂度。

完整教程请移步 专栏:设计模式·观想录