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. Vijos1983 NOIP2015Day2T3 运输计划 transport LCA

    题目链接Vijos 题目链接UOJ 该博客在博客园的链接 转载一个大佬的题解: 点击这里->大佬题解 下面谈谈我的感悟: 当然写代码也是写的很艰辛: 我力劝C++的同胞们,这题卡常数,Dfs党会 ...

  2. P2577 [ZJOI2005]午餐 状压DP

    题目描述 上午的训练结束了,THU ACM小组集体去吃午餐,他们一行N人来到了著名的十食堂.这里有两个打饭的窗口,每个窗口同一时刻只能给一个人打饭.由于每个人的口味(以及胃口)不同,所以他们要吃的菜各 ...

  3. java.io.File中字段的使用

    File.pathSeparator指的是分隔连续多个路径字符串的分隔符,例如:Java   -cp   test.jar;abc.jar   HelloWorld就是指“;” File.separa ...

  4. Python做性能测试-1、Locust基础篇

    前言:说起性能测试,大家想到的基本上都是工具jmeter和loadrunner多少也对执行性能测试的方式有一点认识,这些工具基本都实现了请求-响应-结果统计分析这样完整的测试链路,用户方面只需组织这些 ...

  5. 依赖配置中心实现注有@ConfigurationProperties的bean相关属性刷新

    配置中心是什么 配置中心,通过key=value的形式存储环境变量.配置中心的属性做了修改,项目中可以通过配置中心的依赖(sdk)立即感知到.需要做的就是如何在属性发生变化时,改变带有@Configu ...

  6. SpringBoot使用LomBok

    Lombok是什么?它是一个能帮我们消除那些必须要写但是重复的代码,比如setter,getter,构造函数之类的方法. 首先先简单说一下idea安装lombok,有2种方法: 1.直接从http:/ ...

  7. C# 的Chart

    Axis Label 横纵坐标的文字 (比如 0 20 40 ....) Axis Title 横纵坐标的代表什么(比如 Y Axis Title) Chart Area 图标所在位置 Chart P ...

  8. Django之ORM初始

    上一篇写了一个静态的登录验证. 实景情况网页的登录验证都是动态验证的,过程其实是从后端拿到储存的账户与密码来和前端的输入信息进行匹配校验的. 一.把项目拆分,来一个单独登录的包,放在Django项目下 ...

  9. Django restful

    1.restful api的规范 API与用户的通信协议,总是使用HTTPs协议. 域名  https://api.example.com                         尽量将API ...

  10. VS2013配置OPENCV2.4.9

    转载自->这里 设置opencv SDK解压目录,点击Extract后解压 我是习惯于解压到这个位置的. 解压过程如上图. 2.         文件目录介绍 解压后会在目录下生成opencv的 ...