一次“任性”的编码
最近又用Python写了一个小工具,结合自己在C#上已有的一点重构经验,让我又经历了一次不一样的旅行。
当我开始写这个工具的时候,我决定不做任何设计,来一次“任性”的编码,看最终会是什么样子。
于是,我开始用sublime创建了一个新文件py文件,然后开始在里面写main函数。
当我把main这个单词在屏幕上打出来时,我才开始思考。这个工具要实现的是验证数据库中数据的功能,因此我需要先打开数据库的连接。于是,我写出了下面这段代码
def main(): with pypodbc.connect('....') as con: with con.cursor() as cur: pass
当我写完这段代时,我意识到会有很多的值需要从数据库里面读出来验证。这就意味着这段代码肯定会被多次重复。因此,我决定对其进行第一次重构,移除重复代码。重构之后,我对如何实现这个工具有了进一步的认识。然后我又写出了下面这段代码
import sys import connection def get_qtn_id_by_sch_number(schedule_number): def get_qtn_id(cursor): cursor.execute("select id from phoenix.phx_quotations where lease_number='%s' "% schedule_number) for row in cursor.fetchall(): print('quotation id:%s'%row[0]) return row[0] return None return connection.run(get_qtn_id) def get_offer_id_by_sch_number(schedule_number): def get_offer_id(cursor): cursor.execute("select offer.id from phoenix.phx_offers offer inner join phoenix.phx_quotations qtn on qtn.id = offer.qtn_id where qtn.lease_number='%s'"%schedule_number) for row in cursor.fetchall(): print('offer id:%s'%row[0]) return row[0] return None return connection.run(get_offer_id)
在这段代码里面,连接数据库的代码没有重复,被connection类取代了。但是当我把第二个函数写完时,我又感觉到了不对劲。因为我意识到每段sql的执行只返回一个值。如果每个函数都这么写,还是会出现不少重复代码。于是我进行了第二次重构。
def get_qtn_id_by_sch_number(schedule_number): val = connection.single_value("select id from phoenix.phx_quotations where lease_number='%s' "% schedule_number) print('quotation id:%s'%val) def get_offer_id_by_sch_number(schedule_number): val = connection.single_value("select offer.id from phoenix.phx_offers offer inner join phoenix.phx_quotations qtn on qtn.id = offer.qtn_id where qtn.lease_number='%s'"%schedule_number) print('offer id:%s'%val)
现在的代码让我感到比较满意。于是我就顺着当前的思路,继续写出了下面这段代码
def get_qtn_id_by_sch_number(schedule_number, out): val = connection.single_value("select id from phoenix.phx_quotations where lease_number='%s' "% schedule_number) if out != None: out.add('quotation id:%s'%val) return val def get_offer_id_by_sch_number(schedule_number, out): val = connection.single_value("select offer.id from phoenix.phx_offers offer inner join phoenix.phx_quotations qtn on qtn.id = offer.qtn_id where qtn.lease_number='%s'"%schedule_number) if out != None: out.add('offer id:%s'%val) return val def get_vat_by_sch_number(schedule_number, out): val = connection.single_value(''' select vat from phoenix.phx_invoices where id=%s '''% get_inv_id_by_sch_number(schedule_number, out)) if out != None: out.add('VAT is:%s'%val) return val def get_inv_id_by_sch_number(schedule_number, out): val = connection.single_value("select inv_id from phoenix.phx_invoice_mappings where off_id=%s"%get_offer_id_by_sch_number(schedule_number, out)) inv_number = connection.single_value('''select inv.inv_number from phoenix.phx_invoices inv inner join phoenix.phx_invoice_mappings imp on inv.id = imp.inv_id where imp.off_id=%s '''%get_offer_id_by_sch_number(schedule_number, out)) if out != None: out.add('inv id:%s'%val) out.add('inv number:%s'%inv_number) return val def get_imd_status_by_sch_number(schedule_number, out): val = connection.single_value(''' select imd.status from phoenix.phx_imd_statuses imd inner join phoenix.phx_invoices inv on inv.ims_id = imd.id where inv.id = %s '''%get_inv_id_by_sch_number(schedule_number, out)) if out != None: out.add('invoice status:%s'%val) return val def get_purchase_option_by_sch_number(schedule_number, out): val = connection.single_value(''' select bo.billing_option_desc from phoenix.phx_billing_options bo inner join phoenix.phx_offer_bill_details obd on bo.billing_option_id = obd.purchase_opt_id where obd.offer_id = %s '''% get_offer_id_by_sch_number(schedule_number, out)) if out != None: out.add('billing option:%s'%val) return val
这时,我又觉得不对劲。每个方法里面都有对out的判断,这是一种重复。于是我进行了第三次重构。
def get_qtn_id_by_sch_number(schedule_number, out): sv = single_value.single_value("select id from phoenix.phx_quotations where lease_number='%s' "% schedule_number) sv.set_out(out) sv.set_title('quotation id') return sv.execute() def get_offer_id_by_sch_number(schedule_number, out): sv = single_value.single_value("select offer.id from phoenix.phx_offers offer inner join phoenix.phx_quotations qtn on qtn.id = offer.qtn_id where qtn.lease_number='%s'"%schedule_number) sv.set_out(out) sv.set_title('offer id') return sv.execute() def get_vat_by_sch_number(schedule_number, out): sv = single_value.single_value(''' select vat from phoenix.phx_invoices where id=%s '''% get_inv_id_by_sch_number(schedule_number, out)) sv.set_out(out) sv.set_title('VAT') return sv.execute() def get_inv_id_by_sch_number(schedule_number, out): sv = single_value.single_value("select inv_id from phoenix.phx_invoice_mappings where off_id=%s"%get_offer_id_by_sch_number(schedule_number, None)) sv.set_out(out) sv.set_title('inv id') sv_number = single_value.single_value('''select inv.inv_number from phoenix.phx_invoices inv inner join phoenix.phx_invoice_mappings imp on inv.id = imp.inv_id where imp.off_id=%s '''%get_offer_id_by_sch_number(schedule_number, out)) sv_number.set_out(out) sv_number.set_title('inv number') sv_number.execute() return sv.execute() def get_imd_status_by_sch_number(schedule_number, out): sv = single_value.single_value(''' select imd.status from phoenix.phx_imd_statuses imd inner join phoenix.phx_invoices inv on inv.ims_id = imd.id where inv.id = %s '''%get_inv_id_by_sch_number(schedule_number, out)) sv.set_out(out) sv.set_title('invoice status') return sv.execute() def get_purchase_option_by_sch_number(schedule_number, out): sv = single_value.single_value(''' select bo.billing_option_desc from phoenix.phx_billing_options bo inner join phoenix.phx_offer_bill_details obd on bo.billing_option_id = obd.purchase_opt_id where obd.offer_id = %s '''% get_offer_id_by_sch_number(schedule_number, out)) sv.set_out(out) sv.set_title('billing option') return sv.execute()
当我完成第三次重构时,我对目前的代码比较满意。因为它将过程中的数据转换成了single_value类的状态。single_value类根据当前的状态来决定是否执行这个过程。这样,if out != None就被封装了起来。这也增强了single_value的灵活性。
虽然重构了三次,但整个过程,都比较愉快。因为这三次重构都是根据开发推进的具体阻力来完成的。这也让我再次体会到了软件系统架构设计就是将不变的核心部分从可变的细节中分离出来的道理。
软件架构设计并不是对设计模式和架构模式的生搬硬套,而是对特定软件项目的不变核心部分的深刻理解和发现。