设计模式:创建型模式
文章目录
1.简单工厂模式(Simple Factory)
1.1.定义
简单工厂模式(Simple Factory Pattern):又称为静态工厂方法(Static Factory Method)模式,它属于类创建型模式。在简单工厂模式中,可以根据参数的不同返回不同类的实例。简单工厂模式专门定义一个类来负责创建其他类的实例,被创建的实例通常都具有共同的父类。
理解:简单工厂模式它属于工厂模式的一种,用于创建对象而无需指定具体的类。在简单工厂模式中,一个工厂类负责创建多个不同类型的对象,而客户端只需要知道所需对象的名称或标识即可。
关键角色和概念:
1. 工厂类(Creator):负责创建对象的类,它包含了实际创建对象的逻辑,客户端通过调用工厂类的静态方法或实例化工厂对象来获取所需的产品。
2. 产品类(Product):被创建的对象类,具体产品由工厂类根据客户端的请求创建。
3. 客户端(Client):调用工厂类创建对象的类,通过工厂类获取所需的产品对象。
简单工厂模式的主要优点是客户端不需要知道具体产品的类名,只需知道产品的名称或标识即可获取所需对象,降低了客户端与具体产品类的耦合性。但同时,如果需要添加新的产品类型,仍然需要修改工厂类的代码,可能违反了开放-封闭原则。
这种模式通常适用于工厂类负责创建的对象较少,不会频繁变动的场景。
1.2.结构
简单工厂模式包含如下角色:
- Factory:工厂角色:负责实现创建所有实例的内部逻辑
- Product:抽象产品角色:是所创建的所有对象的父类,负责描述所有实例所共有的公共接口
- ConcreteProduct:具体产品角色:是创建目标,所有创建的对象都充当这个角色的某个具体类的实例。
1.3.时序图
1.4.代码实现
场景1:假设有一个简单的图形绘制程序,支持绘制圆形和矩形。
using System; // 产品接口 interface IShape { void Draw(); } // 具体产品:圆形 class Circle : IShape { public void Draw() { Console.WriteLine("绘制圆形"); } } // 具体产品:矩形 class Rectangle : IShape { public void Draw() { Console.WriteLine("绘制矩形"); } } // 简单工厂类 class ShapeFactory { // 工厂方法,根据传入的类型创建相应的产品对象 public static IShape CreateShape(string shapeType) { switch (shapeType.ToLower()) { case "circle": return new Circle(); case "rectangle": return new Rectangle(); default: throw new ArgumentException("不支持的图形类型"); } } } // 客户端 class Client { static void Main() { // 客户端通过工厂类获取产品对象 IShape circle = ShapeFactory.CreateShape("circle"); IShape rectangle = ShapeFactory.CreateShape("rectangle"); // 客户端使用产品对象 circle.Draw(); rectangle.Draw(); } }
例子中,我们定义了一个IShape
接口作为产品的抽象,然后有两个具体产品类Circle
和Rectangle
实现了这个接口。ShapeFactory
作为简单工厂类,包含了一个静态方法CreateShape
,根据传入的类型字符串创建相应的产品对象。
客户端通过调用工厂方法获取产品对象,并使用这些对象进行相应的操作。这样,客户端不需要直接知道具体产品的类名,只需通过工厂类获取所需对象即可。
场景2:假设有一个简单的餐厅点餐系统,支持点餐并根据菜品种类生成不同的菜单。
using System; using System.Collections.Generic; // 产品接口 interface IFood { void Cook(); } // 具体产品:中式菜 class ChineseFood : IFood { public void Cook() { Console.WriteLine("中式菜已做好"); } } // 具体产品:西式菜 class WesternFood : IFood { public void Cook() { Console.WriteLine("西式菜已做好"); } } // 简单工厂类 class FoodFactory { // 工厂方法,根据传入的类型创建相应的产品对象 public IFood CreateFood(string foodType) { switch (foodType.ToLower()) { case "chinese": return new ChineseFood(); case "western": return new WesternFood(); default: throw new ArgumentException("不支持的菜系类型"); } } } // 客户端 class Client { static void Main() { Console.WriteLine("请输入菜系类型(chinese/western):"); string foodType = Console.ReadLine(); try { // 创建简单工厂对象 FoodFactory factory = new FoodFactory(); // 客户端通过工厂类获取产品对象 IFood food = factory.CreateFood(foodType); // 客户端使用产品对象 food.Cook(); } catch (ArgumentException ex) { Console.WriteLine(ex.Message); } } }
例子中,我们定义了一个IFood
接口作为产品的抽象,然后有两个具体产品类ChineseFood
和WesternFood
实现了这个接口。FoodFactory
作为简单工厂类,包含了一个实例方法CreateFood
,根据传入的菜系类型字符串创建相应的产品对象。
客户端通过输入菜系类型,创建简单工厂对象,然后调用工厂方法获取相应的产品对象,并使用这个对象进行烹饪操作。
1.5.优缺点
优点:
1. 封装对象的创建过程:简单工厂模式将对象的创建过程封装在一个工厂类中,客户端只需关心产品接口和工厂类,而无需关心具体产品的实现。这提供了一种简单的方法来创建对象,隐藏了对象的实例化细节。
2. 解耦客户端和具体产品类:客户端通过工厂类获取产品对象,而不直接依赖具体产品类。这使得客户端代码与具体产品的类名解耦,提高了系统的灵活性和可维护性。
3. 单一责任原则:工厂类负责创建对象,遵循了单一责任原则,使得系统中的各个类更加清晰、易于理解和修改。
缺点:
1. 扩展性受限:新增产品时,需要修改工厂类的创建方法,违反了开闭原则。如果系统中的产品类不断增加,可能导致工厂类变得庞大,维护困难。
2. 不符合开闭原则:当需要新增产品时,需要修改工厂类的代码,这违背了开闭原则,即对扩展开放,对修改关闭。
3. 违反了依赖倒置原则:客户端依赖具体的工厂类,而不是依赖抽象。如果需要切换工厂类,客户端代码也需要修改。
简单工厂模式适用于产品种类相对稳定且变化不频繁的情况,如果系统中的产品类经常变化,可能需要考虑使用其他创建型模式,如工厂方法模式或抽象工厂模式,以更好地符合开闭原则。
1.6.使用场景
- 工厂类负责创建的对象比较少:由于创建的对象较少,不会造成工厂方法中的业务逻辑太过复杂。
- 客户端只知道传入工厂类的参数,对于如何创建对象不关心:客户端既不需要关心创建细节,甚至连类名都不需要记住,只需要知道类型所对应的参数。
1.7.总结
- 创建型模式对类的实例化过程进行了抽象,能够将对象的创建与对象的使用过程分离。
- 简单工厂模式又称为静态工厂方法模式,它属于类创建型模式。在简单工厂模式中,可以根据参数的不同返回不同类的实例。简单工厂模式专门定义一个类来负责创建其他类的实例,被创建的实例通常都具有共同的父类。
- 简单工厂模式包含三个角色:工厂角色负责实现创建所有实例的内部逻辑;抽象产品角色是所创建的所有对象的父类,负责描述所有实例所共有的公共接口;具体产品角色是创建目标,所有创建的对象都充当这个角色的某个具体类的实例。
- 简单工厂模式的要点在于:当你需要什么,只需要传入一个正确的参数,就可以获取你所需要的对象,而无须知道其创建细节。
- 简单工厂模式最大的优点在于实现对象的创建和对象的使用分离,将对象的创建交给专门的工厂类负责,但是其最大的缺点在于工厂类不够灵活,增加新的具体产品需要修改工厂类的判断逻辑代码,而且产品较多时,工厂方法代码将会非常复杂。
- 简单工厂模式适用情况包括:工厂类负责创建的对象比较少;客户端只知道传入工厂类的参数,对于如何创建对象不关心。
2.工厂方法模式(Factory Method)
2.1.定义
工厂方法模式(Factory Method Pattern)又称为工厂模式,也叫虚拟构造器(Virtual Constructor)模式或者多态工厂(Polymorphic Factory)模式,它属于类创建型模式。在工厂方法模式中,工厂父类负责定义创建产品对象的公共接口,而工厂子类则负责生成具体的产品对象,这样做的目的是将产品类的实例化操作延迟到工厂子类中完成,即通过工厂子类来确定究竟应该实例化哪一个具体产品类。
理解:工厂方法模式它定义了一个用于创建对象的接口,但是将实际的实例化过程延迟到子类中。这样,客户端代码在创建对象时不直接指定它们的类,而是通过调用一个工厂方法来创建对象。这种方式允许一个类的实例化延迟到其子类,从而在运行时选择具体实现类,而不是在编译时进行固定选择。
关键角色包括:
1. 抽象产品(Abstract Product):定义了产品的接口,具体产品类必须实现这个接口。
2. 具体产品(Concrete Product):是抽象产品的具体实现,是最终要创建的对象。
3. 抽象工厂(Abstract Factory):声明了创建产品的抽象方法,是一个接口或者抽象类。
4. 具体工厂(Concrete Factory):实现了抽象工厂接口,负责创建具体产品的实例。
在工厂方法模式中,客户端通过调用工厂方法来创建产品,而具体工厂类决定了到底实例化哪个具体产品类。这样的设计使得系统更容易扩展,因为可以新增具体产品类和对应的工厂类,而不需要修改已有的代码。
2.2.结构
工厂方法模式包含如下角色:
- Product:抽象产品
- ConcreteProduct:具体产品
- Factory:抽象工厂
- ConcreteFactory:具体工厂
2.3.时序图
2.4.代码实现
场景1:假设有一个电脑制造公司,他们生产不同类型的电脑,包括台式电脑(Desktop)和笔记本电脑(Laptop)。为了满足不同客户的需求,该公司使用工厂方法模式来创建这些电脑。
在这个场景中:
Computer
是抽象产品,定义了电脑的基本接口。Desktop
和Laptop
是具体产品,分别表示台式电脑和笔记本电脑。ComputerFactory
是抽象工厂,声明了创建电脑的抽象方法。DesktopFactory
和LaptopFactory
是具体工厂,分别负责创建台式电脑和笔记本电脑。
using System; // 抽象产品 interface Computer { void DisplayInfo(); } // 具体产品:台式电脑 class Desktop : Computer { public void DisplayInfo() { Console.WriteLine("这是一台台式电脑。"); } } // 具体产品:笔记本电脑 class Laptop : Computer { public void DisplayInfo() { Console.WriteLine("这是一台笔记本电脑。"); } } // 抽象工厂 interface ComputerFactory { Computer CreateComputer(); } // 具体工厂:台式电脑工厂 class DesktopFactory : ComputerFactory { public Computer CreateComputer() { return new Desktop(); } } // 具体工厂:笔记本电脑工厂 class LaptopFactory : ComputerFactory { public Computer CreateComputer() { return new Laptop(); } } // 客户端代码 class Client { static void Main() { // 使用台式电脑工厂创建台式电脑 ComputerFactory desktopFactory = new DesktopFactory(); Computer desktopComputer = desktopFactory.CreateComputer(); desktopComputer.DisplayInfo(); // 使用笔记本电脑工厂创建笔记本电脑 ComputerFactory laptopFactory = new LaptopFactory(); Computer laptopComputer = laptopFactory.CreateComputer(); laptopComputer.DisplayInfo(); } }
示例中,客户端通过选择不同的具体工厂(DesktopFactory
或 LaptopFactory
),可以创建不同类型的电脑(Desktop
或 Laptop
)。通过工厂方法模式,可以方便地扩展产品和工厂,而不需要修改客户端代码。
场景2:场景描述: 假设有一个在线购物平台,该平台销售不同种类的商品,包括电子产品(Electronic)和服装(Clothing)。为了处理不同种类商品的订单,该平台使用工厂方法模式来创建订单。
在这个场景中:
Product
是抽象产品,定义了商品的基本接口。Electronic
和Clothing
是具体产品,分别表示电子产品和服装。OrderFactory
是抽象工厂,声明了创建订单的抽象方法。ElectronicOrderFactory
和ClothingOrderFactory
是具体工厂,分别负责创建电子产品订单和服装订单。
using System; // 抽象产品 interface Product { void DisplayInfo(); } // 具体产品:电子产品 class Electronic : Product { public void DisplayInfo() { Console.WriteLine("这是一件电子产品。"); } } // 具体产品:服装 class Clothing : Product { public void DisplayInfo() { Console.WriteLine("这是一件服装。"); } } // 抽象工厂 interface OrderFactory { Product CreateProduct(); } // 具体工厂:电子产品订单工厂 class ElectronicOrderFactory : OrderFactory { public Product CreateProduct() { return new Electronic(); } } // 具体工厂:服装订单工厂 class ClothingOrderFactory : OrderFactory { public Product CreateProduct() { return new Clothing(); } } // 客户端代码 class Client { static void Main() { // 使用电子产品订单工厂创建电子产品订单 OrderFactory electronicFactory = new ElectronicOrderFactory(); Product electronicProduct = electronicFactory.CreateProduct(); electronicProduct.DisplayInfo(); // 使用服装订单工厂创建服装订单 OrderFactory clothingFactory = new ClothingOrderFactory(); Product clothingProduct = clothingFactory.CreateProduct(); clothingProduct.DisplayInfo(); } }
示例中,客户端通过选择不同的具体工厂(ElectronicOrderFactory
或 ClothingOrderFactory
),可以创建不同类型的商品订单(Electronic
或 Clothing
)。通过工厂方法模式,可以方便地扩展商品和订单,而不需要修改客户端代码。
2.5.优缺点
优点:
1. 封装对象创建过程:工厂方法模式将对象的创建过程封装在工厂类中,使得客户端无需关心具体产品的实例化过程。这降低了系统的耦合性。
2. 符合开闭原则:工厂方法模式支持新产品的添加,无需修改已有的代码。通过添加新的具体产品类和相应的工厂类,系统可以轻松扩展。
3. 更好的代码组织结构:工厂方法模式提供了一种良好的代码组织结构,各个具体工厂类负责创建对应的具体产品,使得代码结构清晰,易于理解和维护。
4. 符合单一职责原则:每个具体工厂类负责创建一个具体产品,符合单一职责原则,使得每个类的职责更加清晰明确。
缺点:
1. 类的数量增加:随着具体产品类和工厂类的增加,系统中的类的数量会迅速增加。这可能会使得类的管理和维护变得复杂,增加系统的复杂性。
2. 不适用于复杂对象的创建:如果创建的对象比较复杂,包含多个部分或需要特定的配置参数,工厂方法模式可能无法提供足够的灵活性,而需要使用其他创建型模式,如抽象工厂模式。
3. 客户端需要知道具体产品类:客户端在使用工厂方法创建对象时,通常需要知道具体产品的类名,这与封装对象创建过程的初衷略有违背。客户端与具体产品之间仍然存在一定的耦合。
工厂方法模式在某些场景下是非常有用的,特别是在需要实现产品簇的情况下,它提供了一种良好的扩展机制。然而,在选择设计模式时,需根据具体的需求和系统特点综合考虑各种因素。
2.6.使用场景
- 一个类不知道它所需要的对象的类:在工厂方法模式中,客户端不需要知道具体产品类的类名,只需要知道所对应的工厂即可,具体的产品对象由具体工厂类创建;客户端需要知道创建具体产品的工厂类。
- 一个类通过其子类来指定创建哪个对象:在工厂方法模式中,对于抽象工厂类只需要提供一个创建产品的接口,而由其子类来确定具体要创建的对象,利用面向对象的多态性和里氏代换原则,在程序运行时,子类对象将覆盖父类对象,从而使得系统更容易扩展。
- 将创建对象的任务委托给多个工厂子类中的某一个,客户端在使用时可以无须关心是哪一个工厂子类创建产品子类,需要时再动态指定,可将具体工厂类的类名存储在配置文件或数据库中。
2.7.总结
- 工厂方法模式又称为工厂模式,它属于类创建型模式。在工厂方法模式中,工厂父类负责定义创建产品对象的公共接口,而工厂子类则负责生成具体的产品对象,这样做的目的是将产品类的实例化操作延迟到工厂子类中完成,即通过工厂子类来确定究竟应该实例化哪一个具体产品类。
- 工厂方法模式包含四个角色:抽象产品是定义产品的接口,是工厂方法模式所创建对象的超类型,即产品对象的共同父类或接口;具体产品实现了抽象产品接口,某种类型的具体产品由专门的具体工厂创建,它们之间往往一一对应;抽象工厂中声明了工厂方法,用于返回一个产品,它是工厂方法模式的核心,任何在模式中创建对象的工厂类都必须实现该接口;具体工厂是抽象工厂类的子类,实现了抽象工厂中定义的工厂方法,并可由客户调用,返回一个具体产品类的实例。
- 工厂方法模式是简单工厂模式的进一步抽象和推广。由于使用了面向对象的多态性,工厂方法模式保持了简单工厂模式的优点,而且克服了它的缺点。在工厂方法模式中,核心的工厂类不再负责所有产品的创建,而是将具体创建工作交给子类去做。这个核心类仅仅负责给出具体工厂必须实现的接口,而不负责产品类被实例化这种细节,这使得工厂方法模式可以允许系统在不修改工厂角色的情况下引进新产品。
- 工厂方法模式的主要优点是增加新的产品类时无须修改现有系统,并封装了产品对象的创建细节,系统具有良好的灵活性和可扩展性;其缺点在于增加新产品的同时需要增加新的工厂,导致系统类的个数成对增加,在一定程度上增加了系统的复杂性。
- 工厂方法模式适用情况包括:一个类不知道它所需要的对象的类;一个类通过其子类来指定创建哪个对象;将创建对象的任务委托给多个工厂子类中的某一个,客户端在使用时可以无须关心是哪一个工厂子类创建产品子类,需要时再动态指定。