时间:2022-06-22 08:50:36
摘 要:在面向对象的软件设计过程中,为了提升软件的复用性、灵活性、扩展性、可维护性、稳定性,程序员通过封装、继承、派生等手段对软件架构有目的性的进行了一些额外设计。在编码之初,可能觉得有无这些设计并不重要,但是随着软件的需求的变化以及功能的扩展,在后续软件的更改与维护过程中就会越发体现出这些设计工作必要性。以往,这些设计工作都是由有经验的程序员实施的,随着设计模式的成熟和增多,逐渐对这些设计模式进行了归纳与提取,并总结出了各种经典的设计模式供程序员学习与效仿。
【关键词】设计模式 Python 工厂模式 策略模式
Python是一种流行的动态脚本语言,具有语法简捷清晰、跨平台、编码效率高、可维护性强等特点。Python经历了十多年的发展,目前已相当成熟。Python拥有许多C/C++所不具备的优势,比如:编码效率,代码的规范性、可读性,丰富的内置数据类型、跨平台等,但最重要的一点是,Python同样支持面向对象,具有继承、派生等特性。
本文通过实际事例对各种常见的设计模式及设计原则进行讲解,在体现设计模式必要性的同时也对python语言的进行了展示,为python程序员和爱好者提供了参考与借鉴。
1 设计模式
1.1 工厂模式
工厂模式指的是一个工厂对象决定创建出那一种具体的类对象。工厂模式由3个部分组成:工厂对象、抽象产品对象以及具体产品对象,工厂模式如所示。工厂模式通过抽象出的产品对象,减少了代码的耦合。工厂模式如图 1所示。
1.2 策略模式
策略模式是将一系列算法进行封装,对外提供统一的调用接口。策略模式中的各种算法虽然实现不同,但都以相同方式调用。策略模式减少了算法之间的耦合。策略模式如图 2所示。
2 实现及优化
2.1 原始设计
某公司需要对员工考勤记录进行统计。考勤记录为打卡机生成的excel表。根据需求可以很容易的想到要设计2个类来实现上述功能:一个为操作excel的类(ExcelClass类),另一个则是用于统计各种考勤信息的类(OverTime类)。
程序结构大致可以表述如下:
m_OverTimer = OverTime()
myExcel = ExcelClass(“考勤记录.xls”)
m_AllRecords = myExcel. getallRecord
result = m_OverTimer. Process(m_AllRecords)
2.2 简单工厂模式
接近年终,公司领导为完成年度任务,要求部分员工实行611工作制,并严格考勤,于是考勤软件需要实现对611员工迟到、早退、旷工的特殊处理。首先肯定会想到的是要对OverTime进行派生,派生出对普通员工统计的子类(OverTimeNormal)和对611制员工统计的子类(OverTime611)对Process函数进行考勤处理。然后在业务代码中对员工姓名进行判断,如果属于611工作制的员工,则生成OverTime611类对象进行处理,反之则生成OverTimeNormal对象进行处理。
程序大致可以表述如下:
myExcel = ExcelClass(“考勤记录.xls”)
m_AllRecords = myExcel. getallRecord
if (m_AllRecords.name in 611NameList):
m_OverTimer = OverTime611
result = m_OverTimer. Process(m_AllRecords)
else:
m_OverTimer = OverTimeNormal
result = m_OverTimer. Process(m_AllRecords)
这样虽然可以达到目的,但是如果再派生出其他之类(如724工作制),那么不仅需要对派生类进行编写,还需要改动业务代码。这无疑增加了程序的耦合性。于是可以考虑采用工厂模式。
定义了一个工厂函数类,它依据员工姓名生成对应的m_OverTimer.对象,不同对象调用子类重载的Process函数对考勤时间进行计算。
工厂类表述如下:
class factory():
def __init__ (self,name):
if (name in 611NameList):
self.m_OverTimer = OverTime611
else:
self.m_OverTimer = OverTimeNormal
业务代码大致可以表述如下:
myExcel = ExcelClass(“考勤记录.xls”)
m_AllRecords = myExcel. getallRecord
m_factory = factory(m_AllRecords.name)
result = m_factory. Process(m_AllRecords)
但是存在一个问题,就是所有子类对象必须有同样的方法名称,要是不同对象之间不是从同一个父类派生而来,那方法的名称也可能不同,这样就没法使用工厂模式了。策略模式可以解决这个问题。
2.3 策略模式
策略模式,通过将彼此无关的方法进行封装,从而对外提了供统一的抽象接口。策略类大致代码表述如下:
Class Strategy():
def Strategy (self, m_AllRecords):
if AllRecords .name in 611NameList:
return OverTime611. Process( )
else:
return OverTimeNormal. Process( )
此时OverTime611的Process和OverTimeNormal的Process可以是不同的名称,但通过Strategy封装后,抽象出统一的调用接口:Strategy。另外Strategy也结合了工厂模式,可以根据员工姓名调用对应考勤子类重载后的Process方法。
业务代码大致可以表述如下:
myExcel = ExcelClass(“考勤记录.xls”)
m_AllRecords = myExcel. getallRecord
m_ strategy = Strategy()
result = m_ strategy. Strategy (m_AllRecords)
3 模式分析
(1)通过派生的方式,可以扩展各种制度下的考勤统计,各派生类之间无耦合,实现了开放-封闭原则。
(2)通过使用简单工厂模式,将判断生成子类对象的工作从业务代码剥离。业务代码通过调用抽象出的工厂类对象,减少了业务代码与子类的耦合,在进行子类扩展时,只需修改工厂类,而无需对业务代码进行修改。
(3)策略模式将各子类的方法甚至是其他类对象的方法通过聚合而不是继承的方式抽象出一致的调用接口,是遵循接口编程的应用。
(4)除了工厂模式和策略模式外,还对底层第三方库操作EXCEL的方法进行封装,提供抽象接口,将业务代码和底层接口的剥离,减少耦合,实现了依赖倒转原则。
4 结论
本文不但对工厂模式与策略模式进行理论说明,还通过实例对整个优化过程进行了详细的讲解。这种循序渐进的方式让读者充分体会到了各种设计模式的好处,避免了知道理论知识却不知道何时使用的问题。
基于python语言的实现,不仅为python语言使用者提供了设计模式的借鉴与参考,更为广大程序员展示了python语言的魅力。
参考文献
[1]程杰.大话设计模式[M].清华大学出版社,2003.
[2]Erich Gamm、Richard Helm、Ralph Johnson,John Vlissides.译者:李英军、马骁星、蔡敏、刘建中.可以复用面向对象软件的基础[M].机械工业出版社,2008.
[3]Alan Shalloway、James R.Trott.译者:徐言声.设计模式解析[M].人民邮电出版社,2011.
作者单位
中国电子科技集团公司第十研究所 四川省成都市 610036