设计模式之简单工厂、工厂、抽象工厂模式

187 阅读8分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第3天,点击查看活动详情

简述:工厂模式是属于创建型模式,这种模式的初衷就是为了不在代码中显示new一个实例化对象,而是使用某种方法“生产”出来,工厂模式顾名思义就是用工厂生产对象。

一、 简单工厂模式,又称静态工厂(Static Factory Method)模式。

可以根据不同的参数返回不同的对象实例。就是从最我们最熟知最基础的每次手动new一个实例对象转变成工厂new;

简单工厂有三种角色,Factory(工厂角色)、Product(抽象产品角色)、ConcreteProduct(具体产品类),可以增加一个client客户类作调用。流程就是client通过factory获取一个ConcreteProduct。

简单工厂模式实际上没什么技术可言,并且违背了开闭原则,并不属于23种设计模式。(1995 年,GoF(Gang of Four,四人组/四人帮)合作出版了《设计模式:可复用面向对象软件的基础》一书,共收录了 23 种设计模式,从此树立了软件设计模式领域的里程碑,人称「GoF设计模式」。)

类图如下:

image.png

// 兵器--Product(抽象产品角色)
public interface Weapon {
    // 战斗
    String fight();
}
// 剑--ConcreteProduct(具体产品类)
public class Sword implements Weapon{
    @Override
    public String fight() {
        return "我有一剑,可以搬山海!";
    }
}

// 枪(矛)--ConcreteProduct(具体产品类)
public class Spear implements Weapon{
    @Override
    public String fight() {
        return "我有一枪,可以破苍穹!";
    }
}

// 铁匠铺--Factory(工厂角色)
public class Smithy {
    public static Weapon getWeapon(String weaponName) throws Exception
    {
        if (weaponName.equalsIgnoreCase("spear")){
            return new Spear();
        } else if (weaponName.equalsIgnoreCase("sword")){
            return new Sword();
        } else {
            throw new Exception("对不起,本店暂无此业务,但是你可以先给钱。");
        }
    }
}
// 儿女情长什么的,影响我行走江湖了
public class Person {
    private String name;

    public Person(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}
// 武林大赛擂台
class ChallengeArena{
    public static void main(String[] args) throws Exception {
    
        // 中文当变量名,没想到吧!!!
        Person 陈平安 = new Person("陈平安");
        Weapon sword = Smithy.getWeapon("sword");
        System.out.println(陈平安.getName()+":"+sword.fight());

        Person 赵子龙 = new Person("赵子龙");
        Weapon spear = Smithy.getWeapon("spear");
        System.out.println(赵子龙.getName()+":"+spear.fight());
    }
}

image.png

那么现在问题来了,关二爷也想参赛怎么办,得有青龙偃月啊,步骤如下:

(1)添加具体产品类(刀--falchion)并实现抽象产品类(武器--Weapon)

(2)铁匠铺(工厂角色--Smithy)需要增加锻刀的业务(增加代码--所谓违背了开闭原则就在这里)

(3)关二爷(Person)报名参赛(ChallengeArena)

缺点:很明显了,违背了开闭原则,增加业务类型就需要增加代码,并修改原有代码; 优点:优点也是有的,编写代码比较简单,比较容易阅读--几个角色之间关系比较直观。

二、 工厂模式(Factory Pattern)

基于简单工厂模式的进步,修复了简单工厂模式违背开闭原则的缺陷,属于23种设计模式之一。

从设计的角度来看,工厂模式相对于简单工厂模式多了抽象工厂,原来简单模式是一个抽象产品对应多个具体产品,而现在工厂模式是增加了一个抽象工厂,一个抽象工厂对应多个具体工厂,每一种具体产品对应一个具体工厂。(文字还是太枯燥了)

直接上类图!

image.png

再来上代码!

// 超级铁匠铺--抽象工厂
public interface SuperSmithy {
     Weapon getWeapon();
}
// 枪(矛)铁匠铺--(具体工厂)
public class SpearSmithy implements SuperSmithy{
    @Override
    public Weapon getWeapon() {
        return new Spear();
    }
}
// 剑铁匠铺--(具体工厂角色)
public class SwordSmithy implements SuperSmithy{
    @Override
    public Weapon getWeapon() {
        return new Sword();
    }
}
// 兵器--Product(抽象产品角色)
public interface Weapon {
    // 战斗
    String fight();
}
// 剑--ConcreteProduct(具体产品类)
public class Sword implements Weapon{
    @Override
    public String fight() {
        return "我有一剑,可以搬山海!";
    }
}

// 枪(矛)--ConcreteProduct(具体产品类)
public class Spear implements Weapon{
    @Override
    public String fight() {
        return "我有一枪,可以破苍穹!";
    }
}
// 儿女情长什么的,影响我行走江湖了
public class Person {
    private String name;

    public Person(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}
// 武林大赛擂台
class ChallengeArena{
    public static void main(String[] args) throws Exception {

        Person 陈平安 = new Person("陈平安");
        Weapon sword = new SwordSmithy().getWeapon();
        System.out.println(陈平安.getName()+":"+sword.fight());

        Person 赵子龙 = new Person("赵子龙");
        Weapon spear = new SpearSmithy().getWeapon();
        System.out.println(赵子龙.getName()+":"+spear.fight());
    }
}

image.png

可以看出来,剑(具体产品)有对应的剑之铁匠铺(具体工厂),枪(矛)(具体产品)也有对应的枪之铁匠铺(具体工厂),行走江湖的侠客们想要武器就去专门的铁匠铺就行。

那如果现在关二爷又想参赛怎么办,步骤如下:

(1)添加具体产品类(刀--falchion)并实现抽象产品类(武器--Weapon)

(2)直接添加锻刀的铁匠铺(具体工厂角色--FalchionSmithy)并实现抽象工厂(抽象工厂--SuperSmithy)

(3)关二爷(Person)报名参赛(ChallengeArena)

在第二步的时候不用修改原来的代码了,也就是不用违背开闭原则了

缺点:缺点就是每增加一项业务就增加一个具体产品+一个具体工厂,代码量upupup 优点:不用违背开闭原则了

三、 抽象工厂模式(Abstract Factory Pattern)

抽象工厂模式是这几个模式最复杂的,工厂模式是每一种产品都有专门的工厂,比如专门铸剑的铁匠铺、专门锻刀的铁匠铺,简单来说产品跟工厂是一对一的关系。属于23种设计模式之一。

而抽象工厂把这种一对一关系打破了,使得一个工厂可以生产多样产品,比如铁匠铺可以锻件,也可以锻造铠甲,这里引入了产品族与产品等级的概念,同样原材料的是一样的产品族(比如这里陨铁是一个族,金精有又是一个族),生产同样属性的是同样的产品等级(比如这里攻击属性--刀枪剑戟是同一个产品等级,防御属性又是一个等级)。

image.png

当然了,产品族跟产品等级就是自定义的,比如下面是电器工厂的产品族与产品等级

image.png

从这里可以看出,抽象工厂又是普通工厂的进阶版本,普通工厂只会生产同样产品等级的产品(比如只生产攻击属性的刀枪剑戟),当然抽象工厂也会比普通工厂复杂许多,不论是原理还是代码方面。所以下面代码类的命名我直接用拼音了……不然我会乱

简要说明一下: (1)抽象工厂接口--超级铁匠铺,可以锻造武器、铠甲。

(2)具体工厂类--陨铁铁匠铺,可以锻造陨铁武器、铠甲;和金精铁匠铺,可以锻造金精武器、铠甲。

(3)抽象产品接口--武器,可以攻击;铠甲,可以防御。

(4)具体产品类--陨铁剑--攻击;陨铁铠甲--防御;金精剑--攻击;金精铠甲--防御;

image.png

代码如下:

// 超级铁匠铺--抽象工厂
public interface SuperSmithy {
     // 锻造兵器
     Weapon getWeapon();
     // 锻造铠甲
     Armor getArmor();
}
// 使用金精的铁匠铺--具体工厂
public class JinJingSmithy implements SuperSmithy{
    @Override
    public Weapon getWeapon() {
        return new JinJingWeapon();
    }

    @Override
    public Armor getArmor() {
        return new JinJingArmor();
    }
}

// 使用陨铁的铁匠铺--具体工厂
public class YunTieSmithy implements SuperSmithy{
    @Override
    public Weapon getWeapon() {
        return new YunTieWeapon();
    }

    @Override
    public Armor getArmor() {
        return new YunTieArmor();
    }
}

// 兵器--Product(抽象产品角色)
public interface Weapon {
    // 战斗
    String fight();
}
// 金精剑 -- 具体产品
public class JinJingWeapon implements Weapon{
    @Override
    public String fight() {
        return "金精剑攻击";
    }
}

// 陨铁剑 -- 具体产品
public class YunTieWeapon implements Weapon{
    @Override
    public String fight() {
        return "陨铁剑攻击";
    }
}
// 铠甲--Product(抽象产品角色)
public interface Armor {
    // 防御
    String defense();
}
// 金精铠甲 -- 具体产品
public class JinJingArmor implements Armor{
    @Override
    public String defense() {
        return "金精铠甲防御";
    }
}

// 陨铁铠甲 -- 具体产品
public class YunTieArmor implements Armor{
    @Override
    public String defense() {
        return "陨铁铠甲防御";
    }
}
package creativePattern.abstractFactoryPattern;



/**
 * @author AKE
 * @create 2022-05-31-15:56
 */
// 儿女情长什么的,影响我行走江湖了
public class Person {
    private String name;

    public Person(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

// 武林大赛擂台
class ChallengeArena{
    public static void main(String[] args) throws Exception {

        Person 陈平安 = new Person("陈平安");
        // 金精剑
        Weapon 陈平安weapon = new JinJingSmithy().getWeapon();
        // 陨铁铠甲
        Armor 陈平安armor = new YunTieSmithy().getArmor();
        System.out.println(陈平安.getName()+":"+陈平安weapon.fight()+":"+陈平安armor.defense());

        Person 李太白 = new Person("李太白");
        // 陨铁剑
        Weapon 李太白weapon = new YunTieSmithy().getWeapon();
        // 金精铠甲
        Armor 李太白armor = new JinJingSmithy().getArmor();
        System.out.println(李太白.getName()+":"+李太白weapon.fight()+":"+李太白armor.defense());
    }
}

image.png

其实工厂模式跟抽象工厂模式已经很明显了,把抽象工厂模式理解为坐标轴的话,工厂模式只是其中一部分(产品等级,参照上面给出的图);

抽象工厂模式虽然功能最强大,但也是最难理解的;同时抽象工厂也会违背开闭原则的,如果新增加一个具体产品类,那么相对应的工厂类代码也需要扩展。