[Python设计模式] 第15章 如何兼容各种DB——抽象工厂模式
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——抽象工厂模式的更多相关文章
- [Python设计模式] 第7章 找人帮忙追美眉——代理模式
github地址:https://github.com/cheesezh/python_design_patterns 题目1 Boy追求Girl,给Girl送鲜花,送巧克力,送洋娃娃. class ...
- [Python设计模式] 第9章 如何准备多份简历——原型模式
github地址:https://github.com/cheesezh/python_design_patterns 题目 设计一个简历类,必须有姓名,可以设置性别和年龄,即个人信息,可以设置曾就职 ...
- [Python编程实战] 第一章 python的创建型设计模式1.1抽象工厂模式
注:关乎对象的创建方式的设计模式就是“创建型设计模式”(creational design pattern) 1.1 抽象工厂模式 “抽象工厂模式”(Abstract Factory Pattern) ...
- C#设计模式学习笔记:(3)抽象工厂模式
本笔记摘抄自:https://www.cnblogs.com/PatrickLiu/p/7596897.html,记录一下学习过程以备后续查用. 一.引言 接上一篇C#设计模式学习笔记:简单工厂模式( ...
- 《Think Python》第15章学习笔记
目录 <Think Python>第15章学习笔记 15.1 程序员定义的类型(Programmer-defined types) 15.2 属性(Attributes) 15.3 矩形( ...
- 大话设计模式C++实现-第15章-抽象工厂模式
一.UML图 二.概念 抽象方法模式(Abstract Factory):提供一个创建一系列相关或互相依赖对象的接口,而无需指定他们详细的类. 三.包括的角色 (1)抽象工厂 (2)详细工厂:包含详细 ...
- 设计模式(Python)-简单工厂,工厂方法和抽象工厂模式
本系列文章是希望将软件项目中最常见的设计模式用通俗易懂的语言来讲解清楚,并通过Python来实现,每个设计模式都是围绕如下三个问题: 为什么?即为什么要使用这个设计模式,在使用这个模式之前存在什么样的 ...
- 设计模式之第2章-抽象工厂模式(Java实现)
设计模式之第2章-抽象工厂模式(Java实现) “上次是我的不对,贿赂作者让我先讲来着,不过老婆大人大人有大量,不与我计较,这次还让我先把上次未讲完的应用场景部分给补充上去,有妻如此,夫复何求.”(说 ...
- 大话设计模式Python实现- 抽象工厂模式
抽象工厂模式(Abstract Factory Pattern):提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们的类 下面是一个抽象工厂的demo: #!/usr/bin/env pyth ...
随机推荐
- Codeforces 316G3 Good Substrings 字符串 SAM
原文链接http://www.cnblogs.com/zhouzhendong/p/9010851.html 题目传送门 - Codeforces 316G3 题意 给定一个母串$s$,问母串$s$有 ...
- .net core webapi 后台获取客户端ip地址
Asp.Net Core2.0获取客户IP地址,及解决发布到Ubuntu服务器获取不到正确IP解决办法 1.获取客户端IP地址实现方法(扩展类) 1 using Microsoft.AspNetC ...
- 003 使用SpringMVC开发restful API--查询用户
一:介绍说明 1.介绍 2.restful api的成熟度 二:编写Restful API的测试用例 1.引入spring的测试框架 在effective pom中查找 2.新建测试包,测试类 3.测 ...
- Spark中map与flatMap
map将函数作用到数据集的每一个元素上,生成一个新的分布式的数据集(RDD)返回 map函数的源码: def map(self, f, preservesPartitioning=False): &q ...
- JavaEE 之 Spring(三)
1.Spring Web MVC a.定义:是一种基于Java的实现了Web MVC设计模式的请求驱动类型的轻量级Web框架,即使用了MVC架构模式的思想,将web层进行职责解耦,基于请求驱动指的就是 ...
- python3 在 windows 读取路径多了一个\u202a 是咋回
python3 在 windows 读取路径多了一个\u202a 是咋回事
- Ultra-QuickSort POJ - 2299 (逆序对)
In this problem, you have to analyze a particular sorting algorithm. The algorithm processes a seque ...
- Linux添加系统环境变量
在Linux下使用源码安装软件的时候,通常只能在软件安装目录下使用该软件命令(使用yum命令安装的除外),这样太麻烦,我们希望全局使用,可以将软件安装路径添加到系统环境变量里. 添加环境变量有2种方法 ...
- ul无点标签左移
ul标签去除掉点,ul li 块仍会在原来的位置,即与上一块内容相对右移一点. 这是 ul标签的默认padding值导致的. 修改style或者CSS中的class为如下即可 { list-style ...
- 用单向链表实现两数倒序相加(java实现)
很久没做算法题了,准备重操旧业,于是刷了一波LeetCode,看到一个比较经典的链表算法题,分享出来. 题目 给定两个非空链表来表示两个非负整数.位数按照逆序方式存储,它们的每个节点只存储单个数字.将 ...