• 设计模式——2. 工厂模式


    1. 说明

    工厂模式(Factory Pattern)是一种创建型设计模式,它提供了一种创建对象的方式,而无需直接暴露对象的创建逻辑。工厂模式将对象的实例化过程封装在一个工厂类中,使客户端代码与具体对象的创建解耦,从而提高了代码的可维护性和灵活性。

    工厂模式通常有以下几种变体:

    1. 简单工厂模式(Simple Factory Pattern):在简单工厂模式中,只有一个工厂类负责创建多个不同类型的产品。客户端通过向工厂传递不同的参数或信息来请求特定类型的产品。这种方式封装了对象的创建,但工厂类可能会变得庞大。
    2. 工厂方法模式(Factory Method Pattern):工厂方法模式中,每个具体产品都有一个对应的工厂类,客户端通过与特定的工厂类交互来获取产品。每个具体工厂类负责创建一种产品,从而使代码更加可扩展和灵活。
    3. 抽象工厂模式(Abstract Factory Pattern):抽象工厂模式引入了一个抽象工厂类,该工厂类有多个工厂方法,每个工厂方法负责创建一组相关的产品。客户端通过选择特定的抽象工厂来获取一组相关产品。这种模式适用于创建多个相关的对象家族。

    工厂模式的主要优点包括:

    • 将对象的创建和使用分离,降低了代码的耦合性。
    • 通过多态性支持不同类型的产品。
    • 可以轻松添加新的产品类型,而不需要修改客户端代码。

    工厂模式在实际应用中经常被使用,特别是在需要根据配置、条件或用户输入来动态创建对象时,它提供了一种清晰的解决方案。

    2. 使用的场景

    以下是一些工厂模式常见的使用场景:

    1. 对象创建复杂度高:当对象的创建过程涉及复杂的逻辑、依赖关系或条件判断时,使用工厂模式可以将这些复杂性封装在工厂类中,使客户端代码更简洁。
    2. 多种产品类型:当系统需要创建多种不同类型的产品,但客户端不需要关心具体产品的创建细节时,工厂模式非常有用。每个具体产品由对应的工厂类负责创建。
    3. 动态切换产品族:抽象工厂模式适用于需要动态切换产品族(相关产品的集合)的情况。例如,切换不同操作系统的界面元素。
    4. 单一职责原则:工厂模式有助于遵循单一职责原则,即每个类应该只有一个引起变化的原因。通过将对象创建逻辑放入工厂类,可以将对象创建的职责与其他职责分离。
    5. 依赖注入:工厂模式可以与依赖注入(DI)一起使用,用于将依赖的对象注入到其他类中。DI 容器通常使用工厂模式来创建和管理依赖对象。
    6. 模块化和可扩展性:工厂模式有助于模块化设计,使系统更容易扩展和维护。通过添加新的工厂类和产品类,可以轻松地引入新的功能和特性。
    7. 延迟初始化:懒加载(延迟初始化)是工厂模式的一种应用场景,它允许在需要时才创建对象,从而节省资源。
    8. 测试:使用工厂模式可以更容易地进行单元测试。你可以使用工厂方法来创建模拟对象,以便测试其他类的行为。

    总的来说,工厂模式适用于任何需要对象创建和使用分离的情况,以及需要更好的代码组织、可扩展性和可维护性的情况。根据具体的需求,可以选择简单工厂、工厂方法或抽象工厂等变体。

    3. 应用例子

    例如要创建一个汽车制造的示例。不同类型的汽车(例如轿车、卡车和 SUV)都具有相同的属性和方法,但它们的创建可能需要不同的逻辑。以下是一个简单的工厂模式示例:

    # 定义汽车类(产品)
    class Car:
        def __init__(self, make, model):
            self.make = make
            self.model = model
        
        def start_engine(self):
            print(f"{self.make} {self.model}引擎启动")
    
    # 定义不同类型汽车的具体子类
    class Sedan(Car):
        pass
    
    class Truck(Car):
        pass
    
    class SUV(Car):
        pass
    
    # 定义汽车工厂
    class CarFactory:
        def create_car(self, car_type, make, model):
            if car_type == "sedan":
                return Sedan(make, model)
            elif car_type == "truck":
                return Truck(make, model)
            elif car_type == "suv":
                return SUV(make, model)
            else:
                raise ValueError("无效的汽车类型")
    
    # 客户端代码
    car_factory = CarFactory()
    
    sedan = car_factory.create_car("sedan", "Toyota", "Camry")
    truck = car_factory.create_car("truck", "Ford", "F-150")
    suv = car_factory.create_car("suv", "Honda", "CR-V")
    
    sedan.start_engine()
    truck.start_engine()
    suv.start_engine()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41

    在这个示例中:

    • Car 类是汽车的基类,具有共享属性和方法,如 make、model 和 start_engine。
    • Sedan、Truck 和 SUV 类分别表示不同类型的汽车,它们继承了 Car 类。
    • CarFactory 类是汽车工厂,负责根据汽车类型创建不同类型的汽车对象。

    客户端代码通过工厂创建不同类型的汽车对象,并调用它们的方法。这种方式使得客户端代码与具体汽车类型的创建逻辑解耦,同时也有助于实现更多的扩展性和可维护性。你可以根据需要扩展工厂和汽车类型,而无需修改客户端代码。

    4. 实现要素

    工厂模式的实现要素包括以下几个关键部分:

    1. 产品接口或基类(Product):
      这是所有具体产品类必须实现的接口或基类。它定义了产品的公共接口,包括属性和方法。客户端代码将与产品接口或基类进行交互,而不直接与具体产品类交互。
    2. 具体产品类(Concrete Product):
      这些类是产品接口或基类的具体实现。每个具体产品类都提供了产品的具体实现细节。它们通常包含不同类型产品的特有属性和行为。
    3. 工厂接口或基类(Factory):
      工厂接口或基类定义了工厂类必须实现的方法。这些方法通常是工厂方法,用于创建产品对象。工厂接口或基类可以是抽象类或接口。
    4. 具体工厂类(Concrete Factory):
      每个具体工厂类实现了工厂接口或基类中的工厂方法,负责创建具体产品对象。不同的具体工厂类可以创建不同类型的产品。
    5. 客户端(Client):
      客户端是使用工厂模式的代码。它通过与工厂接口或基类交互来获取产品对象,而不需要了解产品对象的具体创建细节。

    实现工厂模式时,重要的是要保持产品接口或基类和工厂接口或基类的抽象性。这样可以确保客户端代码与具体产品和具体工厂解耦,从而使系统更具灵活性和可维护性。客户端只需知道如何使用产品接口或基类和工厂接口或基类,而不需要关心它们的具体实现。

    工厂模式的主要思想是将对象的创建和使用分离,使得系统更容易扩展和维护。不同的变体,如简单工厂、工厂方法和抽象工厂,在实现细节上有所不同,但都遵循上述要素。选择哪种变体取决于具体需求和设计目标。

    5. Java/golang/javascrip/C++ 语言实现方式

    5.1 Java实现工厂模式

    // 1. 定义动物接口(产品)
    interface Animal {
        void speak();
    }
    
    // 2. 创建具体的动物类(具体产品)
    class Dog implements Animal {
        @Override
        public void speak() {
            System.out.println("汪汪汪!");
        }
    }
    
    class Cat implements Animal {
        @Override
        public void speak() {
            System.out.println("喵喵喵!");
        }
    }
    
    // 3. 定义动物工厂接口(工厂)
    interface AnimalFactory {
        Animal createAnimal();
    }
    
    // 4. 创建具体的动物工厂类(具体工厂)
    class DogFactory implements AnimalFactory {
        @Override
        public Animal createAnimal() {
            return new Dog();
        }
    }
    
    class CatFactory implements AnimalFactory {
        @Override
        public Animal createAnimal() {
            return new Cat();
        }
    }
    
    // 5. 客户端代码
    public class FactoryPatternExample {
        public static void main(String[] args) {
            // 使用狗工厂创建狗对象
            AnimalFactory dogFactory = new DogFactory();
            Animal dog = dogFactory.createAnimal();
            dog.speak(); // 输出:汪汪汪!
    
            // 使用猫工厂创建猫对象
            AnimalFactory catFactory = new CatFactory();
            Animal cat = catFactory.createAnimal();
            cat.speak(); // 输出:喵喵喵!
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54

    在这个示例中:

    • Animal 接口定义了动物的公共方法 speak()。
    • Dog 和 Cat 类是具体的动物类,它们实现了 Animal 接口,并提供了不同的 speak() 方法实现。
    • AnimalFactory 接口定义了动物工厂的方法 createAnimal()。
    • DogFactory 和 CatFactory 类是具体的动物工厂类,它们实现了 AnimalFactory 接口,并分别创建狗和猫对象。

    5.2 Golang实现工厂模式

    package main
    
    import "fmt"
    
    // 1. 定义食物接口(产品)
    type Food interface {
        Eat()
    }
    
    // 2. 创建具体的食物类型(具体产品)
    type Pizza struct{}
    
    func (p *Pizza) Eat() {
        fmt.Println("吃比萨!")
    }
    
    type Burger struct{}
    
    func (b *Burger) Eat() {
        fmt.Println("吃汉堡!")
    }
    
    // 3. 定义食物工厂接口(工厂)
    type FoodFactory interface {
        CreateFood() Food
    }
    
    // 4. 创建具体的食物工厂类型(具体工厂)
    type PizzaFactory struct{}
    
    func (pf *PizzaFactory) CreateFood() Food {
        return &Pizza{}
    }
    
    type BurgerFactory struct{}
    
    func (bf *BurgerFactory) CreateFood() Food {
        return &Burger{}
    }
    
    // 5. 客户端代码
    func main() {
        // 使用比萨工厂创建比萨对象
        pizzaFactory := &PizzaFactory{}
        pizza := pizzaFactory.CreateFood()
        pizza.Eat() // 输出:吃比萨!
    
        // 使用汉堡工厂创建汉堡对象
        burgerFactory := &BurgerFactory{}
        burger := burgerFactory.CreateFood()
        burger.Eat() // 输出:吃汉堡!
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53

    在这个示例中:

    • Food 接口定义了食物的公共方法 Eat()。
    • Pizza 和 Burger 结构体是具体的食物类型,它们实现了 Food 接口,并提供了不同的 Eat() 方法实现。
    • FoodFactory 接口定义了食物工厂的方法 CreateFood()。
    • PizzaFactory 和 BurgerFactory 结构体是具体的食物工厂类型,它们实现了 FoodFactory 接口,并分别创建比萨和汉堡对象。

    5.3 Javascript实现工厂模式

    // 1. 定义电视接口(产品)
    class TV {
      play() {
        console.log("正在播放电视节目");
      }
    }
    
    // 2. 创建具体的电视类(具体产品)
    class LEDTV extends TV {
      play() {
        console.log("正在播放LED电视节目");
      }
    }
    
    class LCDTV extends TV {
      play() {
        console.log("正在播放液晶电视节目");
      }
    }
    
    // 3. 定义电视工厂接口(工厂)
    class TVFactory {
      createTV() {
        throw new Error("子类必须实现createTV方法");
      }
    }
    
    // 4. 创建具体的电视工厂类(具体工厂)
    class LEDTVFactory extends TVFactory {
      createTV() {
        return new LEDTV();
      }
    }
    
    class LCDTVFactory extends TVFactory {
      createTV() {
        return new LCDTV();
      }
    }
    
    // 5. 客户端代码
    const ledTVFactory = new LEDTVFactory();
    const lcdTVFactory = new LCDTVFactory();
    
    const ledTV = ledTVFactory.createTV();
    const lcdTV = lcdTVFactory.createTV();
    
    ledTV.play(); // 输出:正在播放LED电视节目
    lcdTV.play(); // 输出:正在播放液晶电视节目
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49

    在这个示例中:

    • TV 类是电视的基类,具有公共方法 play()。
    • LEDTV 和 LCDTV 类是具体的电视类型,它们继承了 TV 类,并提供了不同的 play() 方法实现。
    • TVFactory 类是电视工厂的接口,它定义了 createTV() 方法,子类必须实现该方法。
    • LEDTVFactory 和 LCDTVFactory 类是具体的电视工厂类,它们分别创建 LED 电视和液晶电视对象。

    5.4 C++实现工厂模式

    #include 
    
    // 1. 定义汽车接口(产品)
    class Car {
    public:
        virtual void drive() = 0;
    };
    
    // 2. 创建具体的汽车类(具体产品)
    class Sedan : public Car {
    public:
        void drive() override {
            std::cout << "驾驶轿车" << std::endl;
        }
    };
    
    class Truck : public Car {
    public:
        void drive() override {
            std::cout << "驾驶卡车" << std::endl;
        }
    };
    
    // 3. 定义汽车工厂接口(工厂)
    class CarFactory {
    public:
        virtual Car* createCar() = 0;
    };
    
    // 4. 创建具体的汽车工厂类(具体工厂)
    class SedanFactory : public CarFactory {
    public:
        Car* createCar() override {
            return new Sedan();
        }
    };
    
    class TruckFactory : public CarFactory {
    public:
        Car* createCar() override {
            return new Truck();
        }
    };
    
    // 5. 客户端代码
    int main() {
        CarFactory* sedanFactory = new SedanFactory();
        CarFactory* truckFactory = new TruckFactory();
    
        Car* sedan = sedanFactory->createCar();
        Car* truck = truckFactory->createCar();
    
        sedan->drive(); // 输出:驾驶轿车
        truck->drive(); // 输出:驾驶卡车
    
        delete sedan;
        delete truck;
        delete sedanFactory;
        delete truckFactory;
    
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62

    在这个示例中:

    • Car 类是汽车的基类,具有纯虚拟方法 drive()。
    • Sedan 和 Truck 类是具体的汽车类型,它们继承了 Car 类,并提供了不同的 drive() 方法实现。
    • CarFactory 类是汽车工厂的接口,它定义了纯虚拟方法 createCar(),子类必须实现该方法。
    • SedanFactory 和 TruckFactory 类是具体的汽车工厂类,它们分别创建轿车和卡车对象。

    6. 练习题

    假设你正在开发一个游戏,游戏中有不同类型的武器(如剑、弓、魔杖)。每种武器都有不同的属性和攻击方式。使用工厂模式来实现武器对象的创建,以便游戏中的角色可以获取和使用不同类型的武器。
    要求:

    1. 创建一个名为 Weapon 的基类或接口,用于表示武器。该基类或接口应包含一个 attack() 方法,用于描述武器的攻击方式。
    2. 创建具体的武器类,如 Sword、Bow 和 Wand,它们分别实现 Weapon 接口,并提供不同的 attack() 方法实现。
    3. 创建一个武器工厂类 WeaponFactory,它包含一个工厂方法 createWeapon(),根据传入的参数来创建不同类型的武器对象。
    4. 编写测试代码,创建不同类型的武器对象,并调用它们的 attack() 方法来展示不同武器的攻击方式。
      这个练习题旨在帮助你练习工厂模式的实现和应用。可以使用任何编程语言来完成这个练习,并尽量确保角色可以轻松获取和使用不同类型的武器。

    你可以在评论区里或者私信我回复您的答案,这样我或者大家都能帮你解答,期待着你的回复~

  • 相关阅读:
    数据解读!理想L9冲刺「月销过万」,从BBA手中抢份额
    代码随想录二刷第三天(Python)
    .NET中的Object类学习3_MemberwiseClone方法
    如何解决国标GB28181视频平台EasyGBS国标云服务平台设备在线,通道却显示离线的情况
    企业简化客户服务的5种方法
    Docker-完整项目的部署(保姆级教学)
    大麦网-演出赛事票务系统画uml图
    在分布式系统中,库存超卖怎么办?
    Windows应急响应排查
    【接口自动化测试】一步一步教你搭建接口环境
  • 原文地址:https://blog.csdn.net/guohuang/article/details/133215128