python 与设计模式 ——工厂与装饰者

python 与设计模式第二篇

添加了test.py,里面的单元测试有使用的方法。

源码地址:http://git.oschina.net/duoduo3_69/python_design_pattern

git checkout v002(这个版本与此篇博客相符)

承接上文python 与设计模式 ——工厂与单例

装饰者模式

modeldecorator/*

装饰者模式是一个非常棒的设计模式,他能在不改变基类代码的情况下 动态的添加一些功能,与之前的工厂模式结合,就会实现一行代码添加 一个功能的效果,这也是zarkpy里面装饰者的设计。

装饰者模式本身会有一个被装饰者对象的引用,保持原来接口的情况下 动态添加一些功能,然后再重新调用被装饰者原来的方法,达到一种添 加效果的功能。

装饰者并不是本文的亮点,亮点是工厂与装饰者结合之后,amazing!真 正做到一行代码添加一个功能。请先耐心看前半部分。

Decorator.py,装饰者的基类,将请求的方法转交给model,接受两个 参数,model是需要装饰的对象,arguments是装饰器里面可能用到的 参数。

# -*- coding: utf-8 -*-

class Decorator(object):
    """docstring for Decorator"""
    def __init__(self, model, arguments):
        super(Decorator, self).__init__()
        assert(isinstance(arguments,dict))
        self.model = model
        self.arguments = arguments

    def __getattr__(self, attr):
        """docstring for __getattr__"""
        return getattr(self.model, attr)

依然以之前的Dao为例,对于Dao来说,经常会用到的功能可能有分页,排序, 如何,装饰者在这里显得非常到位(代码中有一个验证的例子,实际上应该 写成模板方法,以后再行修改)。

如果要对Dao中原有的功能添加一些功能的话,装饰者要求与原接口必须相同, 因为python中没有强制的编译,所以这也是一种约定。

为了方便阅读,先把dao贴过来。

# -*- coding: utf-8 -*-
class Dao(object):
    """docstring for BaseDao"""
    decorator = []
    def __init__(self):
        super(Dao, self).__init__()
        
    def get(self,item_id):
        """docstring for get"""
        print 'Dao', 'get', item_id
    
    def insert(self,data):
        """docstring for get"""
        print 'Dao', 'insert', data

    def delete(self,item_id):
        """docstring for get"""
        print 'Dao', 'delete', item_id

    def update(self,item_id,data):
        """docstring for get"""
        print 'Dao', 'update', item_id, data

    def all(self,env=None):
        print 'Dao','all',env

一个验证的装饰者,可以看到,在处理一些事件之后,最终调用 被装饰者原来的方法self.model.get(item_id),以到达装饰 的效果。

# -*- coding: utf-8 -*-
from Decorator import Decorator
    
class Invaildate(Decorator):
    """docstring for Invaildate"""
        
    def get(self,item_id):
        """docstring for get"""
        print 'invalidate', 'get',self.arguments
        self.model.get(item_id)
    
    def insert(self,data):
        """docstring for get"""
        print 'invalidate', 'insert',self.arguments
        self.model.insert(data)
    
    def delete(self,item_id):
        """docstring for get"""
        print 'invalidate', 'delete',self.arguments
        self.model.delete(item_id)
        
    def update(self,item_id,data):
        """docstring for get"""
        print 'invalidate', 'update',self.arguments
        self.model.update(item_id,data)

一个分页装饰者:

# -*- coding: utf-8 -*-
import copy as _copy
from Decorator import Decorator

class Pagination(Decorator):
    """docstring for Pagination"""
    
    def all(self,env=None):
        env = _copy.deepcopy(self.arguments) if self.arguments is not None else {}
        # 一些分页有关的代码
        if env.has_key('per_page_num'):
            print 'Pagination',env['per_page_num']
        return self.model.all(env)

装饰者的使用方法(不使用工厂):

def test_invaild_decorator(self):
    print 'test_invaild_decorator'
    print '============================'
    import DaoFactory
    import modeldecorator

    u1 = DaoFactory.dao_factory("User")
    print 'before decorator'
    print '========'
    u1.get(1)
    u1.update(1,1)
    u1.insert(1)
    u1.delete(1)

    u1 = modeldecorator.Invaildate(u1,dict(arg1="test1"))

    print 'after decorator'
    print '========'
    u1.get(1)
    u1.update(1,1)
    u1.insert(1)
    u1.delete(1)

    print 'after two decorator'
    u1 = modeldecorator.Pagination(u1,dict(per_page_num ="10"))
    u1.all()

    u1 = modeldecorator.Invaildate(u1,dict(arg2="test2"))
    print 'after three decorator'
    print '========'
    u1.get(1)
    u1.update(1,1)
    u1.insert(1)
    u1.delete(1)

单元测试结果

.test_invaild_decorator
============================
before decorator
========
Dao get 1
Dao update 1 1
Dao insert 1
Dao delete 1
after decorator
========
invalidate get {'arg1': 'test1'}
Dao get 1
invalidate update {'arg1': 'test1'}
Dao update 1 1
invalidate insert {'arg1': 'test1'}
Dao insert 1
invalidate delete {'arg1': 'test1'}
Dao delete 1
after two decorator
Pagination 10
Dao all {'per_page_num': '10'}
after three decorator
========
invalidate get {'arg2': 'test2'}
invalidate get {'arg1': 'test1'}
Dao get 1
invalidate update {'arg2': 'test2'}
invalidate update {'arg1': 'test1'}
Dao update 1 1
invalidate insert {'arg2': 'test2'}
invalidate insert {'arg1': 'test1'}
Dao insert 1
invalidate delete {'arg2': 'test2'}
invalidate delete {'arg1': 'test1'}
Dao delete 1

装饰者与工厂结合

细心的读者可能会看到Dao.py这个文件中多了与上个版本相比多了 decorator = []

有了上面装饰者调用的例子,不难写出下面的工厂方法:

CACHED_DECORATOR_DAO = {}
def dao_decorator_factory(dao_name):
    assert isinstance(dao_name,(str,unicode))
    cache_key = dao_name

    if CACHED_DECORATOR_DAO.has_key(cache_key):
        return CACHED_DECORATOR_DAO[cache_key]

    else:
        import dao
        import modeldecorator
        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

        decorator = dao.decorator if dao.decorator else []

        try:
            for d,args in decorator:
                assert(hasattr(sys.modules["modeldecorator"],d))
                dao = getattr(sys.modules["modeldecorator"],d)(dao,args)
        except:
            raise

        CACHED_DECORATOR_DAO[cache_key] = dao
        return dao

主要添加了这几行,for不断的添加装饰。

       decorator = dao.decorator if dao.decorator else []

        try:
            for d,args in decorator:
                assert(hasattr(sys.modules["modeldecorator"],d))
                dao = getattr(sys.modules["modeldecorator"],d)(dao,args)
        except:
            raise

        CACHED_DECORATOR_DAO[cache_key] = dao

精彩的地方在这里

为你的dao添加新的功能,例如为一个Todo类添加分页,和验证的功能(验 证这个装饰者的例子确实不好,应该是模板方法),只需像下面的例子添加 两行代码,分页和验证的功能就ok了,调用的时候需要使用之前写好的工厂, 代码在后面。

# -*- coding: utf-8 -*-

from Dao import Dao
    
class Todo(Dao):
    """docstring for Todo"""
        
    decorator = [
            ("Invaildate",dict(arg1 = "invaildate")),
            ("Pagination",dict(per_page_num = "10")),
            ]
    def __init__(self):
        super(Todo, self).__init__()

单元测试里面的演示代码:

def test_decorator_factory(self):
    print 'test_decorator_factory'
    print '======================================'
    import DaoFactory

    u1 = DaoFactory.dao_decorator_factory('Todo')
    u1.get(1)
    u1.update(1,1)
    u1.insert(1)
    u1.delete(1)
    u1.all()

结果

test_decorator_factory
======================================
invalidate get {'arg1': 'invaildate'}
Dao get 1
invalidate update {'arg1': 'invaildate'}
Dao update 1 1
invalidate insert {'arg1': 'invaildate'}
Dao insert 1
invalidate delete {'arg1': 'invaildate'}
Dao delete 1
Pagination 10
Dao all {'per_page_num': '10'}

经验小结

python虽然不想java,c++这些语言有明确的接口(interface,纯虚函数)从编译 和语法的角度强制实现接口,但是正如之前说过的:

设计模式仅仅是参考,重要的是写一些东西

参考设计模式,遵守一些约定(rail的名言,约定优于配置),自己也可以写框架 (不过最好不要重复发明轮子,但是如果你觉得有必要的话,可以为自己的团队 写适合自己团队的东西)。

最重要的是能解放生产力,宁花机器一分,不花程序员一秒。

如果是写中小型的web的话,可以看看python或rail,java真心慢(生产力)。

巨大的ps:希望能一起写issue

[开源项目]skill_issues——开发经验,要的就是干货

转载于:https://my.oschina.net/duoduo3369/blog/179610

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值