python 与设计模式
源码地址:http://git.oschina.net/duoduo3_69/python_design_pattern
git checkout v001(这个版本与此篇博客相符)
zarkpy里面运用了很多设计模式,以前一直很费解python是怎么应用 设计模式的,他没有接口,也不是编译行的语言。直到sdjl(zarkpy 的原创者)对我说:
设计模式只是参考,重要的是自己写一些东西。
学习的过程中有一些思考,因此记录在这里。
这些原理性的东西也可以运用到别的一些动态语言中去,javascript 则有一本《JavaScript设计模式》可以参考。
Dao
dao/*.py
如果你熟悉java,自然会知道java里面有dao这个模式(Data Access Object), 继承自dao的类有数据库交互的能力。
对于python来说,你可以把dao命名为model(Django)等别的名字。
dao这个类应该是单例的,它类似于一个工具类,想象一下,如果有个User 类,你可能需要对user对象进行get,insert,delete,remove(增删改查), 这时是user对象与数据库之间的交互(把user这条记录扔到数据库里), user对象的状态在交互过程中是不应该改变的。
# -*- coding: utf-8 -*-
class Dao(object):
"""docstring for BaseDao"""
def __init__(self):
super(Dao, self).__init__()
def get(self,item_id):
"""docstring for get"""
pass
def insert(self,data):
"""docstring for get"""
pass
def delete(self,item_id):
"""docstring for get"""
pass
def update(self,item_id,data):
"""docstring for get"""
pass
User继承自Dao
# -*- coding: utf-8 -*-
from Dao import Dao
class User(Dao):
"""docstring for User"""
def __init__(self):
super(User, self).__init__()
单例模式实际上就是使用缓存。
java中使用static这种类加载的方式,将对象缓存在这里, 在编译执行的时 候确保对象是真正的单例。
public class Singleton {
private static Singleton uniqueInstance = new Singleton();
private Singleton() {
}
public static Singleton getInstance() {
return uniqueInstance;
}
}
约定优于配置
rail 里面有句话是这样说的,约定优于配置(当然java也有这些东西, 想象下每个java bean里面那一堆getter setter吧,可以试试lombok), 在python里面可以使用工厂模式加缓存的方法,实现单例, 具体请看DaoFactory的实现方式,这里还用到了python里面模块加载 的一些技巧,参见helper.py与dao/init.py。
helper.py
# -*- coding: utf-8 -*-
import os,sys,glob
# 获得一个文件夹下所有的module,主要用于__init__.py文件自动import所有class
def getDirModules(dir_path, dir_name, except_files=None):
if not except_files:except_files = []
assert(os.path.exists(dir_path))
ret_modules = []
for file_path in glob.glob(dir_path+'/*.py'):
file_name = file_path.rpartition('/')[2].rpartition('.')[0]
if file_name not in except_files:
__import__(dir_name.strip('.') + '.' + file_name)
if file_name in dir(getattr(sys.modules[dir_name.strip('.')], file_name)):
ret_modules.append((file_name, getattr(getattr(sys.modules[dir_name.strip('.')], file_name), file_name)))
return ret_modules
dao/init.py #coding=utf-8 import os import helper # ../helper.py
EXCEPT_FILES = ['__init__']
for module_name, module in helper.getDirModules(os.path.split(os.path.realpath(__file__))[0], __name__, except_files=EXCEPT_FILES):
exec('%s = module' % module_name)
工厂模式
DaoFactory.py
为什么要使用工厂模式呢?为了解放生产力,减少一堆'import XXX,m = XXX()'等 代码的编写吧。对于java来说则是new的操作。
工厂模式就是可以通过传进来的字符串参数来生成对象,当然可以创建 不同的工厂来满足不同的需求,例如对于dao模块我创建了一个工厂是 dao_factory,结合缓存实现了单例,这样用户在调用的时候每次生成的 就会是同一个对象。
ipython -i DaoFactory.py
u1 = dao_factory("User")
u2 = dao_factory("User")
u1 == u2 # True
工厂加缓存实现的单例模式,应该就是spring所做的事情(笔者并没有 看spring 的源码,猜测)。
DaoFactory.py,
# -*- coding: utf-8 -*-
import sys
CACHED_DAO = {}
def dao_factory(dao_name):
"""docstring for dao_factory"""
assert isinstance(dao_name,(str,unicode))
cache_key = dao_name
if CACHED_DAO.has_key(cache_key):
return CACHED_DAO[cache_key]
else:
import dao
try:
assert(hasattr(sys.modules["dao"],cache_key))
dao = getattr(sys.modules["dao"],cache_key)
dao = dao()
except:
print 'dao name is',cache_key
raise
CACHED_DAO[cache_key] = dao
return dao
下面的代码块用到了helper.py以及dao/init.py里面的小技巧,当导入dao模块的时候,会执行__init__.py里面的方法块,它会将这个模块下的所有模块import进来,使得dao模块拥有一些属性,可以在交互模式尝试dir(dao)来看看
import dao
try:
assert(hasattr(sys.modules["dao"],cache_key))
dao = getattr(sys.modules["dao"],cache_key)
dao = dao()
巨大的ps:希望能一起写issue
[开源项目]skill_issues——开发经验,要的就是干货