博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
[Python设计模式] 第15章 如何兼容各种DB——抽象工厂模式
阅读量:4662 次
发布时间:2019-06-09

本文共 5954 字,大约阅读时间需要 19 分钟。

github地址:

题目

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

基础版本

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, abstractmethodclass 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 sysdef createInstance(module_name, class_name, *args, **kwargs):    class_meta = getattr(module_name, class_name)    obj = class_meta(*args, **kwargs)    return objdef 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

转载于:https://www.cnblogs.com/CheeseZH/p/9471431.html

你可能感兴趣的文章
highcharts的设置
查看>>
listview item 动画
查看>>
java哈希表(线性探测哈希表。链式哈希表)
查看>>
模板——倍增LCA
查看>>
第二阶段团队项目冲刺第一天
查看>>
nodejs网页请求data事件返回字符串
查看>>
keil uvision4不能显示中文
查看>>
SubSonic3.0使用外连接查询时查询不出数据的问题修改
查看>>
spring MVC 入门:
查看>>
【转】Java 面试题问与答:编译时与运行时
查看>>
windows启动过程
查看>>
刷面经笔记2019.02.14
查看>>
C# string.Format 与+性能比较
查看>>
设计模式培训之二:简单工厂、工厂方法
查看>>
C语言正整数除法向上取整
查看>>
酒店之王——网络流——dinic
查看>>
Windows7单机部署Hbase
查看>>
理解iOS Event Handling
查看>>
CreateCompatibleDC与BitBlt 学习
查看>>
十、HQL查询
查看>>