C++ 工厂模式与抽象工厂:创建对象的灵活设计

C++ 工厂模式与抽象工厂:让对象“流水线”更优雅

想象一下,你是一家玩具工厂的老板,主要生产两种玩具:小汽车和积木。最初,你的生产流程很简单,需要什么就直接用 new 创建什么:

// 生产小汽车
Car* myCar = new Car();
// 生产积木
Block* myBlock = new Block();

简单粗暴,效率很高,就像直接从仓库里抓取零件组装一样。但问题也随之而来:

  • 耦合度高: 生产代码直接依赖于具体的 CarBlock 类。如果有一天你要生产变形金刚,或者修改 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 中,降低了耦合度。

优点:

  • 集中创建对象: 所有对象的创建逻辑都集中在一个地方,方便维护和管理。
  • 解耦: 客户端代码不再直接依赖于具体的类,只需要知道工厂即可。

缺点:

  • 违反开闭原则: 每增加一种新的玩具,都需要修改 ToyFactorycreateToy() 方法。这会导致工厂类变得越来越庞大,维护起来也越来越困难。
  • 工厂职责过重: 工厂既要负责创建对象,又要负责判断对象的类型,职责过于集中。

简单工厂虽然解决了直接 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++ 工厂模式和抽象工厂模式有更深入的了解。下次在创建对象时,不妨考虑一下使用这些模式,让你的代码更像玩具,有趣且易于维护!

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注