python 与设计模式 ——工厂与单例

原创
2013/11/27 11:19
阅读数 1.2K

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——开发经验,要的就是干货

展开阅读全文
加载中
点击引领话题📣 发布并加入讨论🔥
打赏
0 评论
14 收藏
1
分享
返回顶部
顶部