C++设计模式创建型之工厂模式整理

一、工厂模式分类

        工厂模式属于创建型模式,一般可以细分为简单工厂模式、工厂模式和抽象工厂模式。每种都有不同的特色和应用场景。

二、工厂模式详情

1、简单工厂模式

1)概述

        简单工厂模式相对来说,在四人组写的《设计模式------可复用面向对象软件的基础》中并没有提及,所以可以认为这并不算是一个标准的设计模式,但因为其场景较多,所以这里简单介绍下。有的人认为可以看做一种编程收发或者编程技巧。

2)简单工厂模式代码

class Monster
{
public:
    Monster(int life, int magic, int attack):m_life(life),m_magic(magic),m_attack(attack){}
    virtual ~Monster(){}
protected:
    int m_life;
    int m_magic;
    int m_attack;
};

class M_Undead: public Monster
{
public:
    M_Undead(int life, int magic, int attack):Monster(life, magic, attack)
    {
        cout << "一只亡灵类怪物诞生在这个世界上了" << endl;
    };
};

class M_Element: public Monster
{
public:
    M_Element(int life, int magic, int attack):Monster(life, magic, attack)
    {
        cout << "一只元素类怪物诞生在这个世界上了" << endl;
    };
};

class M_Mechanic: public Monster
{
public:
    M_Mechanic(int life, int magic, int attack):Monster(life, magic, attack)
    {
        cout << "一只机械类怪物诞生在这个世界上了" << endl;
    };
};
class CSimpleFactory
{
public:
    Monster* createMonster(eMonsterType eType)
    {
        Monster* pCreateObj = nullptr;
        if (eType == Undead_Type)
        {
            pCreateObj = new M_Undead(300, 50, 80);
        } 
        else if (eType == Element_Type)
        {
            pCreateObj = new M_Element(200, 80, 100);
        }
        else if (eType == Mechanic_Type)
        {
            pCreateObj = new M_Mechanic(400, 0, 110);
        }
        return pCreateObj;
    }
};

int main()
{

    CSimpleFactory simobj;
    Monster* pM1 = simobj.createMonster(Undead_Type);

    Monster* pM2 = simobj.createMonster(Element_Type);

    Monster* pM3 = simobj.createMonster(Mechanic_Type);

    delete pM1;
    delete pM2;
    delete pM3;
    
    return 0;
}

总结:通过工厂类来创建怪物,则意味着创建怪物时不再使用new关键字,而是通过该工厂类来进行,这样的话,即便将来拐歪种类增加,main主函数中创建怪物的代码也可以尽量保持稳定。同时也避免了在main函数中直接使用new创建对象时必须知道具体类名的情形发生,实现了创建怪物的代码与各个具体怪物类对象要实现的业务逻辑的代码隔离。当然此模式也具有明显的缺点,当引入新的怪物类型时,需要修改createMonster成员函数的源码来增加if判断分支,从而支持对新类型怪物的创建工作,这就违反了开放封闭原则。如果if分支不是很多,只有数个而并不是数十上百个,那适当违反开闭原则也可以接收的。

2、工厂模式

1)概述

        工厂模式通常也是指得是工厂方法模式,换句话说,工厂方法模式可以简称工厂模式或多态工厂模式,此种模式实现难度比简单工厂模式略高一些。需要针对每种类型的怪物都需要创建一个对应的工厂类,它通过增加新的工厂类来符合面向对象程序设计的开闭原则,但付出的代价是需要增加多个新的工厂类。

2)代码示例

class M_ParFactory
{
public:
    virtual Monster* createMonster() = 0;
    virtual ~M_ParFactory(){}
};

class M_UndeadFactory : public M_ParFactory
{
public:
    virtual Monster* createMonster()
    {
        return new M_Undead(300, 50, 80);
    }
};

class M_ElementFactory : public M_ParFactory
{
public:
    virtual Monster* createMonster()
    {
        return new M_Element(200, 80, 100);
    }
};

class M_MechanicFactory : public M_ParFactory
{
public:
    virtual Monster* createMonster()
    {
        return new M_Mechanic(400, 0, 110);
    }
};

Monster* Gbl_CreateMonster(M_ParFactory *factoryObj)
{
    return factoryObj->createMonster();
}

int main()
{
    M_ParFactory* p_ud_fy = new M_UndeadFactory();
    Monster* pM1 = Gbl_CreateMonster(p_ud_fy); //构造一只亡灵类怪物

    M_ParFactory* p_ele_fy = new M_ElementFactory();
    Monster* pM2 = Gbl_CreateMonster(p_ele_fy); //构造一只元素类怪物

    M_ParFactory* p_mec_fy = new M_MechanicFactory();
    Monster* pM3 = Gbl_CreateMonster(p_mec_fy); //构造一只机械类怪物

    //释放资源
    delete p_ud_fy;
    delete p_ele_fy;
    delete p_mec_fy;

    delete pM1;
    delete pM2;
    delete pM3;
}

总结:从上述代码中可以看到,创建怪物对象时,不需要记住具体怪物类的名称,但需要知道创建该类怪物的工厂名称。工厂模式的实现意图是定义一个用于创建对象的接口,但由子类决定要实例化的类是哪一个,该模式是的某个类的实例化延迟到子类。它当出现一个新怪物类型时,既不需要更改Gbl_CreateMonster函数,也不需要像简单工厂模式那样修改MonsterFactory类中的createMonster成员函数来增加新的if分支。

3、抽象工厂模式

1)概述

         随着业务的变化,游戏中的战斗场景数量和类型不断增加,从原来的在城镇中战斗逐步进入在沼泽战斗、在山脉地区战斗等,于是场景将重新分类,怪物分为3类,战斗场景3类,这样会产生9类怪物。如果一个工厂子类能够生产不止一种具有相同规则的怪物对象,那么就可以有效地减少所创建的工厂子类数量,这就是抽象工厂模式的核心思想。抽象工厂模式是按照产品族来生产产品,一个地点有一个工厂,该工厂负责生产本地的所有产品。

2)代码示例

//沼泽类怪物
class M_Undead_Swamp : public Monster
{
public:
    M_Undead_Swamp(int life, int magic, int attack):Monster(life, magic, attack)
    {
        cout << "一只沼泽的亡灵类怪物诞生在这个世界上了" << endl;
    };
};

class M_Element_Swamp : public Monster
{
public:
    M_Element_Swamp(int life, int magic, int attack):Monster(life, magic, attack)
    {
        cout << "一只沼泽的元素类怪物诞生在这个世界上了" << endl;
    };
};

class M_Mechanic_Swamp : public Monster
{
public:
    M_Mechanic_Swamp(int life, int magic, int attack):Monster(life, magic, attack)
    {
        cout << "一只沼泽的机械类怪物诞生在这个世界上了" << endl;
    };
};

//------------------------------------------------------------------------------
//山脉类类怪物
class M_Undead_Mountain : public Monster
{
public:
    M_Undead_Mountain(int life, int magic, int attack):Monster(life, magic, attack)
    {
        cout << "一只山脉的亡灵类怪物诞生在这个世界上了" << endl;
    };
};

class M_Element_Mountain : public Monster
{
public:
    M_Element_Mountain(int life, int magic, int attack):Monster(life, magic, attack)
    {
        cout << "一只山脉的元素类怪物诞生在这个世界上了" << endl;
    };
};

class M_Mechanic_Mountain : public Monster
{
public:
    M_Mechanic_Mountain(int life, int magic, int attack):Monster(life, magic, attack)
    {
        cout << "一只山脉的机械类怪物诞生在这个世界上了" << endl;
    };
};

//------------------------------------------------------------------------------
//城镇类怪物
class M_Undead_Town : public Monster
{
public:
    M_Undead_Town(int life, int magic, int attack):Monster(life, magic, attack)
    {
        cout << "一只城镇的亡灵类怪物诞生在这个世界上了" << endl;
    };
};

class M_Element_Town : public Monster
{
public:
    M_Element_Town(int life, int magic, int attack):Monster(life, magic, attack)
    {
        cout << "一只城镇的元素类怪物诞生在这个世界上了" << endl;
    };
};

class M_Mechanic_Town : public Monster
{
public:
    M_Mechanic_Town(int life, int magic, int attack):Monster(life, magic, attack)
    {
        cout << "一只城镇的机械类怪物诞生在这个世界上了" << endl;
    };
};

class M_ParFactory
{
public:
    virtual Monster* createMonster_Undead() = 0;
    virtual Monster* createMonster_Element() = 0;
    virtual Monster* createMonster_Mechanic() = 0;
    virtual ~M_ParFactory(){}
};

//工厂子类:
//-------------------------------------------------------------------------------------
//沼泽地区工厂
class M_Factory_Swamp : public M_ParFactory
{
public:
    virtual Monster* createMonster_Undead()
    {
        return new M_Undead_Swamp(300, 50, 120);
    }

    virtual Monster* createMonster_Element()
    {
        return new M_Element_Swamp(200, 80, 110);
    }

    virtual Monster* createMonster_Mechanic()
    {
        return new M_Mechanic_Swamp(400, 0, 90);
    }
};

//-------------------------------------------------------------------------------------
//山脉地区工厂
class M_Factory_Mountain : public M_ParFactory
{
public:
    virtual Monster* createMonster_Undead()
    {
        return new M_Undead_Mountain(300, 50, 80);
    }

    virtual Monster* createMonster_Element()
    {
        return new M_Element_Mountain(200, 80, 100);
    }

    virtual Monster* createMonster_Mechanic()
    {
        return new M_Mechanic_Mountain(600, 0, 110);
    }
};

//-------------------------------------------------------------------------------------
//城镇地区工厂
class M_Factory_Town : public M_ParFactory
{
public:
    virtual Monster* createMonster_Undead()
    {
        return new M_Undead_Town(300, 50, 80);
    }

    virtual Monster* createMonster_Element()
    {
        return new M_Element_Town(200, 80, 100);
    }

    virtual Monster* createMonster_Mechanic()
    {
        return new M_Mechanic_Town(400, 0, 110);
    }
};

总结:抽象工厂模式通过增加新代码不是修改原有代码来为游戏增加新功能。如果需要需要产品族,还需要修改工厂父类来增加新的虚函数以支持新类型,各个工厂有需要增加对新类型的支持,那么这种情况下破坏了开闭原则,同时也不适用抽象工厂模式。抽象工厂模式具备工厂模式的优点,如果只是增加新的产品族,则只需要增加新的子工厂类,符合开闭原则,这是抽象工厂模式优点,但是如果增加新的产品等级结构,那么就需要修改抽象层的代码,这是抽象工厂模式的缺点,因此应该避免在产品等级结构不稳定的情况下使用该模式。引入抽象工厂模式实现意图是:提供一个接口让该接口负责创建一系列相关或者相互依赖的对象,而无需制定它们的具体类。

4、三个模式间对比

1)从代码实现复杂度上,简单工厂模式最简单,工厂方法模式次之,抽象工厂模式最复杂。把简单工厂模式中的代码修改得符合开闭原则,就变成了工厂方法模式,修改工厂方法模式的代码是一个工厂支持对多个具体产品的生产,就变成了抽象工厂模式。

2)从需要的工厂数量上,简单工厂模式需要的工厂数量最少,工厂方法模式需要的工厂数量最多,抽象工厂模式能够有效地减少工厂方法模式所需要的工厂数量。

3)从实际应用上,当项目中的产品数量较少时考虑使用简单工厂模式,如果项目稍大一点或者为了满足开闭原则,则可以使用工厂方法模式,而对于大型项目中有众多厂商并且每个厂商都生产一系列产品时应考虑使用抽象工厂模式。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值