基于领域驱动的商城的登录 1、接口的概念(在其他语言中的继承叫做实现): 2、抽象类和抽象方法 3、组合 4、依赖注入 实现:利用类创建的 5、程序设计原则: 6、企业应用设计 下面来一个简单的demo(包含依赖注入的加单的登录)
接口是派生类实现接口类而进行约束派生类的,接口中的方法不能写功能,而派生类实现了接口,就需要把接口的所有的方法都实现一遍。
接口的约束性:
其他语言接口类型 interface 只要继承(实现)接口类,那么派生类(子类)中必须有这个基类(父类)的方法 所以接口是用来约束的。 interface 接口: def f1(self): pass class lei(接口): def f1(self): print(123)
Python中没有接口类型,我们可以人为的 给类创造约束,raise 抛出异常来强制实现基类方法来做接口 用来约束
##python 接口构造 class IOrderRepository: def fettch_one_by(self,nid): """ #获取单条数据 接口类 :param nid: :return: """ raise Exception("子类中必须实现该方法") class OrderRepository(IOrderRepository): """ 继承接口这样就有了约束,这个类必须要重写接口中的方法 """ def __init__(self): pass def fettch_one_by(self,nid): pass obj = OrderRepository() obj.fettch_one_by(1)
接口类 必须实现 切不能写任何代码,其他语言写了就报错,python我们规定不写。但不会报错 class IFoo: def f1(self): """ :return: """ raise Exception("子类必须实现该方法") class Bar(IFoo): def __init__(self): pass
2、抽象类和抽象方法
1 抽象类中有普通方法,抽象方法:普通方法用来继承(普通类的特性),抽象方法用来约束(接口功能) 2 因此抽象类是普通类和接口类的整合 3 单独写抽象类没有意义,只有写了抽象方法,有了约束,子类继承时候必须实现抽象方法。 4 abc模块实现抽象类 5 如果一个抽象类全是抽象方法,则就是一个接口类了 全是普通方法,则就是一个普通类了
abc模块实现抽象类
import abc class Foo(metaclass=abc.ABCMeta): # 普通方法 用来实现继承 def f1(self): print("抽象类的普通方法") #抽象方法 @abc.abstractmethod def f2(self): """ 抽象方法 子类必须实现该方法 否则报错 :return: """ class Bar(Foo): def f2(self): print("这是子类中的抽象方法实现,不实现否则报错") obj = Bar() obj.f2()
3、组合
原始组合:我们都在init写死了,这样后期扩展性能很差
class Foo_bar: def f1(self): print("f1") class SQLHelper: def __init__(self): self.f = Foo_bar() def fetch_all(self): self.f.f1() class Service: def __init__(self,): self.s = SQLHelper() def do1(self): self.s.fetch_all() sv = Service() sv.do1()
组合升级版:将类实例化为对象传入构造方法
class Foo_bar: def f1(self): print("f1") class SQLHelper: def __init__(self,foo_bar): self.foo_bar = foo_bar def fetch_all(self): self.foo_bar.f1() class Service: def __init__(self,sqlhelper): self.sqlhelper = sqlhelper def do1(self): self.sqlhelper.fetch_all() fb = Foo_bar() sqlh = SQLHelper(fb) sv = Service(sqlh) sv.do1()
上述代码弊端,我们如果要是多个依赖,就需要手动完成传参,这样岂不是很费劲。??怎么办呢??
接下来就要将 依赖注入 但是讲解依赖注入之前我们先了解一下类是怎么创建的。
# # 依赖注入: 类创建,过程,在执行init之前执行在指定type类中的call方法中指定 class MyType(type): def __call__(cls, *args, **kwargs): obj = cls.__new__(cls,*args,**kwargs) print(obj,123124214) if type(obj) == Foo: print(11111) else: print(222222) print("========先======") # 如下两种 可以在写这个之前 做一些操作,比如判断是什么类,传入什么参数等待等。 #cls.__init__(obj,*args,**kwargs) obj.__init__(*args,**kwargs) return obj class Foo(metaclass=MyType): def __init__(self,name): print("========后======") self.name = name def f1(self): print(self.name) obj = Foo(123) obj.f1()
解释说明: 一切皆对象。 类也是type的对象。
''' 这里需要注意的:类是有type实例化的,一次类也是一个type类的对象。 而对象加括号,调用类的call方法,因此,下面的Foo(123) ,由于Foo是 type类的实例化对象,因此Foo(123)会调用type的 __call__方法。 因此 args内有我们的传参123 所以cls是一个类名, 而call方法会调用Foo方法中的__new__方法,返回object.__new__ 赋值给obj 就是下面的obj 而接下来执行Foo.__init__ '''
4、依赖注入 实现:利用类创建的
# # 实现 依赖注入 多层调用不用传参的方式 class Mapper: __mapper_dict = {} @staticmethod def register(cls,vlaues): Mapper.__mapper_dict[cls] = vlaues @staticmethod def exists(cls): if cls in Mapper.__mapper_dict: return True return False @staticmethod def get_value(cls): return Mapper.__mapper_dict[cls] class MyType(type): def __call__(cls, *args, **kwargs): obj = cls.__new__(cls,*args,**kwargs) arg_list = list(args) if Mapper.exists(cls): arg_list.append(Mapper.get_value(cls)) # 如下两种 可以在写这个之前 做一些操作,比如判断是什么类,传入什么参数等待等。 #cls.__init__(obj,*args,**kwargs) obj.__init__(*arg_list,**kwargs) return obj class Foo(metaclass=MyType): def __init__(self,name): self.name = name def f1(self): print(self.name) class Bar(metaclass=MyType): def __init__(self,name): self.name = name def f1(self): print(self.name) Mapper.register(Foo,"foo") Mapper.register(Bar,"bar") obj = Foo() obj.f1() obj = Bar() obj.f1()
那么问题来了,类默认是由 type 类实例化产生,type类中如何实现的创建类?类又是如何创建对象?
答:类中有一个属性 __metaclass__,其用来表示该类由 谁 来实例化创建,所以,我们可以为 __metaclass__ 设置一个type类的派生类,从而查看 类 创建的过程
5、程序设计原则:
# 单一责任原则SRP # 一个对象只应该为一个元素服务: 比如你的数据库增删改查难道要放到多个类? # 开放封闭原则:OCP # 对于 装饰器,对内是封闭的,对外可以扩展 # 对于一个源码方法比如str类,它的源码我们尽量不改,我们用setattr(str,"k1",函数名) 这就实现了对外扩展该类 #其他语言叫extent # 里氏替换原则:LSP # 可以用任何派生类替换基类 # def (int i): # 可以是任何int的派生类 #print(i) #其他语言必须制定类型 可以是这个类型的任何派生类 #接口分离原则ISP #对于接口进行分类,避免一个方法过多 # 比如对于两个接口类 : 猫类(方法:喵喵叫 爬树) 狗类(方法:汪汪叫 摇尾巴) 这样我们继承接口类,进行约束只需要实现对应的就行。 #而如果这几个方法都写在一个接口类,那我们不需要用的方法也需要在派生类中实现。 # 依赖倒置原则 DIP #http://blog.csdn.net/zhengzhb/article/details/7289269 """ 定义:高层模块不应该依赖低层模块,二者都应该依赖其抽象;抽象不应该依赖细节;细节应该依赖抽象。 问题由来:类A直接依赖类B,假如要将类A改为依赖类C,则必须通过修改类A的代码来达成。这种场景下,类A一般是高层模块,负责复杂的业务逻辑;类B和类C是低层模块,负责基本的原子操作;假如修改类A,会给程序带来不必要的风险。 解决方案:将类A修改为依赖接口I,类B和类C各自实现接口I,类A通过接口I间接与类B或者类C发生联系,则会大大降低修改类A的几率。 依赖倒置原则基于这样一个事实:相对于细节的多变性,抽象的东西要稳定的多。以抽象为基础搭建起来的架构比以细节为基础搭建起来的架构要稳定的多。在java中,抽象指的是接口或者抽象类,细节就是具体的实现类,使用接口或者抽象类的目的是制定好规范和契约,而不去涉及任何具体的操作,把展现细节的任务交给他们的实现类去完成。 """ # 依赖注入和控制翻转 # 上面已经搞了
6、企业应用设计
企业设计程序 分四层 #1 数据访问层 # DAO 一个表一个类 # repository 仓库 # 工作单元 增删改 用来做事务的 # 三个列表: # 查询对象 格式化创建sql #2 业务处理层 #a 业务逻辑组织 # 事务脚本 一个功能一个函数 经常用的 # 活动记录: 请求来, 在业务逻辑时候创建类,让业务的类跟数据库的的表也一致,根据数据库表字段来设计业务类, 直接对象交给orm。 # 适用于 博客和新闻系统 # # 领域模型 # 建模: 类的属性和行为 解决某件事就是领域。加起来就是领域模型 # 反模式 #不能有行为 也即是 类方法 # DDD : #根据事情驱动着你解决 # b 设计模式 # 工厂 策略 单例 #3 服务层: # 定义请求和响应的模式:各有规则 #4 表示层 UI
下面来一个简单的demo(包含依赖注入的加单的登录)
1访问login.html
<!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> <meta name="viewport" content="width=device-width" /> <meta http-equiv="X-UA-Compatible" content="IE=8" /> <title>购物商城</title> <link href="/statics/Admin/Css/common.css" rel="stylesheet" /> <link href="/statics/Admin/Css/account.css" rel="stylesheet" /> </head> <body> <div class="account-container bg mt10"> <div class='header clearfix'> <div> <a href="/home/index"> <img src="/statics/Admin/Images/mll_logo.gif"> </a> </div> </div> </div> <div class='account-container mt30'> <div class='body clearfix pd10' style='position: relative;'> <div class='logo left'> <img style='height:350px;' src="/statics/Admin/Images/login_logo.png" /> </div> <div class='login left mt30'> <form id='Form' action='/login' method='POST'> <div class='group mt10'> <label class='tip'><span class="red">*</span>用户名:</label> <input type='text' require='true' label='用户名' Field='string' range='4-40' name='username' /> <i class='i-name'></i> </div> <div class='group'> <label class='tip'><span class="red">*</span>密码:</label> <input type='password' require='true' label='密码' min-len='6' name='password' /> <i class='i-pwd'></i> </div> <div class='group'> <label class='tip'><span class="red">*</span>验证码:</label> <input type='text' require='true' label='验证码' style='80px;' name='checkcode' /> <a style='125px;display:inline-block;'><img class='checkcode' onclick='ChangeCode();' id='imgCode' src='/check' /></a> </div> <div class='group font12 mb0'> <label class='tip'></label> <label style='246px;display: inline-block;'> <input id='protocol' name='protocol' type='checkbox' checked='checked' /> <span>自动登录</span> <span class='ml10'><a href='#'>忘记密码?</a></span> </label> </div> <div class='group mt0'> <label class='tip'></label> <input type='submit' class='submit' value='登 录' /> </div> </form> <div class='go-register'><a href='#'>免费注册 >> </a></div> </div> </div> </div> <div class='account-container mt20' style='text-align:center;color:#555;'> © 2004-2015 www.autohome.com.cn All Rights Reserved. xxxxx 版权所有 </div> <script src="/statics/Admin/js/jquery-1.8.2.min.js"></script> <script src="/statics/Admin/js/treebiao.js"></script> <script type="text/javascript"> $(function(){ $.login('#Form',''); }); function ChangeCode() { var code = document.getElementById('imgCode'); code.src += '?'; } </script> </body> </html>
2、获取前端输入的数据并在后台接收
3、service = UserService()
#!usr/bin/env/python # -*- coding:utf-8 -*- #1、模型 #模型--把数据库取出的数据数字 转换成对应的汉字 class UserModel: def __init__(self, nid, username, email, last_login, vip_obj, user_type_obj): self.nid = nid self.username = username self.email = email self.last_login = last_login self.vip_obj = vip_obj # {vip:1,金牌会员} self.user_type_obj = user_type_obj # {user_type:1,普通} class UserTypeModel: USER_TYPE = ( {'nid': 1, 'caption': '用户'}, {'nid': 2, 'caption': '商户'}, {'nid': 3, 'caption': '管理员'}, ) def __init__(self, nid): self.nid = nid # @property # def caption(self): # for item in UserTypeModel.USER_TYPE: # if item['nid'] == self.nid: # return item['caption'] def get_caption(self): for item in UserTypeModel.USER_TYPE: if item['nid'] == self.nid: return item['caption'] caption = property(fget=get_caption) class VipModel: VIP_TYPE = ( {'nid': 1, 'caption': '铜牌'}, {'nid': 2, 'caption': '银牌'}, {'nid': 3, 'caption': '金牌'}, {'nid': 4, 'caption': '铂金'}, ) def __init__(self, nid): self.nid = nid def caption(self): for item in VipModel.VIP_TYPE: if item['nid'] == self.nid: return item['caption'] #2、接口 class IUSerRepository: #根据用户名密码获取对象 def fetch_one_by_user_pwd(self,user,pwd): '''''' # 根据邮箱密码获取对象 def fetch_one_by_email_pwd(self,email,pwd): '''''' #3、协调 依赖数据仓库 # 降低耦合 以组合的形式传进来 from Infrastructure.DI.Meta import DIMetaClass class UserService(metaclass=DIMetaClass): # 注入 找到 mapper 找到mapper中的依赖关系 def __init__(self, user_repository): # user_repository 是数据仓库的对象 self.userRepository = user_repository def check_login(self, user, email, pwd): if user: # m 是 UserModel 的对象 或者 None m = self.userRepository.fetch_one_by_user_pwd(user, pwd) # print(m.username,'+++++++++++++++++++++++++') else: m = self.userRepository.fetch_one_by_email_pwd(email, pwd) # 将 m 字典转换成对象 return m def get_user_by_page(self,start, rows): count = self.userRepository.fetch_account_count() account_list = self.userRepository.fetch_user_by_page(start, rows) print(count,account_list,'---------------------------------') return count, account_list def get_user_detail_by_nid(self, account_nid): result = self.userRepository.fetch_user_detail_by_nid(account_nid) return result def get_user_to_select(self): user_list = self.userRepository.fetch_user() return user_list
4、check_login(..........)
执行check_login(..........) 去数据库中差到库中的用户名密码
def check_login(self, user, email, pwd): if user: # m 是 UserModel 的对象 或者 None m = self.userRepository.fetch_one_by_user_pwd(user, pwd) # print(m.username,'+++++++++++++++++++++++++') else: m = self.userRepository.fetch_one_by_email_pwd(email, pwd) # 将 m 字典转换成对象 return m
#!usr/bin/env/python # -*- coding:utf-8 -*- # 4、实现业务接口 具体的SQL语句 from Model.User import IUSerRepository# from Repository.DbConnection import DbConnection from Model.User import UserModel from Model.User import VipModel from Model.User import UserTypeModel #继承--接口 为了做约束用 class UserRepository(IUSerRepository): def __init__(self): #连接数据库 self.db_conn = DbConnection() def fetch_one_by_user_pwd(self,user,pwd): #自己拼接 SQL 语句 SQL注入 #连接数据库 cursor = self.db_conn.connect() #数据库中的SQL查询 sql = '''select nid,username, password, last_login,vip,user_type,email from userinfo where username=%s and password=%s''' #执行查询 cursor.execute(sql,(user,pwd)) #如果匹配成功 结果放到 db_result里 db_result = cursor.fetchone()#没有的话就是None 字典 #关闭查询 self.db_conn.close() #将字典直接转换成对象 if db_result: obj = UserModel( nid=db_result['nid'], username=db_result['username'], email=db_result['email'], last_login=db_result['last_login'], # 希望vip_obj是一个对象 vip_obj=VipModel(nid=db_result['vip']), # user_type_obj=UserTypeModel(nid=db_result['user_type']), ) return obj return db_result def fetch_one_by_email_pwd(self,email,pwd): pass
5、将check_login()执行的结果转成对象直接返回
这样一个完整的登录请求就算完成了