模板方法(Template Method)
解决算法框架问题
描述
定义了一个算法的骨架,并将某些步骤延迟到子类中进行实现,从而使得算法的具体实现能够在子类中自由变化。这种方式可以大大减少重复代码,同时也能够提高代码的可读性和可维护性。
适用环境
当多个子类有相同的行为,但实现细节不同时使用;当需要控制子类的扩展时使用。
优点:
可以通过定义一个算法骨架来保持代码的一致性;可以在不修改算法结构的情况下扩展算法。
缺点:
由于模板方法模式采用的是基于继承的实现方式,因此可能导致代码的复杂度增加。
违反原则:
开放-封闭原则:如果需要更改算法结构,则需要对所有的子类进行修改,可能违反该原则。
代码实现
卖书的过程中需要记录每本书的销售信息,包括销售日期、销售数量和销售价格等。现在使用传统的方式来记录销售信息:
class Book:
def __init__(self, name, price):
self.name = name
self.price = price
self.sales_records = []
def sell_book(self, date, quantity, price):
record = {
"date": date, "quantity": quantity, "price": price}
self.sales_records.append(record)
def print_sales_record(self):
for record in self.sales_records:
print(f"销售日期:{record['date']}, 销售数量:{record['quantity']}, 销售价格:{record['price']}")
book1 = Book('Python编程从入门到实践', 68.0)
book1.sell_book('20220101', 10, 60.0)
book1.sell_book('20220102', 5, 70.0)
book1.print_sales_record()
以上代码存在以下问题:
- 每次销售需要手动创建一个记录对象并添加到销售记录列表中容易出错。
- 打印销售记录时需要遍历所有的销售记录进行输出不够直观。
可以通过模板方法模式来解决这个问题,具体实现如下:
# 创建一个抽象类RecordBook,定义一个模板方法sell_book()和两个抽象方法get_date()和get_price(),
# 其中模板方法中固定了销售记录的处理流程,具体子类只需要实现get_date()和get_price()方法即可。
from abc import ABC, abstractmethod
class RecordBook(ABC):
def sell_book(self, quantity):
date = self.get_date()
price = self.get_price()
self.add_sales_record(date, quantity, price)
@abstractmethod
def get_date(self):
pass
@abstractmethod
def get_price(self):
pass
def add_sales_record(self, date, quantity, price):
record = {
"date": date, "quantity": quantity, "price": price}
self.sales_records.append(record)
def print_sales_record(self):
for record in self.sales_records:
print(f"销售日期:{record['date']}, 销售数量:{record['quantity']}, 销售价格:{record['price']}")
# 创建一个具体子类,实现抽象类中定义的两个抽象方法。
class Book(RecordBook):
def __init__(self, name, price):
self.name = name
self.price = price
self.sales_records = []
def get_date(self):
return '2022-01-01' # 假设使用当前日期作为销售日期
def get_price(self):
return self.price # 销售价格就是图书价格
# 在客户端代码中创建Book类的实例,并调用其模板方法sell_book()来记录销售信息
if __name__ == "__main__":
book1 = Book('Python编程从入门到实践', 68.0)
book1.sell_book(10)
book1.sell_book(5)
book1.print_sales_record()