C++ 工厂模式与抽象工厂:让对象“流水线”更优雅
想象一下,你是一家玩具工厂的老板,主要生产两种玩具:小汽车和积木。最初,你的生产流程很简单,需要什么就直接用 new
创建什么:
// 生产小汽车
Car* myCar = new Car();
// 生产积木
Block* myBlock = new Block();
简单粗暴,效率很高,就像直接从仓库里抓取零件组装一样。但问题也随之而来:
- 耦合度高: 生产代码直接依赖于具体的
Car
和Block
类。如果有一天你要生产变形金刚,或者修改Car
的构造方式,所有用到new Car()
的地方都要改,简直噩梦! - 扩展性差: 每增加一种新玩具,你都要修改生产代码,这违背了面向对象设计中的“开闭原则”(对扩展开放,对修改关闭)。
- 维护困难: 代码散落在各个角落,难以追踪和维护。想象一下,你的代码库里到处都是
new Car()
,谁知道哪个是玩具汽车,哪个是真正的汽车啊!
这时候,你就需要一种更优雅的方式来创建对象,让你的玩具工厂更高效、更灵活,这就是 工厂模式 大显身手的时候了。
简单工厂:把“new”交给工厂
简单工厂模式就像你雇了一个专门负责生产玩具的工头,你告诉他要什么玩具,他负责帮你 new
出来。
class Toy {
public:
virtual void play() = 0;
virtual ~Toy() {}
};
class Car : public Toy {
public:
void play() override {
std::cout << "Vroom Vroom!" << std::endl;
}
};
class Block : public Toy {
public:
void play() override {
std::cout << "Stacking blocks..." << std::endl;
}
};
class ToyFactory {
public:
static Toy* createToy(std::string type) {
if (type == "Car") {
return new Car();
} else if (type == "Block") {
return new Block();
} else {
return nullptr; // 或者抛出一个异常
}
}
};
// 使用工厂创建玩具
Toy* myCar = ToyFactory::createToy("Car");
myCar->play(); // 输出: Vroom Vroom!
delete myCar;
现在,你只需要告诉 ToyFactory
你想要什么类型的玩具,它就会帮你创建出来。这样,你就把 new
的操作集中到了 ToyFactory
中,降低了耦合度。
优点:
- 集中创建对象: 所有对象的创建逻辑都集中在一个地方,方便维护和管理。
- 解耦: 客户端代码不再直接依赖于具体的类,只需要知道工厂即可。
缺点:
- 违反开闭原则: 每增加一种新的玩具,都需要修改
ToyFactory
的createToy()
方法。这会导致工厂类变得越来越庞大,维护起来也越来越困难。 - 工厂职责过重: 工厂既要负责创建对象,又要负责判断对象的类型,职责过于集中。
简单工厂虽然解决了直接 new
对象的一些问题,但它仍然不够灵活,无法应对更复杂的需求。想象一下,你的玩具工厂开始生产不同风格的玩具,比如卡通汽车、赛车、工程积木、益智积木等等,简单工厂就显得力不从心了。
工厂方法:让每个玩具都有自己的工厂
为了解决简单工厂的缺点,我们引入 工厂方法模式。就像你为每种类型的玩具都设立一个专门的生产车间,每个车间只负责生产一种类型的玩具。
class ToyFactory {
public:
virtual Toy* createToy() = 0;
virtual ~ToyFactory() {}
};
class CarFactory : public ToyFactory {
public:
Toy* createToy() override {
return new Car();
}
};
class BlockFactory : public ToyFactory {
public:
Toy* createToy() override {
return new Block();
}
};
// 使用工厂方法创建玩具
ToyFactory* carFactory = new CarFactory();
Toy* myCar = carFactory->createToy();
myCar->play(); // 输出: Vroom Vroom!
delete myCar;
delete carFactory;
现在,每种玩具都有自己的工厂,当需要增加一种新的玩具时,只需要创建一个新的工厂类即可,无需修改现有的代码,符合开闭原则。
优点:
- 符合开闭原则: 增加新的产品时,只需要增加新的工厂类,无需修改现有的代码。
- 职责明确: 每个工厂只负责创建一种产品,职责更加明确。
- 灵活性高: 可以方便地扩展新的产品类型。
缺点:
- 类数量增多: 每增加一种产品,都需要增加一个工厂类,类的数量会比较多。
- 客户端需要选择工厂: 客户端需要知道应该使用哪个工厂来创建对象。
工厂方法模式解决了简单工厂的缺点,提高了代码的灵活性和可扩展性。但如果你的玩具工厂需要生产一系列相关的玩具,比如汽车系列和积木系列,每个系列又包含不同的款式,工厂方法模式就显得有些力不从心了。这时候,你需要更强大的 抽象工厂模式。
抽象工厂:打造玩具系列的流水线
抽象工厂模式就像你把玩具工厂分成不同的生产线,每条生产线负责生产一个系列的玩具。每个系列都包含一系列相关的产品,比如汽车系列包含卡通汽车、赛车、工程车等等。
// 抽象产品:汽车和积木
class Car {
public:
virtual void drive() = 0;
virtual ~Car() {}
};
class Block {
public:
virtual void stack() = 0;
virtual ~Block() {}
};
// 具体产品:卡通汽车、赛车、工程积木、益智积木
class CartoonCar : public Car {
public:
void drive() override {
std::cout << "Cartoon Car driving..." << std::endl;
}
};
class RaceCar : public Car {
public:
void drive() override {
std::cout << "Race Car speeding..." << std::endl;
}
};
class EngineeringBlock : public Block {
public:
void stack() override {
std::cout << "Stacking engineering blocks..." << std::endl;
}
};
class PuzzleBlock : public Block {
public:
void stack() override {
std::cout << "Stacking puzzle blocks..." << std::endl;
}
};
// 抽象工厂:玩具工厂
class ToyFactory {
public:
virtual Car* createCar() = 0;
virtual Block* createBlock() = 0;
virtual ~ToyFactory() {}
};
// 具体工厂:卡通玩具工厂和真实玩具工厂
class CartoonToyFactory : public ToyFactory {
public:
Car* createCar() override {
return new CartoonCar();
}
Block* createBlock() override {
// 这里可以根据需要返回不同的卡通积木
return new PuzzleBlock();
}
};
class RealToyFactory : public ToyFactory {
public:
Car* createCar() override {
return new RaceCar();
}
Block* createBlock() override {
return new EngineeringBlock();
}
};
// 使用抽象工厂创建玩具系列
ToyFactory* cartoonFactory = new CartoonToyFactory();
Car* cartoonCar = cartoonFactory->createCar();
Block* puzzleBlock = cartoonFactory->createBlock();
cartoonCar->drive(); // 输出: Cartoon Car driving...
puzzleBlock->stack(); // 输出: Stacking puzzle blocks...
delete cartoonCar;
delete puzzleBlock;
delete cartoonFactory;
抽象工厂模式定义了一个接口,用于创建一系列相关或相互依赖的对象,而无需指定它们具体的类。这样,你就可以方便地创建不同系列的玩具,而无需修改客户端代码。
优点:
- 创建相关产品: 可以保证创建的产品属于同一个系列,符合一致性原则。
- 易于切换产品系列: 只需要更换工厂,就可以切换整个产品系列。
- 隐藏具体实现: 客户端代码不需要知道具体产品的实现细节。
缺点:
- 扩展困难: 如果需要增加新的产品系列,需要修改所有的工厂类,违反开闭原则。
- 结构复杂: 代码结构比较复杂,需要维护多个抽象类和具体类。
工厂模式的选择:没有最好的,只有最合适的
选择哪种工厂模式取决于你的具体需求。
- 简单工厂: 适用于创建对象比较简单,且对象类型变化不频繁的情况。
- 工厂方法: 适用于需要频繁增加新的对象类型,且对象类型比较复杂的情况。
- 抽象工厂: 适用于需要创建一系列相关或相互依赖的对象,且需要易于切换产品系列的情况。
就像选择玩具材料一样,没有最好的材料,只有最适合的材料。选择合适的工厂模式,可以让你更好地管理对象的创建过程,提高代码的灵活性、可扩展性和可维护性。
总结:让你的代码更像玩具
工厂模式和抽象工厂模式都是创建对象的常用设计模式,它们可以帮助你更好地组织和管理代码,提高代码的质量。
- 工厂模式 将对象的创建过程封装起来,降低了代码的耦合度,提高了代码的灵活性。
- 抽象工厂模式 用于创建一系列相关或相互依赖的对象,可以保证产品的一致性,方便切换产品系列。
记住,设计模式不是万能的,它们只是解决特定问题的工具。在使用设计模式时,要根据实际情况进行选择,不要为了使用而使用。就像玩积木一样,要有创意,才能搭建出你想要的城堡!
希望这篇文章能让你对 C++ 工厂模式和抽象工厂模式有更深入的了解。下次在创建对象时,不妨考虑一下使用这些模式,让你的代码更像玩具,有趣且易于维护!