[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

原文地址:https://www.cnblogs.com/CheeseZH/p/9471431.html

时间: 2024-11-14 19:10:03

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

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

注:关乎对象的创建方式的设计模式就是"创建型设计模式"(creational design pattern) 1.1 抽象工厂模式 "抽象工厂模式"(Abstract Factory Pattern)用来创建复杂的对象,这种对象由许多小对象组成,而这些小对象都属于某个特定的"系列"(family). 比如说,在GUI 系统里可以设计"抽象控件工厂"(abstract widget factory),并设计三个"具体子

设计模式笔记10: 抽象工厂模式

1.1 定义 提供一个创建一系列相关或相互依赖对象的接口,而无需指定他们具体的类. 1.2 类图 1.3 代码实现 1 using System; 2 using System.Collections.Generic; 3 using System.Configuration; 4 using System.Linq; 5 using System.Reflection; 6 using System.Text; 7 using System.Threading.Tasks; 8 9 names

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

一.UML图 二.概念 抽象方法模式(Abstract Factory):提供一个创建一系列相关或互相依赖对象的接口,而无需指定他们详细的类. 三.包括的角色 (1)抽象工厂 (2)详细工厂:包含详细工厂1和详细工厂2.详细工厂1用于生产详细产品A1和详细产品B1,详细工厂2用于生产详细产品A2和详细产品B2: (3)抽象产品:包含抽象产品A和抽象产品B. (4)详细产品:包含抽象产品A所相应的详细产品A1和A2.以及抽象产品B所相应的详细产品B1和B2. 说明:在<大话设计模式>中,上述的1

第15章 就不能换DB吗?—抽象工厂模式

由于抽象工厂在我们编程当中经常使用和常见,所有本篇文章对<大话设计模式>中的15章做了很详细的比较.通过一个Dao层可以更换访问任意数据库的例子来学习抽象工厂模式.例如:Dao层可以访问Sqlserver数据库,也可以访问Access数据库,当程序新增访问Oracle数据库时,无需修改现有代码,只需要添加访问Oracle相关的类就可以,实现了开闭原则.本篇文章的例子中每种数据库上都有User和Department表,我们Dao层对这两个表进行查询和插入操作. 最基本数据库访问 一下是访问Sql

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

本系列文章是希望将软件项目中最常见的设计模式用通俗易懂的语言来讲解清楚,并通过Python来实现,每个设计模式都是围绕如下三个问题: 为什么?即为什么要使用这个设计模式,在使用这个模式之前存在什么样的问题? 是什么?通过Python语言来去实现这个设计模式,用于解决为什么中提到的问题. 怎么用?理解了为什么我们也就基本了解了什么情况下使用这个模式,不过在这里还是会细化使用场景,阐述模式的局限和优缺点. 这次的主角是简单工厂,工厂方法和抽象工厂模式,由于这几个模式联系紧密,有一定的相似性,所以放在

设计模式(15)-----抽象工厂模式

抽象工厂模式(abstract factory) 定义 提供一个创建一系列相关或相互依赖对象的接口,而无需指定他们具体的类. UML图 例子 在开发的过程难免会遇到更换数据库的事情,也就意味着会出现大量的代码修改.而使用抽象工厂模式,可以很好的避免修改的行为.现在有两个操作对象,一个是用户,一个是部门,都具有插入和查询的功能. POJO package com.csdhsm.pattemdesign.abstractfactory; /** * @Title: User.java * @Desc

第3章 抽象工厂模式(Abstract Factory)

原文 第3章 抽象工厂模式(Abstract Factory) 场景我们的系统要同时支持两个数据库  SqlServer 跟Oracle数据库  并且不同的环境要进行随时切换. 看下面的代码: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54

C#设计模式之四抽象工厂模式(AbstractFactory)【创建型】

一.引言 写了3篇有关设计模式的文章了,大家有了些反馈,说能从中学到一些东西,我感到很欣慰,那就继续努力.今天我要写第四个模式了,该模式叫抽象工厂.上一篇文章我们讲了[工厂方法]模式,它是为了解决[简单工厂]模式所面对的问题,它的问题就是:如果我们增加新的产品,工厂类的方法就要修改本身的代码,增加产品越多,其逻辑越复杂,同时这样的修改也是不符合[开放关闭原则OCP],对修改代码关闭,对增加代码开放.为了解决[简单工厂]的问题,我们引出了[工厂方法]模式,通过子类化工厂类,解决了工厂类责任的划分,

C#设计模式之三抽象工厂模式(AbstractFactory)【创建型】

原文:C#设计模式之三抽象工厂模式(AbstractFactory)[创建型] 一.引言 写了3篇有关设计模式的文章了,大家有了些反馈,说能从中学到一些东西,我感到很欣慰,那就继续努力.今天我要写第四个模式了,该模式叫抽象工厂.上一篇文章我们讲了[工厂方法]模式,它是为了解决[简单工厂]模式所面对的问题,它的问题就是:如果我们增加新的产品,工厂类的方法就要修改本身的代码,增加产品越多,其逻辑越复杂,同时这样的修改也是不符合[开放关闭原则OCP],对修改代码关闭,对增加代码开放.为了解决[简单工厂