Java设计模式之工厂模式

209 阅读6分钟

参考自:工厂模式——看这一篇就够了

工厂模式

  • 简单工厂
  • 工厂方法
  • 抽象工厂

简单工厂

我们以战士、射手、法师为示例。

当我们想创建这三个职业的对象时,可以新建一个英雄接口,所有继承了这个接口的对象都要实现攻击函数:

package com.cc.factory.simpleFactory;

/**
 * 英雄接口
 * @author cc
 * @date 2021-12-16 17:34
 */
public interface Hero {
    // 英雄可以进行攻击
    void attack();
}

然后分别创建战士Warrior、射手Shooter以及法师Master:

package com.cc.factory.simpleFactory;

/**
 * 战士
 * @author cc
 * @date 2021-12-16 17:39
 */
public class Warrior implements Hero {
    @Override
    public void attack() {
        System.out.println("战士攻击");
    }
}
package com.cc.factory.simplefactory;

/**
 * 射手
 * @author cc
 * @date 2021-12-16 17:41
 */
public class Shooter implements Hero {
    @Override
    public void attack() {
        System.out.println("射手攻击");
    }
}
package com.cc.factory.simplefactory;

/**
 * 法师
 * @author cc
 * @date 2021-12-16 17:41
 */
public class Master implements Hero {
    @Override
    public void attack() {
        System.out.println("法师攻击");
    }
}

接下来是生产英雄的简单工厂HeroSimpleFactory,这个工厂根据传入参数的不同来实例化不同对象:

package com.cc.factory.simpleFactory;

import com.cc.factory.simplefactory.Hero;
import com.cc.factory.simplefactory.Master;
import com.cc.factory.simplefactory.Shooter;
import com.cc.factory.simplefactory.Warrior;

/**
 * 英雄简单工厂
 * @author cc
 * @date 2021-12-16 17:42
 */
public class HeroSimpleFactory {
    public static Hero getHero(String type) {
        Hero hero = null;
        if ("warrior".equalsIgnoreCase(type)) {
            hero = new Warrior();
        } else if ("shooter".equalsIgnoreCase(type)) {
            hero = new Shooter();
        } else if ("master".equalsIgnoreCase(type)) {
            hero = new Master();
        }
        return hero;
    }
}

在main函数中调用这个英雄的简单工厂测试一下:

package com.cc.factory.simplefactory;

public class Main {
    public static void main(String[] args) {
        {
            Hero hero = HeroSimpleFactory.getHero("warrior");
            hero.attack();
        }
        {
            Hero hero = HeroSimpleFactory.getHero("shooter");
            hero.attack();
        }
        {
            Hero hero = HeroSimpleFactory.getHero("master");
            hero.attack();
        }
    }
}

结果:

战士攻击
射手攻击
法师攻击
简单工厂总结

通过这个示例,我们可以看出来,简单工厂的实现十分简单,所以如果系统本身不复杂的话可以采用简单工厂方案。

但是缺点也是明显的,比如如果我们设计出了新的英雄比如精灵、矮人,就要修改简单工厂的源代码,这违背了开闭原则(软件应该对拓展开放,对修改关闭,其含义是说软件应该通过扩展来实现变化,而不是通过修改已有代码来实现变化)。

所以为了遵循开闭原则,我们有了工厂方法模式。

工厂方法

还是上面关于英雄的示例,我们新建一个接口HeroFactory:

package com.cc.factory.factorymethod;

/**
 * 英雄工厂
 * @author cc
 * @date 2021-12-16 17:55
 */
public interface HeroFactory {
    // 获取英雄
    void getHero();
}

然后新建三个英雄的工厂,工厂需要实现HeroFactory接口:

package com.cc.factory.factorymethod;

public class WarriorFacrtory implements HeroFactory {
    @Override
    public Hero getHero() {
        return new Warrior();
    }
}
package com.cc.factory.factorymethod;

public class ShooterFacrtory implements HeroFactory {
    @Override
    public Hero getHero() {
        return new Shooter();
    }
}
package com.cc.factory.factorymethod;

public class MasterFacrtory implements HeroFactory {
    @Override
    public Hero getHero() {
        return new Master();
    }
}

测试一下:

package com.cc.factory.factorymethod;

public class Main {
    public static void main(String[] args) {
        {
            HeroFactory factory = new WarriorFacrtory();
            Hero hero = factory.getHero();
            hero.attack();
        }
        {
            HeroFactory factory = new ShooterFacrtory();
            Hero hero = factory.getHero();
            hero.attack();
        }
        {
            HeroFactory factory = new MasterFacrtory();
            Hero hero = factory.getHero();
            hero.attack();
        }
    }
}

结果:

战士攻击
射手攻击
法师攻击

这就是工厂方法的使用,和简单工厂不同的地方是,当我们增加了新的英雄之后,要创建对应的生产工厂,让子类工厂去实现英雄的创建,以拓展的形式实现功能,这符合开闭原则。

优点:工厂模式有十分好的拓展性,每个工厂只负责一个任务,这也符合了单一职责原则(即一个类只负责做一件事)

工厂方法总结

工厂方法有一个局限性,就是只创建一个对象,假设我们现在需要创建一组对象,比如英雄除了有attack()攻击行为,还会有武器装备,战士的武器装备是长剑、重甲;射手的武器装备的弓箭、皮甲;法师的武器装备是魔杖、布甲,

那么在工厂方法中,仅一个战士英雄,我们就需要创建英雄、长剑、重甲三个工厂类,这样的架构设计并不好,所以我们又有了抽象工厂。

抽象工厂

抽象工厂AbstractFactory声明了一组用于创建对象的方法,让我们来创建一个英雄的抽象工厂:

package com.cc.factory.abstractfactory;

/**
 * 英雄抽象工厂
 * @author cc
 * @date 21-12-16 23:07
 */
public abstract class HeroAbstractFactory {
    abstract Hero createHero();
    abstract Weapon createWeapon();
    abstract Equip createEquip();
}

这个英雄抽象工厂声明了三个方法,分别是创建英雄、创建武器和创建装备,所有继承了这个英雄抽象工厂的工厂类都要实现这三个方法,这样就实现了生产一组对象的目的。

先创建武器和装备的接口类:

package com.cc.factory.abstractfactory;

/**
 * 武器
 * @author cc
 * @date 21-12-16 23:07
 */
public interface Weapon {
    // 创建武器
    void action();
}
package com.cc.factory.abstractfactory;

/**
 * 装备
 * @author cc
 * @date 21-12-16 23:09
 */
public interface Equip {
    // 创建装备
    void action();
}

然后分别创建长剑、弓箭、魔杖和重甲、皮甲、布甲的类:

package com.cc.factory.abstractfactory;

/**
 * 长剑类
 * @author cc
 * @date 21-12-16 23:11
 */
public class Sword implements Weapon {
    @Override
    public void action() {
        System.out.println("创建长剑");
    }
}

package com.cc.factory.abstractfactory;

/**
 * 弓箭类
 * @author cc
 * @date 21-12-16 23:12
 */
public class Bow implements Weapon {
    @Override
    public void action() {
        System.out.println("创建弓箭");
    }
}
package com.cc.factory.abstractfactory;

/**
 * 魔杖类
 * @author cc
 * @date 21-12-16 23:19
 */
public class Wand implements Weapon {
    @Override
    public void action() {
        System.out.println("创建魔杖");
    }
}

package com.cc.factory.abstractfactory;

/**
 * 重甲类
 * @author cc
 * @date 21-12-16 23:15
 */
public class HeavyArmor implements Equip {
    @Override
    public void action() {
        System.out.println("创建重甲");
    }
}
package com.cc.factory.abstractfactory;

/**
 * 轻甲类
 * @author cc
 * @date 21-12-16 23:15
 */
public class LeatherArmor implements Equip {
    @Override
    public void action() {
        System.out.println("创建轻甲");
    }
}
package com.cc.factory.abstractfactory;

/**
 * 布甲类
 * @author cc
 * @date 21-12-16 23:16
 */
public class ClothArmor implements Equip {
    @Override
    public void action() {
        System.out.println("创建布甲");
    }
}

现在可以创建英雄的生产工厂了:

package com.cc.factory.abstractfactory;

/**
 * 战士生产工厂
 * @author cc
 * @date 21-12-16 23:10
 */
public class WarriorFactory extends HeroAbstractFactory {
    @Override
    Hero createHero() {
        return new Warrior();
    }

    @Override
    Weapon createWeapon() {
        return new Sword();
    }

    @Override
    Equip createEquip() {
        return new HeavyArmor();
    }
}
package com.cc.factory.abstractfactory;

/**
 * 射手生产工厂
 * @author cc
 * @date 21-12-16 23:29
 */
public class ShooterFactory extends HeroAbstractFactory {
    @Override
    Hero createHero() {
        return new Shooter();
    }

    @Override
    Weapon createWeapon() {
        return new Bow();
    }

    @Override
    Equip createEquip() {
        return new LeatherArmor();
    }
}
package com.cc.factory.abstractfactory;

/**
 * 法师生产工厂
 * @author cc
 * @date 21-12-16 23:30
 */
public class MasterFactory extends HeroAbstractFactory {
    @Override
    Hero createHero() {
        return new Master();
    }

    @Override
    Weapon createWeapon() {
        return new Wand();
    }

    @Override
    Equip createEquip() {
        return new ClothArmor();
    }
}

测试一下:

package com.cc.factory.abstractfactory;

public class Main {
    public static void main(String[] args) {
        {
            HeroAbstractFactory factory = new WarriorFactory();
            Hero hero = factory.createHero();
            Weapon weapon = factory.createWeapon();
            Equip equip = factory.createEquip();

            hero.attack();
            weapon.action();
            equip.action();
        }
        {
            HeroAbstractFactory factory = new ShooterFactory();
            Hero hero = factory.createHero();
            Weapon weapon = factory.createWeapon();
            Equip equip = factory.createEquip();

            hero.attack();
            weapon.action();
            equip.action();
        }
        {
            HeroAbstractFactory factory = new MasterFactory();
            Hero hero = factory.createHero();
            Weapon weapon = factory.createWeapon();
            Equip equip = factory.createEquip();

            hero.attack();
            weapon.action();
            equip.action();
        }
    }
}

结果:

战士攻击
创建长剑
创建重甲

射手攻击
创建弓箭
创建轻甲

法师攻击
创建魔杖
创建布甲

测试代码中我们可以看到,通过抽象工厂的实现,可以针对不同的工厂子类创建不同的英雄,并且对英雄的一组对象如武器装备进行实例化。

优点:抽象工厂可以在类的内部对组对象进行关联和定义,而不需要引入新的管理对象。

缺点:组对象的拓展会十分麻烦,如上示例,假如我们需要给英雄再添加一个释放技能的行为,需要修改所有的工厂方法,这又违背了开闭原则。

其实,当抽象工厂中只有一个组件的话,本质上和工厂方法是差不多的,具体要使用什么设计模式,还是要根据场景随机应变。