本文是该系列的第二篇,采用问答式的方式展开,问题由我提出,答案由 Chat AI 作出,灰色背景的文字则主要是我的旁白。
工厂设计模式是一种创建型设计模式,旨在解决对象的创建和实例化过程中的一些问题。通常情况下,我们可以通过直接在代码中使用 new 关键字创建对象实例,但是在某些场景下,我们可能需要更加灵活、可扩展的创建方式,以简化代码并提高可维护性。
工厂模式通过引入一个专门的“工厂”对象来创建和生成对象实例,从而将对象的创建和实例化过程完全隔离开来。这个“工厂”对象承担了所有的创建和实例化逻辑,客户端代码只需要向它提供所需的参数以及需要的对象类型,就能够得到一个完整的可用对象实例。
工厂模式通常会包括一个工厂接口和多个具体的工厂实现,每个工厂实现专门负责某种类型的对象的创建和实例化。客户端代码只需使用工厂接口,并通过依赖注入等方式获取具体的工厂实现对象,即可创建和获取所需的对象实例。
使用工厂模式能够使代码更加灵活、可扩展,并使客户端代码更加简洁易懂,因此在很多框架和开发场景中都广泛应用。
有点啰嗦,简单来说:工厂负责对象的创建,客户端不再关心对象是怎么创建的了,将变化控制在工厂端。
工厂模式一般可以分为三种形式:简单工厂模式、工厂方法模式和抽象工厂模式。
简单工厂模式:也叫静态工厂模式,由一个工厂类根据传入的参数决定创建哪一种产品类的实例。它由一个具体工厂类来创建产品,而这些产品往往都具有共同的父类。简单工厂模式一般只有一个工厂类,且该工厂类的方法一般是静态的。相对于其他工厂模式,简单工厂模式的实现比较简单,并且能够实现简单的对象创建和管理。
工厂方法模式:也叫多态工厂模式,定义一个用于创建对象的接口,让子类决定实例化哪一个类。工厂方法模式能够更加灵活地管理对象的创建,而且能够支持多种对象的创建方式。它将创建过程交给子类去实现,使得工厂方法和产品类之间的耦合度更加低,同时也更容易扩展和维护。
抽象工厂模式:也叫工具箱,提供了一种将多个工厂方法组合在一起使用的方式,它提供了一种抽象工厂来创建一系列相关或相互依赖的对象。抽象工厂模式可以帮助我们实现产品族的概念,即一组相关或相互依赖的产品组成的一个家族。它将同族产品的创建交给一组相关或相互依赖的工厂方法来实现,从而实现同族产品的风格统一和工艺相近。
产品族:我理解像小米手机、音箱、耳机就属于同一个产品族。它们相关在哪里呢?应该是都属于小米这个牌子。
下面是一个使用 C++ 语言实现简单工厂模式的示例:
#include
using namespace std;
// 抽象产品类
class Product {
public:
virtual void Show() = 0;
};
// 具体产品类A
class ProductA : public Product {
public:
void Show() {
cout << "Product A" << endl;
}
};
// 具体产品类B
class ProductB : public Product {
public:
void Show() {
cout << "Product B" << endl;
}
};
// 简单工厂类
class SimpleFactory {
public:
static Product* CreateProduct(char type) {
switch (type) {
case 'A':
return new ProductA();
case 'B':
return new ProductB();
default:
return nullptr;
}
}
};
int main() {
// 通过简单工厂类获取产品实例
Product* productA = SimpleFactory::CreateProduct('A');
productA->Show();
Product* productB = SimpleFactory::CreateProduct('B');
productB->Show();
return 0;
}
在上面的示例中,我们定义了一个抽象产品类 Product
和两个具体产品类 ProductA
和 ProductB
,以及一个简单工厂类 SimpleFactory
。在简单工厂类中,我们定义了一个静态方法 CreateProduct
,根据传入的参数类型返回相应的产品实例。
在主函数中,我们通过调用简单工厂类的静态方法来获取指定类型的产品实例,并调用其 Show()
方法展示产品信息。
静态工厂根据传入的参数,来判断需要创建的产品实例。有一个明显的缺点,当新增产品时,要新增 if 分支,违反了开闭原则。
另外,这个例子是挺好理解,但是不够具体,所以我希望换个具体的例子。
好的,让我们假设有一个宠物店,我们需要根据客户的需求提供不同种类的宠物,例如猫、狗等。我们可以使用简单工厂模式来实现宠物的创建。
以下是使用 C++ 语言实现简单工厂模式的宠物店示例:
#include
using namespace std;
// 宠物类抽象基类
class Pet {
public:
virtual void Show() = 0;
};
// 猫类
class Cat : public Pet {
public:
void Show() {
cout << "我是一只猫" << endl;
}
};
// 狗类
class Dog : public Pet {
public:
void Show() {
cout << "我是一只狗" << endl;
}
};
// 简单工厂类
class PetFactory {
public:
static Pet* CreatePet(char type) {
switch (type) {
case 'C':
return new Cat();
case 'D':
return new Dog();
default:
return nullptr;
}
}
};
int main() {
// 通过简单工厂类获取猫和狗的实例
Pet* cat = PetFactory::CreatePet('C');
cat->Show();
Pet* dog = PetFactory::CreatePet('D');
dog->Show();
return 0;
}
在上面的示例中,我们定义了一个抽象基类 Pet
,以及两个具体子类 Cat
和 Dog
。我们还定义了一个简单工厂类 PetFactory
,其中 CreatePet()
方法根据传入的参数类型返回相应的实例。在主函数中,我们通过调用简单工厂类的静态方法来获取指定类型的宠物实例,并调用其 Show()
方法展示宠物信息。
以上代码演示了利用简单工厂模式根据客户的需求来创建不同种类的宠物,客户只需要告诉我们希望获取哪种类型的宠物,我们就可以提供相应的实例。
好的,下面是使用工厂方法模式实现宠物店的示例代码:
#include
using namespace std;
// 抽象基类
class Pet {
public:
virtual void Show() = 0;
};
// 猫类
class Cat : public Pet {
public:
void Show() {
cout << "我是一只猫" << endl;
}
};
// 狗类
class Dog : public Pet {
public:
void Show() {
cout << "我是一只狗" << endl;
}
};
// 宠物工厂抽象基类
class PetFactory {
public:
virtual Pet* CreatePet() = 0;
};
// 猫工厂类
class CatFactory : public PetFactory {
public:
Pet* CreatePet() {
return new Cat();
}
};
// 狗工厂类
class DogFactory : public PetFactory {
public:
Pet* CreatePet() {
return new Dog();
}
};
int main() {
// 通过具体工厂类获取猫和狗的实例
PetFactory* factory1 = new CatFactory();
Pet* cat = factory1->CreatePet();
cat->Show();
PetFactory* factory2 = new DogFactory();
Pet* dog = factory2->CreatePet();
dog->Show();
return 0;
}
在上面的代码中,我们定义了一个 Pet
抽象基类、两个具体子类 Cat
和 Dog
,然后我们将宠物工厂抽象成了一个 PetFactory
基类,并将猫工厂类和狗工厂类继承自 PetFactory
,每个工厂类都实现了 CreatePet()
方法来生产具体的宠物。
在主函数中,我们使用 PetFactory
的具体实现类(即 CatFactory
和 DogFactory
)来创建对应的宠物实例。由于我们使用了工厂方法模式,所以客户端代码只需要知道具体的工厂类,而不需要直接调用单独的 CreatePet()
方法或者宠物类的构造函数,从而实现了更好的解耦,也方便了代码的维护和扩展。
简单工厂是将所有宠物的生产放到一个工厂中,而工厂方法为每一种宠物单独创建了一个工厂类(狗工厂和猫工厂)。现在客户端只需要在创建工厂之后,直接调用 CreatePet() 方法,就能生产出工厂对应的宠物了,而不再像之前那样需要传入宠物类型参数。
但是缺点也很明显,每增加一种宠物,就要增加一个工厂类。当宠物类过多时,该缺点更明显,这怎么解决呢,我们接着看一下抽象工厂方法,或许能找到答案。
好的,下面是使用抽象工厂模式实现宠物店的示例代码:
#include
using namespace std;
// 抽象基类
class Pet {
public:
virtual void Show() = 0;
};
// 猫类
class Cat : public Pet {
public:
void Show() {
cout << "我是一只猫" << endl;
}
};
// 狗类
class Dog : public Pet {
public:
void Show() {
cout << "我是一只狗" << endl;
}
};
// 猫食品抽象基类
class CatFood {
public:
virtual void Show() = 0;
};
// 狗食品抽象基类
class DogFood {
public:
virtual void Show() = 0;
};
// 宠物工厂方法抽象基类
class PetFactory {
public:
virtual Pet* CreatePet() = 0;
virtual CatFood* CreateCatFood() = 0;
virtual DogFood* CreateDogFood() = 0;
};
// 猫工厂方法类
class CatFactory : public PetFactory {
public:
Pet* CreatePet() {
return new Cat();
}
CatFood* CreateCatFood() {
return new CatFood();
}
DogFood* CreateDogFood() {
return nullptr;
}
};
// 狗工厂方法类
class DogFactory : public PetFactory {
public:
Pet* CreatePet() {
return new Dog();
}
CatFood* CreateCatFood() {
return nullptr;
}
DogFood* CreateDogFood() {
return new DogFood();
}
};
// 猫食品类
class CatFoodA : public CatFood {
public:
void Show() {
cout << "我是猫食品A" << endl;
}
};
// 狗食品类
class DogFoodB : public DogFood {
public:
void Show() {
cout << "我是狗食品B" << endl;
}
};
int main() {
// 通过具体工厂方法类获取对应的宠物和食品实例
PetFactory* factory1 = new CatFactory();
Pet* cat = factory1->CreatePet();
cat->Show();
CatFood* catFood = factory1->CreateCatFood();
catFood->Show();
PetFactory* factory2 = new DogFactory();
Pet* dog = factory2->CreatePet();
dog->Show();
DogFood* dogFood = factory2->CreateDogFood();
dogFood->Show();
return 0;
}
在上面的代码中,我们在工厂方法中添加了创建猫食品和狗食品的方法。我们还定义了一个 CatFood
抽象类和一个 DogFood
抽象类,以及它们各自的具体子类 CatFoodA
和 DogFoodB
。
通过这种方式,我们可以将同属于一个产品族的产品创建进行统一管理。在主函数中,我们使用 PetFactory
的具体实现类(即 CatFactory
和 DogFactory
)来创建对应的宠物和食品的实例,从而实现了同属于一个产品族的产品创建统一管理的目的。
猫食品和狗视频居然有各自的抽象基类,而且在 PetFactory 这个抽象基类中,同时存在着 CreateCatFood 和 CreateDogFood 这两个方法,我是很惊讶的。
当我看到它说“将同属于一个产品族的产品创建进行统一管理”时,我明白了过来。它应该是将狗食品、猫食品、宠物当做了一个级别的了,认为这三者属于一个产品族,但其实狗食品和猫食品都属于宠物食品,它们无法和宠物相提并论。
但这只是我的猜测,为了证实这一点,我向 AI 做出来试探。
确实,如果我们只需要生产一种食品,我们可以将 CreateCatFood()
和 CreateDogFood()
方法统一为 CreateFood()
方法,代码就可以变得更加简洁和易于维护。但是,抽象工厂模式的设计初衷是用于创建多个产品族,即多个相关联的产品群,而不仅仅是一个产品类,这样可以更好地实现产品族之间的解耦和扩展性。因此,当生产的产品数量增多时,抽象工厂模式通常会更加实用和合理。在这个例子中,我们只提供了两种不同的食品,可能不足以体现抽象工厂模式的优势,但是在实际的项目中,往往存在更多的产品族和相关联的产品群,这时抽象工厂模式的优势就会更加显著。
确实,它认为狗粮、猫粮、宠物是一个产品族的,看起来 AI 还是有点傻。我们可以说宠物、宠物食品是一个产品族,甚至可以把狗粮、猫粮、狗、猫一起看做一个产品族,但像它这样划分肯定是四不像的。
是的,你理解得很对。在抽象工厂模式中,一个工厂是负责生产一个产品族(产品群),并且每个产品族中的产品类是相互关联的,但不同产品族之间的产品类是没有关联的,因此我们需要为每个产品族分别创建一个工厂。在这个宠物店的例子中,狗工厂和猫工厂就是两个工厂,分别负责生产狗和猫两种宠物,而狗粮和猫粮并不是同一个产品族,因此它们并不在同一个工厂中,而是分别在狗产品族和猫产品族中,所以我们需要为它们各自创建一个工厂方法,由它们的具体实现类分别来产生狗粮和猫粮的实例。
我测试的这个 AI 就是这样,你说啥,它都说对对对、是是是,附和你,谄媚至极。
它在这里提到了一个产品族中的产品是相互关联的,狗和狗粮关联,猫和猫粮有关联。
你的理解是正确的,当不同产品族中的产品类之间没有关联时,我们是可以将工厂方法进行简化的,使得每个工厂方法只负责创建一个产品实例。因此,在这个例子中,如果猫粮和狗粮之间是没有任何关联的,我们可以将 PetFactory
中的 CreateCatFood()
和 CreateDogFood()
方法简化为一个 CreateFood()
方法。
示例代码如下:
#include
using namespace std;
// 抽象基类
class Pet {
public:
virtual void Show() = 0;
};
// 猫类
class Cat : public Pet {
public:
void Show() {
cout << "我是一只猫" << endl;
}
};
// 狗类
class Dog : public Pet {
public:
void Show() {
cout << "我是一只狗" << endl;
}
};
// 食品抽象基类
class Food {
public:
virtual void Show() = 0;
};
// 宠物工厂方法抽象基类
class PetFactory {
public:
virtual Pet* CreatePet() = 0;
virtual Food* CreateFood() = 0;
};
// 猫工厂方法类
class CatFactory : public PetFactory {
public:
Pet* CreatePet() {
return new Cat();
}
Food* CreateFood() {
return new CatFood();
}
};
// 狗工厂方法类
class DogFactory : public PetFactory {
public:
Pet* CreatePet() {
return new Dog();
}
Food* CreateFood() {
return new DogFood();
}
};
// 猫食品类
class CatFood : public Food {
public:
void Show() {
cout << "我是猫食品" << endl;
}
};
// 狗食品类
class DogFood : public Food {
public:
void Show() {
cout << "我是狗食品" << endl;
}
};
int main() {
// 通过具体工厂方法类获取对应的宠物和食品实例
PetFactory* factory1 = new CatFactory();
Pet* cat = factory1->CreatePet();
cat->Show();
Food* catFood = factory1->CreateFood();
catFood->Show();
PetFactory* factory2 = new DogFactory();
Pet* dog = factory2->CreatePet();
dog->Show();
Food* dogFood = factory2->CreateFood();
dogFood->Show();
return 0;
}
在上述代码中,我们将 CreateCatFood()
和 CreateDogFood()
方法简化为了一个 CreateFood()
方法。通过这种方式,当不同产品族中的产品类之间没有关联时,我们可以简化工厂方法,这样可以让代码更加简洁易懂。然而,当产品类之间有关联时,我们就必须保留原有的工厂方法,否则会破坏产品之间的完整性。
代码写的符合我要求,但是它为什么说我在做简化呢,我也没有简化啊,每个工厂都能生产一个产品族(宠物和宠物食品),好吧,我认为它说的有道理,只是不适用这个例子。狗粮和猫粮之间到底有没有关联呢?我认为是没有的,狗吃的和猫吃的能有什么联系呢,虽然它们都是宠物食品,但这个关系并不是关联。
我觉得它的抽象工厂例子不太好,想让它换个例子。
当创建的产品有多个组合方式,即每种产品都有不同的变种或者样式时,就需要用到抽象工厂模式来隔离不同的产品族。比如,假设我们在设计一个手机商城网站,需要选择手机品牌和手机配件。为了方便用户购买,我们需要将同一品牌的手机和配件分别显示在一起,保证用户可以轻松购买配件。但是,不同品牌的手机,配件组合方式不同,因此需要为每个品牌创建一个工厂来管理其手机和配件的生产。
在这个例子中,我们可以将不同品牌的手机称为“手机产品族”,每个品牌的手机和配件称为“产品群”。因此,我们需要创建一个手机厂商的工厂方法来生产不同品牌的手机和配件。
以下是这个例子的示例代码:
#include
using namespace std;
// 手机基类
class Phone {
public:
virtual void Show() = 0;
};
// Apple手机类
class ApplePhone : public Phone {
public:
void Show() {
cout << "我是苹果手机" << endl;
}
};
// 小米手机类
class XiaomiPhone : public Phone {
public:
void Show() {
cout << "我是小米手机" << endl;
}
};
// 手机配件基类
class PhoneAccessory {
public:
virtual void Show() = 0;
};
// Apple手机配件类
class AppleAccessory : public PhoneAccessory {
public:
void Show() {
cout << "我是苹果手机配件" << endl;
}
};
// 小米手机配件类
class XiaomiAccessory : public PhoneAccessory {
public:
void Show() {
cout << "我是小米手机配件" << endl;
}
};
// 手机工厂方法抽象基类
class PhoneFactory {
public:
virtual Phone* CreatePhone() = 0;
virtual PhoneAccessory* CreatePhoneAccessory() = 0;
};
// Apple手机工厂方法类
class ApplePhoneFactory : public PhoneFactory {
public:
Phone* CreatePhone() {
return new ApplePhone();
}
PhoneAccessory* CreatePhoneAccessory() {
return new AppleAccessory();
}
};
// 小米手机工厂方法类
class XiaomiPhoneFactory : public PhoneFactory {
public:
Phone* CreatePhone() {
return new XiaomiPhone();
}
PhoneAccessory* CreatePhoneAccessory() {
return new XiaomiAccessory();
}
};
int main() {
// 通过具体工厂方法类获取对应的手机和配件实例
PhoneFactory* appleFactory = new ApplePhoneFactory();
Phone *applePhone = appleFactory->CreatePhone();
applePhone->Show();
PhoneAccessory *appleAccessory = appleFactory->CreatePhoneAccessory();
appleAccessory->Show();
PhoneFactory* xiaomiFactory = new XiaomiPhoneFactory();
Phone *xiaomiPhone = xiaomiFactory->CreatePhone();
xiaomiPhone->Show();
PhoneAccessory *xiaomiAccessory = xiaomiFactory->CreatePhoneAccessory();
xiaomiAccessory->Show();
return 0;
}
在上述代码中,我们创建了两个不同品牌的手机,每个品牌都有自己的厂商工厂,分别负责生产不同品牌的手机和配件。在具体工厂方法中,我们分别使用 CreatePhone()
和 CreatePhoneAccessory()
方法来创建对应的手机和配件实例。由于不同手机品牌的配件组合方式不同,因此需要为每个品牌分别提供一个对应的工厂方法,而不是将它们合并在一个工厂方法中。
这个例子就跟我想的一样,一个品牌对应一个工厂,生产自家的一系列产品。那我什么宠物那个例子它就始终转不过弯呢?狗牌子和猫牌子按理说好理解的啊,我认为它刚刚改代码并不是真的认为应该那样,只是在迁就我,我再问问看。
你说得没错,我之前的例子可能不是很妥当,把狗和猫粮放在一个产品族中确实不太合理。在实际开发中,我们在定义不同的抽象工厂时,通常需要把不同的产品分类更加明确,防止不同产品之间的互相混淆。以下是一个更加恰当的例子:
假设我们正在开发一个电子产品商城,我们需要提供两种类型的电子产品:台式机和笔记本。我们还需要为每种电子产品提供两种不同的配件:键盘和鼠标。
在这个例子中,我们可以将不同类型的电子产品称为“电子产品族”,每种电子产品的配件称为“组件”。因此,我们需要创建一个电子产品的工厂方法来生产不同类型的电子产品,以及每种电子产品对应的组件。
下面是一个实现这个例子的抽象工厂模式代码:
#include
using namespace std;
// 电子产品基类
class ElectronicProduct {
public:
virtual void Show() = 0;
};
// 台式机类
class Desktop : public ElectronicProduct {
public:
void Show() {
cout << "我是台式机" << endl;
}
};
// 笔记本类
class Laptop : public ElectronicProduct {
public:
void Show() {
cout << "我是笔记本" << endl;
}
};
// 电子产品组件基类
class ElectronicComponent {
public:
virtual void Show() = 0;
};
// 键盘类
class Keyboard : public ElectronicComponent {
public:
void Show() {
cout << "我是键盘" << endl;
}
};
// 鼠标类
class Mouse : public ElectronicComponent {
public:
void Show() {
cout << "我是鼠标" << endl;
}
};
// 电子产品工厂方法抽象基类
class ElectronicFactory {
public:
virtual ElectronicProduct* CreateProduct() = 0;
virtual ElectronicComponent* CreateComponent() = 0;
};
// 台式机工厂方法类
class DesktopFactory : public ElectronicFactory {
public:
ElectronicProduct* CreateProduct() {
return new Desktop();
}
ElectronicComponent* CreateComponent() {
return new Keyboard();
}
};
// 笔记本工厂方法类
class LaptopFactory : public ElectronicFactory {
public:
ElectronicProduct* CreateProduct() {
return new Laptop();
}
ElectronicComponent* CreateComponent() {
return new Mouse();
}
};
int main() {
// 通过具体工厂方法类获取对应的电子产品和组件实例
ElectronicFactory* desktopFactory = new DesktopFactory();
ElectronicProduct *desktop = desktopFactory->CreateProduct();
desktop->Show();
ElectronicComponent *keyboard = desktopFactory->CreateComponent();
keyboard->Show();
ElectronicFactory* laptopFactory = new LaptopFactory();
ElectronicProduct *laptop = laptopFactory->CreateProduct();
laptop->Show();
ElectronicComponent *mouse = laptopFactory->CreateComponent();
mouse->Show();
return 0;
}
在上述代码中,我们创建了两种不同类型的电子产品,每种类型的电子产品需要提供自己的工厂方法,以及对应的组件。在具体工厂方法中,我们分别使用 CreateProduct()
和 CreateComponent()
方法来创建对应的产品和组件实例。由于不同类型的电子产品的配件组合方式不同,因此需要为每种类型的电子产品分别提供一个对应的工厂方法,而不是将它们合并在一个工厂方法中。
反手就又是一个例子,确实比我效率高,这认错态度也可以。那我就认为你真的知道错误了,哈哈。
好的,下面是三种不同的工厂模式的优缺点总结:
简单工厂模式的优点是实现简单易懂,适用于对实例化过程较为简单的类。例如,如果需要根据一些较为简单的条件来创建对象,则使用简单工厂模式可能是一个不错的选择。
缺点是工厂类集成难度较高,如果新添加了一个类,就需要修改工厂类的代码,这违背了开闭原则。因此在类的数量较多或者产品族的类别比较多时,简单工厂模式并不适用。
工厂方法模式的优点是遵循开闭原则,扩展性好,每个产品的具体实现都可以对应一个具体的工厂类进行生产,使系统具有良好的灵活性和可扩展性。
缺点是每个具体产品对应一个具体工厂,类的个数会增加,导致系统中类的个数增加,会增加系统的实现难度和复杂度。
抽象工厂模式的优点是提供一个接口,用于创建相关联的或者相互依赖的产品族,解决了工厂方法模式中同样的问题,克服了简单工厂模式违背开闭原则的缺点。
缺点是增加类的数量,扩展新的产品结构需要编写新的产品类和新的工厂,因为每个具体工厂只负责创建一种产品结构,难以扩展。同时,由于它的增加新的产品族比较困难,因此不太适用于大型产品族的开发。
它不行,显示不了图片,还得靠我自己来啊。