创建型模式 - 工厂模式(创建单一产品)

工厂模式

主要分为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与创建的工厂方法类相符

小结

  1. 简单工厂没有父类,创建具体的Product对象需要用户传入不同参数;工厂模式有父类,创建具体的Product无需用户传入不同参数,只需创建不同的具体工厂对象;
  2. 工厂模式具体的子类跟产品具体子类对应关系是一一对应,即一个具体工厂只生产一种产品;
  3. 要扩展一个具体的产品对象时,只需要添加一个新的工厂

参考

  1. 工厂模式, 菜鸟教程
  2. 三种工厂模式的分析以及C++实现, 博客园
  3. 设计模式之Factory
  4. GOF design patterns , YouTube
posted @ 2018-08-23 00:56  明明1109  阅读(307)  评论(0编辑  收藏  举报