一、预备知识:

1、接口:

  - URL形式

  - 数据类型 (Python中不存在)

a.类中的方法可以写任意个,想要对类中的方法进行约束就可以使用接口;

b.定义一个接口,接口中定义一个方法f1;

c.只要继承(实现)了接口,那么类就会受约束,该类必须要有f1方法!

d.接口只用来做约束,不需要写具体功能。

由于python中无接口类型,但是可以人为构造,抛出异常!

raise Exception('子类中必须实现该方法')

class IOrderRepository:
def fetch_one_by(self,nid):
'''
获取单条数据的方法,所有继承当前类的类必须实现(有)该方法
:param nid:
:return:
'''
raise Exception('子类中必须实现该方法') class OrderRepository(IOrderRepository): def fetch_one_by(self,nid):
print('...获取数据') obj = OrderRepository
obj.fetch_one_by(1)

demo

类:类中的方法可以写功能,也可以不写功能,

抽象类:类和接口的集合,抽象类中可以有抽象方法和普通方法,

子类继承抽象类时,必须实现抽象方法!

抽象类即做了约束,还有具体功能

python提供了模块abc,用于实现抽象类和抽象方法的功能

import abc

class Foo(metaclass=abc.ABCMeta):

    def f1(self):
print(123) def f2(self):
print(456) @abc.abstractmethod
def f3(self):
''' :return:
''' class Bar(Foo):
def f1(self):
pass def f3(self):
pass b = Bar()
b.f1()

demo


耦合:

不同模块之间互连程度的度量(耦合性也叫块间联系)。指软件系统中各模块间相互联系紧密程度的一种度量。模块之间联系越紧密,其耦合性就越强,模块的独立性则越差。

class SqlHelper:
def fetch_one(self):
pass
def fetch_all(self):
pass 方式一:
class UserInfo:
def __init__(self):
pass def login(self):
s = SqlHelper()
s.fetch_one() def logout(self):
s = SqlHelper()
s.fetch_one() def register(self):
s = SqlHelper()
s.fetch_one() *************************************************
方式二:
class UserInfo:
def __init__(self,helper):
self.s = SqlHelper() def login(self):
self.s.fetch_one() def logout(self):
self.s.fetch_one() def register(self):
self.s.fetch_one() obj = UserInfo()
obj.login()

强耦合demo

为了降低耦合,一般可以这样做:

class SqlHelper:
def fetch_one(self):
pass
def fetch_all(self):
pass class UserInfo:
def __init__(self,helper):
self.s = helper def login(self):
self.s.fetch_one() h = SqlHelper()
obj = UserInfo(h)
obj.login()

demo

如果SqlHelper和其他类,如Foo类有依赖关系那么又要解耦了:

class Foo:
def f1(self):
pass class SqlHelper:
def __init__(self,foo):
self.f = foo def fetch_one(self):
self.f.f1() class UserInfo:
def __init__(self,helper):
self.s = helper def login(self):
self.s.fetch_one() f = Foo()
s = SqlHelper(f)
u = UserInfo(s)
u.login()

demo

最终发现代码,嵌套太多层了。

为了解决这种问题,可以使用依赖注入!

提案:实例化的时候,不需要自己传参数


面向对象知识点:

class Foo:
def __init__(self,name):
self.name = name def f1(self):
print(self.name)

# 解释器解释的时候:

1.遇到 class Foo , 执行type 的__init__方法

2.Typr的init方法里做什么呢? => 创建了类

obj = Foo(123)

3.执行Type的__call__方法,

      -> 执行Foo类的__new__方法

      -> 执行Foo类的__init__方法

如何在执行__init__方法前,传递参数呢?

可以选择去重写Type的__call__方法

你写的任何类都是Type的对象,

class MyType(type):
def __call__(self, *args, **kwargs):
print(self) class Foo(metaclass=MyType): # 指定由MyType生成
def __init__(self,name):
self.name = name obj = Foo(123)
# <class '__main__.Foo'>

最终执行流程:

class MyType(type):

    def __call__(cls, *args, **kwargs):
obj = cls.__new__(cls,*args, **kwargs) # 创建对象
obj.__init__(*args, **kwargs)      # 初始化
return obj class Foo(metaclass=MyType):
def __init__(self,name):
self.name = name def f1(self):
print(self.name) obj = Foo(123)

为了在调用时不传参数,设法在obj.__init__()执行前时,传入参数

class MyType(type):

    def __call__(cls, *args, **kwargs):
obj = cls.__new__(cls,*args, **kwargs)
args_list = list(args)
args_list.append(123)
obj.__init__(*args_list, **kwargs)
return obj obj = Foo()

为了不让参数固定,做进一步修改,利用第三方来传递参数!

依赖注入:

class Mapper:

    __mapper_relation = {}

    @staticmethod
def register(cls,value):
Mapper.__mapper_relation[cls] = value @staticmethod
def exist(cls):
if cls in Mapper.__mapper_relation:
return True
else:
return False @staticmethod
def value(cls):
return Mapper.__mapper_relation[cls] class MyType(type): def __call__(cls, *args, **kwargs):
obj = cls.__new__(cls,*args, **kwargs)
args_list = list(args)
if Mapper.exist(cls):
value = Mapper.value(cls)
args_list.append(value)
obj.__init__(*args_list, **kwargs)
return obj class Foo(metaclass=MyType):
def __init__(self,name):
self.name = name def f1(self):
print(self.name) Mapper.register(Foo,'') obj = Foo()
obj.f1()

demo

有了依赖注入后,之前嵌套过多的demo可以修改为:

#!/usr/bin/env python
# -*-coding:utf-8 -*-
class Mapper: __mapper_relation = {} @staticmethod
def register(cls,value):
Mapper.__mapper_relation[cls] = value @staticmethod
def exist(cls):
if cls in Mapper.__mapper_relation:
return True
else:
return False @staticmethod
def value(cls):
return Mapper.__mapper_relation[cls] class MyType(type): def __call__(cls, *args, **kwargs):
obj = cls.__new__(cls,*args, **kwargs)
args_list = list(args)
if Mapper.exist(cls):
value = Mapper.value(cls)
args_list.append(value)
obj.__init__(*args_list, **kwargs)
return obj class Test():
def __int__(self):
pass class Foo(metaclass=MyType):
def __init__(self,t):
self.t = t def f1(self):
print(self.name) class Bar(metaclass=MyType):
def __init__(self,f):
self.f = f def f1(self):
print(self.name) Mapper.register(Foo,Test())
Mapper.register(Bar,Foo()) b = Bar()
print(b)
print(b.f)
print(b.f.t) # <__main__.Bar object at 0x0000002D504C4550>
# <__main__.Foo object at 0x0000002D504C4518>
# <__main__.Test object at 0x0000002D504C4470>

1.总体框架目录结构:

备注:

  • Infrastructure:一些公共组件,例如md5加密,分页模块,session等。
  • Model :关于数据库的逻辑处理模块
  • Repository :数据访问层,包含数据库的增删改查
  • Service :服务层,调用Model,包含带有规则的请求和返回
  • Statics:静态文件目录
  • UI层:业务处理
  • Views:模板文件
  • Application:tornado程序的起始文件
  • Config:配置文件
  • Mapper:依赖注入文件,负责整个框架不同类的依赖注入

2.首先我们从Moldel开始查看:

本文主要以用户管理为例进行介绍,因此我们来关注一下User.py文件:

代码结构:

下面对上述代码结构做一一介绍:

IUseRepository类:接口类,用于约束数据库访问类的方法
示例代码:

class IUseRepository:
"""
用户信息仓库接口
""" def fetch_one_by_user_pwd(self, username, password):
"""
根据用户名密码获取模型对象
:param username: 主键ID
:param password: 主键ID
:return:
"""
def fetch_one_by_email_pwd(self, email, password):
"""
根据邮箱密码获取模型对象
:param email: 主键ID
:param password: 主键ID
:return:
""" def update_last_login_by_nid(self,nid,current_date):
"""
根据ID更新最新登陆时间
:param nid:
:return:
"""

  从上述代码可以看出,数据库访问类如果继承IUseRepository类,就必须实现其中的抽象方法。

接下来的三个类,VipType、UserType、User是与用户信息相关的类,是数据库需要保存的数据,

我们希望存入数据库的数据格式为:nid 、username、email、last_login、user_type_id、vip_type_id,其中User类用于保存上述数据。

因为user_type_id、vip_type_id存的是数字,即user_type_id、vip_type_id是外键,不能直接在前端进行展示,

因此,我们创建了VipType、UserType类,用于根据id,获取对应的VIP级别和用户类型。

class User:
"""领域模型"""
def __init__(self, nid, username, email, last_login, user_type, vip_type):
self.nid = nid
self.username = username
self.email = email
self.last_login = last_login
self.user_type = user_type
self.vip_type = vip_type

User类

class UserType:

    USER_TYPE = (
{'nid': 1, 'caption': '用户'},
{'nid': 2, 'caption': '商户'},
{'nid': 3, 'caption': '管理员'},
) def __init__(self, nid):
self.nid = nid def get_caption(self):
caption = None for item in UserType.USER_TYPE:
if item['nid'] == self.nid:
caption = item['caption']
break
return caption caption = property(get_caption)

UserType类

class VipType:

    VIP_TYPE = (
{'nid': 1, 'caption': '铜牌'},
{'nid': 2, 'caption': '银牌'},
{'nid': 3, 'caption': '金牌'},
{'nid': 4, 'caption': '铂金'},
) def __init__(self, nid):
self.nid = nid def get_caption(self):
caption = None for item in VipType.VIP_TYPE:
if item['nid'] == self.nid:
caption = item['caption']
break
return caption caption = property(get_caption)

VipType类

注:VipType、UserType这两个类获取对应的caption均是通过类的普通特性访问,即类似字段方式访问。

接下来的类UserService是本py文件的重中之重,它负责调用对应的数据库访问类的方法,并被服务层service调用,具有承上启下的作用:

示例代码:

class UserService:

    def __init__(self, user_repository):
self.userRepository = user_repository def check_login(self, username=None, email=None, password=None): if username:
user_model = self.userRepository.fetch_one_by_user_pwd(username, password)
else:
user_model = self.userRepository.fetch_one_by_email_pwd(email, password)
if user_model:
current_date = datetime.datetime.now()
self.userRepository.update_last_login_by_nid(user_model.nid, current_date)
return user_model

  这里,我们重点介绍一下上述代码:

初始化参数user_repository:通过依赖注入对应的数据库访问类的对象;

check_login:访问数据库的关键逻辑处理方法,根据用户是用户名+密码方式还是邮箱加密码的方式登录,然后调用对应的数据库处理方法,如果登陆成功,更新时间和最后登录时间,最后将User类的实例返回给调用它的服务层service。(详细见下文数据库处理类的方法)

我们先来看一下对应的数据库处理类中的一个方法:

def fetch_one_by_user_pwd(self, username, password):
ret = None
cursor = self.db_conn.connect()
sql = """select nid,username,email,last_login,vip,user_type from UserInfo where username=%s and password=%s"""
cursor.execute(sql, (username, password))
db_result = cursor.fetchone()
self.db_conn.close() if db_result:
ret = User(nid=db_result['nid'],
username=db_result['username'],
email=db_result['email'],
last_login=db_result['last_login'],
user_type=UserType(nid=db_result['user_type']),
vip_type=VipType(nid=db_result['vip'])
)
return ret

  这里我们使用pymysql进行数据库操作,以用户名+密码登陆为例,如果数据库有对应的用户名和密码,将查询结果放在User类中进行初始化,至此,ret中封装了用户的全部基本信息,将ret返回给上面的check_login方法,即对应上文中的返回值user_model,user_model返回给调用它的服务层service。

总结:Molde最终将封装了用户基本信息的User类的实例返回给服务层service。

3.接下来我们看一下服务层service:

service也是一个承上启下的作用,它调用Moldel文件对应的数据库业务协调方法,并被对应的UI层调用(本例中是UIadmin)。

目录结构:

同样的,我们只介绍User文件夹:它包含4个py文件:

  • ModelView:用于用户前端展示的数据格式化,重点对user_type_id、vip_type_id增加对应的用户类型和VIP级别,即将外键数据对应的caption放在外键后面,作为增加的参数。
  • Request:封装请求Moldel文件对应数据库协调类的参数
  • Response:封装需要返回UI层的参数
  • Sevice:核心服务处理文件

下面对上述目录做详细代码:

ModelView:

class UserModelView:

    def __init__(self, nid, username, email, last_login, user_type_id, user_type_caption, vip_type_id, vip_type_caption):
self.nid = nid
self.username = username
self.email = email
self.last_login = last_login
self.user_type = user_type_id
self.user_type_caption = user_type_caption
self.vip_type = vip_type_id
self.vip_type_caption = vip_type_caption

  注:对user_type_id、vip_type_id增加对应的用户类型和VIP级别,即将外键数据对应的caption放在外键后面,作为增加的参数。

Request:

class UserRequest:

    def __init__(self, username, email, password):
self.username = username
self.email = email
self.password = password

Response:

class UserResponse:

    def __init__(self, status=True, message='', model_view=None):
self.status = status # 是否登陆成功的状态
self.message = message #错误信息
self.modelView = model_view #登陆成功后的用户数据 

UserService:

class UserService:

    def __init__(self, model_user_service):   #通过依赖注入Moldel对应的数据库业务协调方法,此例中对应上文中的user_service
self.modelUserService = model_user_service def check_login(self, user_request): #核心服务层业务处理方法
response = UserResponse() #实例化返回类 try:
model = self.modelUserService.check_login(user_request.username, user_request.email, user_request.password) #接收上文中的用户基本信息,是User类的实例
if not model:
raise Exception('用户名或密码错误')
else: #如果登陆成功,通过UserModelView类格式化返回前端的数据
model_view = UserModelView(nid=model['nid'],
username=model['usename'],
email=model['email'],
last_login=model['last_login'],
user_type_id=model['user_type'].nid,
user_type_caption=model['user_type'].caption,
vip_type_id=model['vip_type'].nid,
vip_type_caption=model['vip_type'].caption,)
response.modelView = model_view #定义返回UI层的用户信息
except Exception as e:
response.status = False
response.message = str(e)

  总结:至此,Service返回给Ui层的数据是是否登陆成功的状态status、错误信息、已经格式化的用户基本信息。

4.UI层

UI层主要负责业务处理完成后与前端的交互,它是服务层Service的调用者:

示例代码:

class Login(AdminRequestHandler):

    def get(self, *args, **kwargs):
is_login=self.session["is_login"]
current_user=""
if is_login:
current_user=self.session["user_info"].username
self.render('Account/Login.html',current_user=current_user) def post(self, *args, **kwargs):
user_request=[]
#获取用户输入的用户名邮箱密码
user_request.append(self.get_argument('name',None))
user_request.append(self.get_argument('email',None))
user_request.append(self.get_argument('pwd',None))
checkcode=self.get_argument("checkcode",None)
Mapper.mapper(*user_request)
obj=UserService.check_login()
self.session["is_login"]=True
self.session["user_info"]=obj.modelView
self.write("已登陆")

  

【Python之路】特别篇--基于领域驱动模型架构设计的京东用户管理后台的更多相关文章

  1. 【tornado】系列项目(一)之基于领域驱动模型架构设计的京东用户管理后台

    本博文将一步步揭秘京东等大型网站的领域驱动模型,致力于让读者完全掌握这种网络架构中的“高富帅”. 一.预备知识: 1.接口: python中并没有类似java等其它语言中的接口类型,但是python中 ...

  2. 【tornado】系列项目(二)基于领域驱动模型的区域后台管理+前端easyui实现

    本项目是一个系列项目,最终的目的是开发出一个类似京东商城的网站.本文主要介绍后台管理中的区域管理,以及前端基于easyui插件的使用.本次增删改查因数据量少,因此采用模态对话框方式进行,关于数据量大采 ...

  3. 基于领域驱动设计(DDD)超轻量级快速开发架构(二)动态linq查询的实现方式

    -之动态查询,查询逻辑封装复用 基于领域驱动设计(DDD)超轻量级快速开发架构详细介绍请看 https://www.cnblogs.com/neozhu/p/13174234.html 需求 配合Ea ...

  4. 领域驱动模型DDD(二)——领域事件的订阅/发布实践

    前言 凭良心来说,<微服务架构设计模式>此书什么都好,就是选用的业务过于庞大而导致代码连贯性太差,我作为读者来说对于其中采用的自研框架看起来味同嚼蜡,需要花费的学习成本实在是过于庞大,不仅 ...

  5. Java开发架构篇:领域驱动设计架构基于SpringCloud搭建微服务

    作者:小傅哥 博客:https://bugstack.cn 沉淀.分享.成长,让自己和他人都能有所收获! 一.前言介绍 微服务不是泥球小单体,而是具备更加清晰职责边界的完整一体的业务功能服务.领域驱动 ...

  6. python之路入门篇

    一. Python介绍 python的创始人为吉多·范罗苏姆(Guido van Rossum).1989年的圣诞节期间,Guido开始写能够解释Python语言语法的解释器.Python这个名字,来 ...

  7. 基于领域驱动设计(DDD)超轻量级快速开发架构

    smartadmin.core.urf 这个项目是基于asp.net core 3.1(最新)基础上参照领域驱动设计(DDD)的理念,并参考目前最为了流行的abp架构开发的一套轻量级的快速开发web ...

  8. 领域驱动模型DDD(一)——服务拆分策略

    前言 领域驱动模型设计在业界也喊了几年口号了,但是对于很多"务实"的程序员来说,纸上谈"术"远比敲代码难得太多太多.本人能力有限,在拜读相关作品时既要隐忍书中晦 ...

  9. [uboot] (番外篇)uboot 驱动模型(转)重要

    [uboot] uboot流程系列:[project X] tiny210(s5pv210)上电启动流程(BL0-BL2)[project X] tiny210(s5pv210)从存储设备加载代码到D ...

随机推荐

  1. HTNL基础之二

    HTML实体字符 “<”:< “>”:> “空格”: ' / / '  “"”:"  “®”:®  “©”:© 列表 ①无序列表:列表用来在网页上组织信息, ...

  2. 基于TCP 协议的socket 简单通信

    DNS 服务器:域名解析 socket 套接字 : ​ socket 是处于应用层与传输层之间的抽象层,也是一组操作起来非常简单的接口(接受数据),此接口接受数据之后,交由操作系统 为什么存在 soc ...

  3. [Tarjan系列] Tarjan算法求无向图的双连通分量

    这篇介绍如何用Tarjan算法求Double Connected Component,即双连通分量. 双联通分量包括点双连通分量v-DCC和边连通分量e-DCC. 若一张无向连通图不存在割点,则称它为 ...

  4. hdu 6047

    题解:先对b排序,用一个数组预处理a,记录当前位置之后到n的最大值,然后在用一个变量维护新增变量的最大值,用的时候和前面的数组的最大值做一个比较就ok. AC代码: #include <cstd ...

  5. 复习二叉数 pat l2-006 数的遍历

    L2-006. 树的遍历   给定一棵二叉树的后序遍历和中序遍历,请你输出其层序遍历的序列.这里假设键值都是互不相等的正整数. 输入格式: 输入第一行给出一个正整数N(<=30),是二叉树中结点 ...

  6. Entity的约束

    在DBContext的OnModelCreating()方法中调用上面的那个类 1.Infrastruture的Database文件夹建立Entityconfiguretions的文件夹 2.MyCo ...

  7. 设计模式 -- MVC

    MVC 在Web中应用是常见的了,成为基础应用模式. 不好的用法是把业务写在C 中,M只是失血模型. 应该要重M 轻C,业务写在M中,但是这样有问题了.View 会引用Model,那么View会看到M ...

  8. IE各版本处理XML的方式

    一.支持DOM2级的方式我们知道,现阶段支持DOM2的主流浏览器有IE9+.Firefox.Opera.Chrome和Safari.1.1.创建XML//实际上,DOM2级在document.impl ...

  9. SPOJ-MobileService--线性DP

    题目链接 https://www.luogu.org/problemnew/show/SP703 方法一 分析 很显然可以用一个四维的状态\(f[n][a][b][c]​\)表示完成第i个任务时且三人 ...

  10. 集合源码阅读——ArrayList

    ArrayList 关键点: >>扩容每次扩容1.5倍 >>modcount的作用 >>ArrayList的父类AbstractList的成员变量 >> ...