创建者模式-工厂模式

220 阅读6分钟

工厂模式

通过一个案例来了解一下工厂模式

需求:设计一个咖啡店点餐系统。设计一个咖啡联(Coffee),并定义其两个子类(美式咖啡 【americanCoffee 】和拿铁咖啡【IatteCoffee】);再设计一个咖啡店类 (Softeestore),咖啡店具有点咖啡的功能。

//咖啡类
class Coffee{
    func getName()->String{
        fatalError("Must Override")
    }
//    加糖
    func addSugar(){
        print("加糖")
    }
//    加奶
    func addMilk(){
        print("加奶")
    }
}
class AmericanCoffee:Coffee{
    override func getName() -> String {
        return "美式咖啡"
    }
}
class LattecotfeeCoffee:Coffee{
    override func getName() -> String {
        return "拿铁咖啡"
    }
}
class CoffeeStore{
    func orderCoffee(type:String)->Coffee{
//        声明coffee类型的变量,根据不同类型创建不同的coffee子类对象
        var coffee:Coffee?
        if type == "american"{
             coffee = AmericanCoffee()
        }else if type == "latte" {
             coffee = LattecotfeeCoffee()
        }else {
             fatalError("对不起,您点的咖啡没有")
        }
//        加配料
        coffee?.addMilk()
        coffee?.addSugar()
        return coffee!
    }
}
​

测试代码

var store = CoffeeStore()
var coffee = store.orderCoffee(type: "latte")
print(coffee.getName())

运行结果

image-20220910220742978

现在我们已经实现了咖啡店点咖啡的效果,但是这个代码中的咖啡店类依赖于美式咖啡和拿铁咖啡具体的类,我们后续要再增加新品种卡布奇诺之类的咖啡,势必要修改咖啡店的类,这就违背了开闭原则,对修改关闭,那么我们如果解决呢

这些咖啡对象都需要创建,如果创建的时候直接new该咖啡对象,就会对该对象耦合严重,假如我们要更换对象,所有new对象的地方都需要修改一遍,工这显然违背了软件设计的开闭原则。如果我们使用工厂来生产对象,我们就只和工厂打交道就可以了彻底和对象解轉,如果要更换对象,直接在工厂里更换该对象即可,达到了与对象解耦的目的;所以说,工厂模式最大的优点就是:解耦

简单工厂模式

简单工厂不是一种设计模式,反而比较像一种编程习惯

简单工厂包含如下角色
  • 抽象产品:定义了产品的规范,描述了产品的主要特性和功能
  • 具体产品:实现或继承了抽象产品的类
  • 具体工厂:提供了创建产品的方法,调用者通过该方法来创建产品
//咖啡类
class Coffee{
    func getName()->String{
        fatalError("Must Override")
    }
//    加糖
    func addSugar(){
        print("加糖")
    }
//    加奶
    func addMilk(){
        print("加奶")
    }
}
class AmericanCoffee:Coffee{
    override func getName() -> String {
        return "美式咖啡"
    }
}
class LattecoffeeCoffee:Coffee{
    override func getName() -> String {
        return "拿铁咖啡"
  
    }
}
//简单咖啡工厂
class SimpleCoffeeFactory{
    func creatorCoffee(type:String)->Coffee{
        var coffee:Coffee?
        if type == "american"{
             coffee = AmericanCoffee()
        }else if type == "latte" {
             coffee = LattecotfeeCoffee()
        }else {
            fatalError("对不起,您点的咖啡没有")
        }
        return coffee!
    }
}
//咖啡店类
class CoffeeStore{
    func orderCoffee(type:String)->Coffee{
//        声明coffee类型的变量,根据不同类型创建不同的coffee子类对象
        let factory = SimpleCoffeeFactory()
//        调用生产咖啡的方法
        let coffee = factory.creatorCoffee(type: type)
       //        加配料
        coffee.addMilk()
        coffee.addSugar()
        return coffee
    }
}
​

测试代码

var store = CoffeeStore()
var coffee = store.orderCoffee(type: "latte")
print(coffee.getName())

测试结果

image-20220910224916687

分析咖啡店代码。咖啡店已经不依赖具体的咖啡对象了,解除了咖啡店和咖啡对象的耦合。

简单工厂模式的优缺点

  • 优点:封装了创建对象的过程,可以通过参数直获取对象。把对象的创建和业务逻辑层分开,这样以后就避免了修改客户代码,如果要实现新的类,直接修改工厂类,而不是在咖啡店类中修改,这样就降低了咖啡店类修改的可能性,更加容易扩展。
  • 缺点:增加新产品时还是需要修改工厂类的代码,违背了开闭原则
静态工厂

在开发中也有一部分人将工厂类中的创建对象定义为静态的,这个就是静态工厂不是,他也不是23中设计模式中的。

//简单咖啡工厂
class SimpleCoffeeFactory{
   static func creatorCoffee(type:String)->Coffee{
        var coffee:Coffee?
        if type == "american"{
             coffee = AmericanCoffee()
        }else if type == "latte" {
             coffee = LattecotfeeCoffee()
        }else {
            fatalError("对不起,您点的咖啡没有")
        }
        return coffee!
    }
}
​
//咖啡店类
class CoffeeStore{
    func orderCoffee(type:String)->Coffee{
//        声明coffee类型的变量,根据不同类型创建不同的coffee子类对象
//        调用生产咖啡的方法
        let coffee = SimpleCoffeeFactory.creatorCoffee(type: type)
       //        加配料
        coffee.addMilk()
        coffee.addSugar()
        return coffee
    }
}
​

只需要将简单咖啡工厂中的生产咖啡方法定义为静态的,这样在咖啡店只需要使用咖啡工厂类名直接调用创建咖啡的方法就可以,而不需要先实例化一个咖啡工厂类

工厂方法模式

简单工厂模式中,违背了开闭原则,使用工厂方法模式就可以完美的解决这个缺点,完全遵循开闭原则

概念 定义一个创建对象的接口,让子类决定实例化哪个产品对象。工厂方法是一个产品类的实例化延迟到其工厂的子类

结构 工厂方法模式的主要角色:

  • 抽象工厂:提供了创建产品的接口,调用者通过他访问具体工厂的工厂方法来创建产品。
  • 具体工厂:主要是实现抽象工厂中的抽象方法,完成具体产品的创建
  • 抽象产品:定义了产品规范,描述了产品的主要特性和功能
  • 具体产品:实现了抽象产品角色所定义的接口,由具体工厂来创建,它童具体工厂之间一一对应。
//咖啡类
class Coffee{
    func getName()->String{
        fatalError("Must Override")
    }
//    加糖
    func addSugar(){
        print("加糖")
    }
//    加奶
    func addMilk(){
        print("加奶")
    }
}
class AmericanCoffee:Coffee{
    override func getName() -> String {
        return "美式咖啡"
    }
}
class LatteCoffee:Coffee{
    override func getName() -> String {
        return "拿铁咖啡"
  
    }
}
//咖啡工厂 抽象工厂
protocol CoffeeFactory{
    func creatorCoffee()->Coffee
}
//美式咖啡对象
class AmericanCoffeeFactory:CoffeeFactory{
    func creatorCoffee() -> Coffee {
        return AmericanCoffee()
    }
}
//拿铁咖啡工厂 专门用来生产拿铁咖啡
class LatteeCoffeeFactory:CoffeeFactory{
    func creatorCoffee() -> Coffee {
        return LatteCoffee()
    }
}
//咖啡店类
class CoffeeStore{
    var factory:CoffeeFactory
    init(factory:CoffeeFactory) {
        self.factory = factory
    }
//    点咖啡功能
    func orderCoffee() -> Coffee{
       let coffee = factory.creatorCoffee()
        coffee.addMilk()
        coffee.addSugar()
        return coffee
    }
}
​

测试代码

//创建工厂对象
var coffeeFactory = LatteeCoffeeFactory()
//创建咖啡店对象
var store = CoffeeStore(factory: coffeeFactory)
//点咖啡
var coffee = store.orderCoffee()
print(coffee.getName())
​

运行结果

image-20220911102053718

现在咖啡店可以自由的引入咖啡工厂生产咖啡,如果有新的咖啡品种,只需要定义一个咖啡的子类,再创建一个新的咖啡工厂的实现类,不需要修改原来的代码,符合开闭原则。咖啡店只需要设置对应咖啡品种的工厂就可以了。

工厂方法模式优缺点

优点:

  • 用户只需要知道具体工厂的名称就可以得到所要的产品,无需知道产品的具体创建过程
  • 在系统增加新的产品时,只需要添加具体产品类和对应的具体工厂类,无需对原工厂进行任何修改,满足开闭原则

缺点

  • 每增加一个产品就要增加一个具体的产品类和一个对应的具体工厂类,这增加了系统的复杂度

感谢黑马程序员课程