创建型模式 - 工厂模式(创建单一产品)
工厂模式
主要分为2种:简单工厂,工厂方法。有别于抽象工厂。
简单工厂
1. 简介
client通过给SimleFactory传不同参数,由SimpleFactory根据实际情况决定如何初始化具体的Product对象。
适用场景:替代new来创建对象;不希望client直接处理Product创建细节;Product类型比较确定,不会经常变动;Product子类可以通过同一种创建方法创建,包括传入的参数数量、类型一致;
特点:实现简单,能对client屏蔽创建Product细节;
缺点: 当需要增加/删除Product子类时,需要修改Factory类;当Product子类较多时,Factory容量就会很大,复杂度变高;
2. 通用类图
3. 示例
简单工厂举个有意思的例子(参考文档4:youtube视频):
EnemyShipTesting(client端)要创建EnemyShip,如果没有SimpleFactory,就要在EnemyShipTesting中处理各种与EnemyShip及其子类相关的细节。而设置了EnemyShipSimpleFactory之后,这些工作细节都可以交给SimpleFactory来做,client端只用关注需要选择创建什么EnemyShip类型即可,而不用关系如何创建,也不用知道具体的EnemyShip子类。类图如下:
4. 实现代码
EnemyShip产品抽象类
// EnemyShip.java
public abstract class EnemyShip {
private String name;
private double amtDamage;
public void setName(String newName) { name = newName; }
public String getName() { return name; }
public void setDamage(double newDamage) { amtDamage = newDamage; }
public double getDamage() { return amtDamage; }
public void followHeroShip(){
System.out.println(getName() + " is following the hero.");
}
public void displayEnemyShip(){
System.out.println(getName() + " is on the screen.");
}
public void enemyShipShoots(){
System.out.println(getName() + " attacks and does " + getDamage() + " damage.");
}
}
EnemyShip产品子类UFOEnemyShip
// UFOEnemyShip.java
public class UFOEnemyShip extends EnemyShip {
public UFOEnemyShip() {
setName("UFO Enemy Ship");
setDamage(20.0);
}
}
EnemyShip产品子类RocketEnemyShip
// RocketEnemyShip.java
public class RocketEnemyShip extends EnemyShip {
public RocketEnemyShip(){
setName("Rocket Enemy Ship");
setDamage(10.0);
}
}
EnemyShip产品子类BigUFOEnemyShip
// BigUFOEnemyShip.java
public class BigUFOEnemyShip extends EnemyShip {
public BigUFOEnemyShip() {
setName("Big UFO Enemy Ship");
setDamage(30.0);
}
}
SimpleFactory对应具体的EnemyShipSimpleFactory
// EnemyShipSimpleFactory.java
public class EnemyShipSimpleFactory {
// SimpleFactory 根据传入参数创建不同的EnemyShip产品对象
public EnemyShip makeEnemyShip(String enemyShipType) {
EnemyShip enemyShip = null;
if(enemyShipType.equals("R")) {
enemyShip = new RocketEnemyShip();
} else if(enemyShipType.equals("U")) {
enemyShip = new UFOEnemyShip();
} else if(enemyShipType.equals("B")) {
enemyShip = new BigUFOEnemyShip();
}
return enemyShip;
}
}
client对应EnemyShipTesting
// EnemyShipTesting.java
public class EnemyShipTesting {
public static void main(String[] args) {
Scanner userInput = new Scanner(System.in);
System.out.println("What type of enemy?( U / R / B)");
String enemyShipOption = "";
EnemyShip enemyShip = null;
// client 根据具体情况给SimpleFactory传送参数, 由SimpleFactory决定如何初始化具体的EnemyShip产品对象
if (userInput.hasNextLine() ) {
enemyShipOption = userInput.nextLine();
EnemyShipSimpleFactory factory = new EnemyShipSimpleFactory();
enemyShip = factory.makeEnemyShip(enemyShipOption);
doStuffEnemy(enemyShip);
}
}
/*
public static void main(String[] args) {
EnemyShip ufoShip = null;
Scanner userInput = new Scanner(System.in);
String enemyShipOption = "";
System.out.println("What type of ship?(U / R)");
// 在client中根据选择, 创建不同的EnemyShip产品对象
if (userInput.hasNextLine()) {
enemyShipOption = userInput.nextLine();
if(enemyShipOption.equals("R")) {
ufoShip = new RocketEnemyShip();
}else if(enemyShipOption.equals("U")) {
ufoShip = new UFOEnemyShip();
}
doStuffEnemy(ufoShip);
}
}
*/
public static void doStuffEnemy(EnemyShip anEnemyShip){
anEnemyShip.displayEnemyShip();
anEnemyShip.followHeroShip();
anEnemyShip.enemyShipShoots();
}
5. 测试结果
工厂方法
1. 简介
提供一个统一的抽象接口(工厂方法)用来,让(工厂)子类决定实例化哪一个类,使对象的初始化延迟到(工厂)子类进行。
适用场景:替代new来创建对象;具体产品会经常变动(添加);
特点:每一个具体的工厂只生产一种产品;扩展一个新产品时,只需要添加对应具体的工厂即可,而且不用修改原工厂;
缺点:当要生成的具体产品很多时,要添加的具体工厂就会很多;
2. 通用类图
3. 示例 UML类图
demo:针对上面简单工厂的例子,进行修改
4. 实现代码
几个Product类EnemyShip, UFOEnemyShip, RocketEnemyShip, BigUFOEnemyShip可以不用变动。针对每一个Product类新添加对应的工厂类,来生产Product(一一对应)。
EnemyShipFacotry<--> EnemyShip
// EnemyShipFacotry.java
public interface EnemyShipFacotry {
public EnemyShip makeEnemyShip();
}
UFOEnemyShipFactory<-->UFOEnemyShip
// UFOEnemyShipFactory.java
public class UFOEnemyShipFactory implements EnemyShipFacotry {
@Override
public EnemyShip makeEnemyShip(){
return new UFOEnemyShip();
}
}
RocketEnemyShipFactory<-->RocketEnemyShip
// RocketEnemyShipFactory.java
public class RocketEnemyShipFactory implements EnemyShipFacotry{
@Override
public EnemyShip makeEnemyShip() {
return new RocketEnemyShip();
}
}
BigUFOEnemyShipFactory<-->BigUFOEnemyShip
// BigUFOEnemyShipFactory.java
public class BigUFOEnemyShipFactory implements EnemyShipFacotry{
@Override
public EnemyShip makeEnemyShip() {
return new BigUFOEnemyShip();
}
}
客户端测试类EnemyShipTesting
// EnemyShipTesting 测试类
public class EnemyShipTesting {
public static void main(String[] args) {
EnemyShip enemyShip = null;
// 创建具体的工厂模式()
EnemyShipFacotry enemyShipFacotry = new BigUFOEnemyShipFactory();
// 调用工厂方法创建对象
enemyShip = enemyShipFacotry.makeEnemyShip();
// 测试创建对象
doStuffEnemy(enemyShip);
}
public static void doStuffEnemy(EnemyShip anEnemyShip){
anEnemyShip.displayEnemyShip();
anEnemyShip.followHeroShip();
anEnemyShip.enemyShipShoots();
}
}
5. 测试结果
生产的Product与创建的工厂方法类相符
小结
- 简单工厂没有父类,创建具体的Product对象需要用户传入不同参数;工厂模式有父类,创建具体的Product无需用户传入不同参数,只需创建不同的具体工厂对象;
- 工厂模式具体的子类跟产品具体子类对应关系是一一对应,即一个具体工厂只生产一种产品;
- 要扩展一个具体的产品对象时,只需要添加一个新的工厂