github地址:https://github.com/cheesezh/python_design_patterns

题目

如何让一个程序,可以灵活替换数据库?

基础版本

class User():
"""
用户类,模拟用户表,假设只有ID和name两个字段
"""
def __init__(self):
self.id = None
self.name = None class SqlServerUser():
"""
sqlserveruser类,用于操作User表
"""
def insert(self, user):
print("向SQL Server中添加一个User") def get_user(self, id):
print("从SQL Server中搜索User", id) def main():
user = User() su = SqlServerUser()
su.insert(user)
su.get_user(1) main()
向SQL Server中添加一个User
从SQL Server中搜索User 1

点评

这里之所以不能灵活更换数据库,是因为su = SqlServerUser() 将客户端和SQL Server绑定在一起,如果这里是“多态的”,那么就不需要考虑是SQL Server还是Access了。

这里可以用“工厂方法模式”改进,工厂方法模式是定义一个用于创建对象的接口,让子类决定实例化哪一个类。

改进版本1.0——工厂方法模式

from abc import ABCMeta, abstractmethod

class IUser():
__metaclass__ = ABCMeta @abstractmethod
def insert(self, user):
pass @abstractmethod
def get_user(self, id):
pass class SqlServerUser(IUser): def insert(self, user):
print("在SQL Server中添加一个User") def get_user(self, id):
print("从SQL Server中搜索User", id) class AccessUser(IUser): def insert(self, user):
print("在Access中添加一个User") def get_user(self, id):
print("从Access中搜索User", id) class IFactory():
__metaclass__ = ABCMeta @abstractmethod
def create_user(self):
pass class SqlServerFactory(IFactory):
def create_user(self):
return SqlServerUser() class AccessFactory(IFactory):
def create_user(self):
return AccessUser() def main():
user = User()
factory = SqlServerFactory()
iuser = factory.create_user() iuser.insert(user)
iuser.get_user(1) main()
在SQL Server中添加一个User
从SQL Server中搜索User 1

点评

现在如果要更换数据库,只需要把factory = SqlServerFactory()更改成factory = AccessFactory()即可。这里由于多态的关系,使得声明IUser接口的对象iuser事先并不知道在访问哪个数据库,却可以在运行时很好的完成工作,这就是业务逻辑与数据访问解耦。

但是,数据库中不可能只有一个User表,还可能有其他表,比如Department,那就需要增加好多个新的类。

class Department():
def __init__(self):
self.id = None
self.name = None class IDepartment():
__metaclass__ = ABCMeta @abstractmethod
def insert(self, department):
pass @abstractmethod
def get_department(self, id):
pass class SqlServerDepartment(IDepartment):
def insert(self, department):
print("在SQL Server中添加一个Department") def get_department(self, id):
print("从SQL Server中搜索Department", id) class AccessDepartment(IDepartment):
def insert(self, department):
print("在Access中添加一个Department") def get_department(self, id):
print("从Access中搜索Department", id) class IFactory():
__metaclass__ = ABCMeta @abstractmethod
def create_user(self):
pass @abstractmethod
def create_department(self):
pass class SqlServerFactory(IFactory):
def create_user(self):
return SqlServerUser() def create_department(self):
return SqlServerDepartment() class AccessFactory(IFactory):
def create_user(self):
return AccessUser() def create_department(self):
return AccessDepartment() def main():
user = User()
dept = Department() factory = SqlServerFactory() iuser = factory.create_user()
iuser.insert(user)
iuser.get_user(1) idept = factory.create_department()
idept.insert(dept)
idept.get_department(1) main()
在SQL Server中添加一个User
从SQL Server中搜索User 1
在SQL Server中添加一个Department
从SQL Server中搜索Department 1

点评

这样就可以做到,只需要更改factory = SqlServerFactory(),就可以随便切换数据库了。

当只有一个User类和User操作类的时候,只需要工厂方法模式就可以了。但是数据库中显然有很多的表,而SQL Server和Acess又是两大不同的类,所以解决这种涉及多个产品系列的问题,就需要使用抽象工厂模式。

抽象工厂模式

抽象工厂模式,提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们的具体类。

在上述问题中:

  • User和Department相当于两个抽象产品;
  • SqlServerUser和AccessUser是抽象产品User的具体产品实现;
  • IFactory是一个抽象工厂接口,里边包含所有的产品创建的抽象方法;
  • SqlServerFactory和AccessFactory是具体工厂;

通常的过程是,在运行时刻创建一个ConcretFactory类的实例,这个具体的工厂再创建具有特定实现的产品对象,也就是说,为创建不同的产品对象,客户端应使用不同的具体工厂。

抽象工厂模式的优点是什么?

最大的好处便是易于交换产品系列,由于具体工厂类在一个应用中只需要在初始化的时候出现一次,这就使得改变一个应用的具体工厂变得非常容易,它只需要改变具体工厂即可使用不同的产品配置。

其次的好处就是让具体的创建实例过程与客户端分离,客户端是通过它们的抽象接口操纵实例,产品的具体类名也被具体工厂的实现分离,不会出现在客户端代码中。

抽象工厂模式的缺点是什么?

抽象工厂模式可以很方便地切换两个数据库的访问代码,但是当需要增加功能,比如增加项目表Project,那就需要增加三个类IProject,SqlServerProject,AccessProject,还要更改IFactory,SqlServerFactory和AccessFactory。如果有100个调用数据访问的类,那要更改100次才能切换数据库,这是非常丑陋的做法。

用简单工厂改进抽象工厂

去除IFactory,SqlServerFactory和AccessFactory三个工厂类,取而代之的是DataAccess类。

class DataAcess():

    # 类变量,通过`类名.变量名`访问
db = "sql_server" @classmethod
def create_user(self):
if DataAcess.db == "sql_server":
return SqlServerUser()
elif DataAcess.db == "access":
return AccessUser() @classmethod
def create_department(self):
if DataAcess.db == "sql_server":
return SqlServerDepartment()
elif DataAcess.db == "access":
return AccessDepartment() def main():
user = User()
dept = Department() iu = DataAcess.create_user()
iu.insert(user)
iu.get_user(1) idept = DataAcess.create_department()
idept.insert(dept)
idept.get_department(1) main()
在SQL Server中添加一个User
从SQL Server中搜索User 1
在SQL Server中添加一个Department
从SQL Server中搜索Department 1

点评

所有用到简单工厂的地方,都可以考虑使用反射技术来去除swith或if-else,接触分支带来的耦合。

反射版本

import sys

def createInstance(module_name, class_name, *args, **kwargs):
class_meta = getattr(module_name, class_name)
obj = class_meta(*args, **kwargs)
return obj def main():
db = "Access" # load from config file
user = User()
dept = Department() iuser = createInstance(sys.modules[__name__], "{}User".format(db))
iuser.insert(user)
iuser.get_user(1) idept = createInstance(sys.modules[__name__], "{}Department".format(db))
idept.insert(dept)
idept.get_department(1) main()
在Access中添加一个User
从Access中搜索User 1
在Access中添加一个Department
从Access中搜索Department 1

[Python设计模式] 第15章 如何兼容各种DB——抽象工厂模式的更多相关文章

  1. [Python设计模式] 第7章 找人帮忙追美眉——代理模式

    github地址:https://github.com/cheesezh/python_design_patterns 题目1 Boy追求Girl,给Girl送鲜花,送巧克力,送洋娃娃. class ...

  2. [Python设计模式] 第9章 如何准备多份简历——原型模式

    github地址:https://github.com/cheesezh/python_design_patterns 题目 设计一个简历类,必须有姓名,可以设置性别和年龄,即个人信息,可以设置曾就职 ...

  3. [Python编程实战] 第一章 python的创建型设计模式1.1抽象工厂模式

    注:关乎对象的创建方式的设计模式就是“创建型设计模式”(creational design pattern) 1.1 抽象工厂模式 “抽象工厂模式”(Abstract Factory Pattern) ...

  4. C#设计模式学习笔记:(3)抽象工厂模式

    本笔记摘抄自:https://www.cnblogs.com/PatrickLiu/p/7596897.html,记录一下学习过程以备后续查用. 一.引言 接上一篇C#设计模式学习笔记:简单工厂模式( ...

  5. 《Think Python》第15章学习笔记

    目录 <Think Python>第15章学习笔记 15.1 程序员定义的类型(Programmer-defined types) 15.2 属性(Attributes) 15.3 矩形( ...

  6. 大话设计模式C++实现-第15章-抽象工厂模式

    一.UML图 二.概念 抽象方法模式(Abstract Factory):提供一个创建一系列相关或互相依赖对象的接口,而无需指定他们详细的类. 三.包括的角色 (1)抽象工厂 (2)详细工厂:包含详细 ...

  7. 设计模式(Python)-简单工厂,工厂方法和抽象工厂模式

    本系列文章是希望将软件项目中最常见的设计模式用通俗易懂的语言来讲解清楚,并通过Python来实现,每个设计模式都是围绕如下三个问题: 为什么?即为什么要使用这个设计模式,在使用这个模式之前存在什么样的 ...

  8. 设计模式之第2章-抽象工厂模式(Java实现)

    设计模式之第2章-抽象工厂模式(Java实现) “上次是我的不对,贿赂作者让我先讲来着,不过老婆大人大人有大量,不与我计较,这次还让我先把上次未讲完的应用场景部分给补充上去,有妻如此,夫复何求.”(说 ...

  9. 大话设计模式Python实现- 抽象工厂模式

    抽象工厂模式(Abstract Factory Pattern):提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们的类 下面是一个抽象工厂的demo: #!/usr/bin/env pyth ...

随机推荐

  1. BZOJ4003 [JLOI2015]城池攻占 左偏树 可并堆

    欢迎访问~原文出处——博客园-zhouzhendong 去博客园看该题解 题目传送门 - BZOJ4003 题意概括 题意有点复杂,直接放原题了. 小铭铭最近获得了一副新的桌游,游戏中需要用 m 个骑 ...

  2. webstorm 2017 激活破解方法大全

    webstorm 作为最近最火的前端开发工具,也确实对得起那个价格,但是秉着勤俭节约的传统美德,我们肯定是能省则省啊. 方法一:(更新时间:2018/4/8)v3.3 注册时,在打开的License ...

  3. es6的模块化--AMD/CMD/commonJS/ES6

    , ); ); }) , )); }); , )); ; export { firstName, lastName, year }; // es6引用 import { firstName, last ...

  4. TF:Tensorflow结构简单应用,随机生成100个数,利用Tensorflow训练使其逼近已知线性直线的效率和截距—Jason niu

    import os os.environ[' import tensorflow as tf import numpy as np x_data = np.random.rand(100).astyp ...

  5. HDU 1301-Jungle Roads【Kruscal】模板题

    题目链接>>> 题目大意: 给出n个城市,接下来n行每一行对应该城市所能连接的城市的个数,城市的编号以及花费,现在求能连通整个城市所需要的最小花费. 解题分析: 最小生成树模板题,下 ...

  6. java接口签名(Signature)实现方案

    预祝大家国庆节快乐,赶快迎接美丽而快乐的假期吧!!! 一.前言 在为第三方系统提供接口的时候,肯定要考虑接口数据的安全问题,比如数据是否被篡改,数据是否已经过时,数据是否可以重复提交等问题.其中我认为 ...

  7. continue — Skip to the next iteration of a loop in a shell script

    continue — Skip to the next iteration of a loop in a shell script

  8. 英语口语练习系列-C13-聚会

    词汇 音频 1. apartment [əˈpɑ:tmənt] n. 公寓 a big / small apartment 一个大的/小的公寓 in an apartment 在公寓 2. arm [ ...

  9. 4535 ACM 礼尚往来 数学排列组合

    题目:http://acm.hdu.edu.cn/showproblem.php?pid=4535 题意:每个礼物都不相同的组合个数 数学规律: 将每个女友排序为1···n,对应的女友送男友的礼物排序 ...

  10. 在npm上发布一个自己的包

    1.首先你要在npm上创建一个账号,这里需要输入邮箱的,注意激活邮箱否则无法publish自己的包 2.在本地创建一个文件夹,输入npm init初始化项目,这里是我使用npm init创建的pack ...