设计模式 ---- 工厂模式&抽象工厂模式&简单工厂

从“猫吃鱼”理解简单工厂、工厂方法与抽象工厂

这个例子只是同步哦猫吃鱼来详细理解设计模式,其他例子比如:商品打折,后需要增加春节折扣,中秋节折扣
植物大战僵尸:玩家点击卡片生成植物,生成向日葵,生成豌豆射手,后续要添加其他植物拓展

一、模式前置需求

假设现在有一个简单的需求:

有一只猫,猫可以吃鱼。

现有类:猫、鱼

存在方法:猫可以吃鱼、鱼可以被吃

class Cat
{
public:
    void EatFish(Fish* fish)
    {
        std::cout << "猫吃鱼" << std::endl;
		fish->HasBeenEat();
    }
};

class Fish
{
public:
    void HasBeenEat()
	{
	    std::cout << "我是鱼,我被吃了" << std::endl;
	}
}

在使用该类的时候

Cat* cat = new Cat();
Fish* fish = new Fish();
//执行猫吃鱼的动作
cat->EatFish(fish);

delete cat;
delete fish;

增加需求:猫不止要吃鱼

那么我要吃其他的食物呢,野猫会捕食野鼠
如果不使用设计模式,那么需要在原有的Cat类中修改
原类直接修改:

class Cat
{
public:
    void EatFish(Fish* fish)
    {
        std::cout << "猫吃鱼" << std::endl;
        fish->HasBeenEat();
    }

    void EatMouse(Mouse* mouse)
    {
        std::cout << "猫吃老鼠" << std::endl;
        mouse->HasBeenEat();
    }
};

还需新增类

class Mouse
{
public:
    void HasBeenEat()
	{
	    std::cout << "我是老鼠,我被吃了" << std::endl;
	}
}

后续如果,猫需要吃兔子

class Cat
{
public:
    void EatFish(Fish* fish)
    {
        // ...
    }

    void EatMouse(Mouse* mouse)
    {
        // ...
    }

    void EatRabbit(Rabbit* rabbit)
    {
        // ...
    }
};

还需要增加兔子类

class Rabbit
{
public:
    void HasBeenEat()
    {
        std::cout << "我是兔子,我被吃了" << std::endl;
    }
};

这样如果每增加一种食物就需要修改类Cat,并且新增一个对应的食物类
业务代码同时依赖具体的 Cat 和具体的 Fish 类型。
每增加一种新的食物类型,就需要修改 Cat 类,为其增加对应的处理方法。这使得 Cat 类与具体食物类型产生了较强的耦合,也违反了开闭原则。(即:软件实体应对拓展开放,对修改关闭)

如果后继续增加需求的话,Cat类会越来越臃肿

抽象

在理想的状态下,我们增加需求,应该是增加对应的类,而不是不断的修改Cat

Cat 应该聚焦与吃东西,而不是具体的以来某一种具体的食物

而所有的食物都有一个相同的属性:被吃掉

那么所有的食物都可以被抽象为 Food

class Food{
public:

    virtual ~Food() = default;
	
	virtual void HasBeenEat() = 0;
	
};

鱼继承 Food 类:

class Fish : public Food{
public:
    void HasBeenEat() override
    {
	    std::cout << "我是鱼,我被吃掉了" << endl;
	}
};

老鼠集成 Food 类:

class Mouse : public Food
{
public:
    void HasBeenEat() override
    {
        std::cout << "我是老鼠,我被吃了" << std::endl;
    }
};

Cat 类依赖的是 Food 而不是具体的食物

Cat 类:

class Cat()
{
public:
    void EatFood(Food* food){
	    std::cout << "猫吃食物" << endl;
	    food->HasBeenEat();
	}
};

那么在使用的时候就有

Cat* cat = new Cat();

Fish* fish = new Fish();
Mouse* mouse = new Mouse();

cat->Eat(fish);
cat->Eat(mouse);

delete cat;
delete fish;
delete mouse;

这样就是 cat 依赖于 Food

老鼠兔子 依赖于 Food

如果增加一个其他食物,只需要增加对应的一个类就行,不需要修改Cat

但是这样还是存在一个问题,主业务代码还是依赖具体的食物,Cat需要吃对应的食物之前,还是需要一个一个的new(对应的食物)

二、简单工厂

在现阶段主业务逻辑依赖于对应的食物

如果将所有的食物的创建交给一个专门的类


enum class FoodType {
    Fish,
    Mouse,
    Rabbit
};

class FoodFactory
{
public:
    static Food* CreateFood(FoodType type)
    {
        switch (type)
        {
        case FoodType::Fish:
            return new Fish();

        case FoodType::Mouse:
            return new Mouse();

        case FoodType::Rabbit:
            return new Rabbit();

        default:
            return nullptr;
        }
    }
};

这样主业务的逻辑就变成:

Cat* cat = new Cat();

Food* food = FoodFactory::CreateFood(fish);

cat->Eat(food);

delete cat;
delete food;

这样是主业务逻辑将创建对应的类的操作剥离出来,由一个专门负责对象创建的 Factory 统一封装对象的创建逻辑

三、工厂方法

简单工厂将创建逻辑剥离出来,但工厂类本身仍然依赖所有具体食物,每增加一种食物就要修改工厂类

工厂方法模式将工厂也抽象化,每个具体工厂只负责生产一种产品,增加新产品时只需新增对应的工厂子类,无需修改已有工厂。

但要注意:客户端代码中 new FishFactory() 依然依赖了具体工厂,这种依赖通常被上移到程序的最顶层(如 main 函数或配置模块),而业务逻辑层只依赖 FoodFactory 抽象基类

如果增加食物,Factory仍然会不停的修改,仍然会变得越来越臃肿

那么能不能将Factory抽象呢

就像增加食物一样,增加工厂,创建新的食物,直接增加对应的类

先提取所有共有的属性

这里是new对应的类,即:CreateFood创建食物(如果要应用到植物大战僵尸,这里也可以是,创建植物

class FoodFactory
{
public:
    virtual ~FoodFactory() = default;

    virtual Food* CreateFood() = 0;
};

创建一个只生成鱼的工厂

class FishFactory : public FoodFactory
{
public:
    Food* CreateFood() override
    {
        return new Fish();
    }
};

创建一个老鼠工厂

class MouseFactory : public FoodFactory
{
public:
    Food* CreateFood() override
    {
        return new Mouse();
    }
};

在使用的时候直接调用对用的工厂方法就可直接生成对应的食物

FoodFactory* factory = new FishFactory();

Food* food = factory->CreateFood();

Cat* cat = new Cat();

cat->EatFood(food);

delete cat;
delete food;
delete factory;

这样在出现需要开拓新业务(即创建新的食物、创建新的植物...)时,只需要添加新的工厂类,和新的操作实体类(food 类)

在主业务逻辑中直接调用该创建调用工厂方法即可

四、抽象工厂

增加需求

如果现在增加需求,食物获取。猫获得鱼需要垂钓,猫获取老鼠需要陷阱

class GetFood
{
    virtual ~GetFood() = default;

    virtual void GetFoodMethod() = 0;
}

垂钓

class Angling : public GetFood
{
    void GetFoodMethod() override
	{
	    std::cout << "进行了垂钓" << endl;
	}
}

捕鼠

class Trap : public GetFood 
{
public:
    void GetFoodMethod() override
	{
	    std::cout << "设置了捕鼠器" << std::endl; 
	}
};

现在对进行海洋食物族进行抽象

class FoodFactory
{
public:
	virtual ~FoodFactory() = default;
	// 创建食物
	virtual Food* CreateFood() = 0;
	// 创建获取食物的方法
	virtual GetFood* CreateGetFoodMethod() = 0;
};

鱼产品工厂

class FishFactory : public FoodFactory
{
public:
	Food* CreateFood() override
	{
		return new Fish();
	}
	GetFood* CreateGetFoodMethod() override
	{
		return new Angling();
	}
};

class LandFoodFactory : public FoodFactory
{
public:
    Food* CreateFood() override
    {
        return new Mouse();        // 生产老鼠
    }
    GetFood* CreateGetFoodMethod() override
    {
        return new Trap();         // 使用陷阱
    }
};

主业务逻辑

FoodFactory* factory = new FishFactory();

Food* food = factory->CreateFood();

GetFood* getMethod = factory->CreateGetFoodMethod();

getMethod->GetFoodMethod();

Cat* cat = new Cat();

cat->EatFood(food);

FoodFactory* factory2 = new LandFoodFactory();

Food* food2 = factory2->CreateFood();

GetFood* method2 = factory2->CreateGetFoodMethod();

delete cat;

delete getMethod;

delete food;

delete factory;

delete method2;

delete food2;

delete factory2;

本质上,抽象工厂是对工厂方法的优化,将与对应食物的相关类也进行一次抽象

posted on 2026-08-31 00:19  咸云闲鱼  阅读(8)  评论(0)    收藏  举报