转自:Sindri的小巢(简书)
设计模式(Design pattern)是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。GoF提出了23种设计模式,本系列将使用Swift语言来实现这些设计模式。
概述
模板设计模式是最基础的设计模式之一,在网上被称作模板方法模式,但实际运用时这种设计模式却不仅仅局限于方法。因此笔者对于模板设计模式的理解定义如下:
【模板设计模式】将常见的方法以及对象成员进行封装,创建了一个实现一组抽象方法以及抽象对象成员作为对象实现使用的模板
坏味道的代码
在Swift中有个有趣的数据结构——元组Tuples,现在有这么一段代码。其中传入的每一个元祖表示某个商品的数据类型:
func calculateTax(product: (String, Double, Int)) -> Double { return product.1 * 0.17 } func calculateStockValue(products: [(String, Double, Int)]) -> Double { return products.reduce(0.0){ $0 + $1.1 * Double($1.2) } } override func viewDidLoad() { super.viewDidLoad() let products = [ ("Pizazz", 99.8, 10), ("Hamburger", 25.6, 8), ("Beef", 19.9, 30) ] print("The tax of \(products[0].1) is \(calculateTax(product[0]))") print("The stock value of all products is \(calculateStockValue(products))") }
上面的方法通过传入一个或者多个元组用来计算增值税(17%)以及商品存货总额。毫无疑问,对于稍有经验的开发者来说,都能说出这段代码有太大的坏味道了:两个方法过度的依赖于传入的元组结构,这意味着几乎无法复用这些方法
高耦合关系
模板设计模式
在开发语言从面向过程发展到面向对象的过程中,高级语言以及开发框架早已利用各种设计模式进行了封装,我们在不知不觉中享受着这种封装带来的便利却不自知。上方代码中的坏味道主要因为参数类型以及参数值受到限制,为了避免这种坏味道出现,通常使用模板设计模式来解决过高的耦合度。
Swift的模板设计模式
在Swift中,模板设计模式通过使用class和struct两种数据类型作为类对象的创建结构模板,用来创建不同内容的具体对象。因此改变之后的代码如下:
func calculateTax(product: Product) -> Double { return product.price * 0.17 } func calculateStockValue(products: [Product]) -> Double { return products.reduce(0){ return $0 + $1.price * Double($1.stock) } } class Product { var name: String var price: Double var stock: Int init(name: String, price price: Double, stock stock: Int) { self.name = name self.price = price self.stock = stock } }
通过以class结构为模板,我们可以创建各种不同属性的商品。虽然在定义class结构的过程中增加了一些额外的工作,但一来这使得我们的代码更加符合OOP的编程思想,二来解除了方法和元组之间的高度耦合。但这样的代码仍然存在另一个耦合问题——方法和类之间的耦合。如果有一天,项目需要面对全球市场,但是在不同的国家税种不同、税率也不同,这段代码也就有了另外的坏味道。因此我们需要把这两个方法同样放到class结构中成为方法模板
class Product { var name: String var price: Double var stock: Int init(name: String, price price: Double, stock stock: Int) { self.name = name self.price = price self.stock = stock } private func calculateTax() -> Double { return price * 0.17 } private func calculateStockValue() -> Double { return price * Double(stock) } func log() { print("The tax of \(name) is \(calculateTax())") print("The stock value of product is \(calculateStockValue())") } } class Product_Japan: Product { override private calculateTax() -> Double { return price * 0.05 } }
将方法抽象封装到父类中,并在子类中重写具体实现,这是模板方法模式的典型做法
模板方法模式
为了计算不同地区的税率要实现多个这样的子类的做法带来的代码量也是十分可观的,而且当税率成了一个可改变的因素时,方法可以改变成为接收税率进行计算:
func calculateTax(ratio: Double) -> Double { return price * ratio }
总结
模式优点
-
将不变的行为抽离封装起来,去除了重复代码
-
通过子类重写父类实现,可以扩展新的行为
模式缺点
-
class和struct都能利用模板模式来解除高耦合,但两者在传递时采用不同的方式,这容易引发错误
-
容易造成子类的数量暴增,导致代码设计更加抽象