在之前tornado商城项目中,在开始之前需要引入一些项目设计知识,如接口,抽象方法抽象类,组合,程序设计原则等,个人理解项目的合理设计可增加其灵活性,
降低数据之间的耦合性,提高稳定性,下面介绍一些预备知识
1、接口
其实py中没有接口这个概念。要想实现接口的功能,可以通过主动抛出异常来实现
接口作用:对派生类起到限制的作用
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
接口,python中的接口,通过在父类中主动抛出异常实现
接口的作用:起到了限制的作用
"""
class IFoo:
def fun1(self):
pass
raise Exception("错误提示")
class Bar(IFoo):
def fun1(self):
#方法名必须和父类中的方法名相同,不然没办法正常执行,会抛出异常
print("子类中如果想要调用父类中的方法,子类中必须要有父类中的方法名")
def fun2(self):
print("test")
obj = Bar()
obj.fun2()
2.抽象方法抽象类
抽象类,抽象方法是普通类和接口的综合,即可以继承也可以起到限制作用
由于python 本身没有抽象类、接口的概念,所以要实现这种功能得abc.py 这个类库,
具体实现方法如下 :
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
抽象类,抽象方法
抽象类,抽象方法是普通类和接口的综合,即可以继承也可以起到限制作用
"""
import abc
class Foo(metaclass=abc.ABCMeta):
def fun1(self):
print("fun1")
def fun2(self):
print("fun2")
@abc.abstractclassmethod
def fun3(self):
pass
class Bar(Foo):
def fun3(self):
print("子类必须有父类的抽象方法名,不然会抛出异常")
obj = Bar()
obj.fun1()
obj.fun2()
obj.fun3()
3.组合
python中“多用组合少用继承”,因为继承的偶合性太强,可以把基类,当做参数传入派生类中,用于解偶
§继承
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#继承
class Animals:
def eat(self):
print(self.Name + " eat")
def drink(self):
print(self.Name + " drink")
class Person(Animals):
def __init__(self, name):
self.Name = name
def think(self):
print(self.Name + " think")
obj = Person("user1")
obj.drink()
obj.eat()
obj.think()
§组合
class Animals:
def __init__(self,name):
self.Name = name
def eat(self):
print(self.Name + " eat")
def drink(self):
print(self.Name + " drink")
class Person:
def __init__(self, obj):
self.obj = obj
def eat(self):
self.obj.eat()
def think(self,name):
print(name + " think")
animals = Animals("animals")
obj = Person(animals)
obj.think("person")
obj.eat()
4.依赖注入
像上一例中,如果有多层关系时,需要传入多个对象,为了解决这个问题就引入了依赖注入,
如上例在Person类实例化时自动传入Animals对象
class Foo:
def __init__(self):
self.name = 111
def fun(self)
print(self.name)
obj = Foo() #obj是Foo的实例化对象
在python中一切皆对象,Foo是通过type类创建的
#!/usr/bin/env python
# -*- coding:utf-8 -*-
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)
解释器解释:
1.遇到 class Foo,执行type的__init__方法
1.Type的init的方法里做什么么呢?不知道
obj = Foo(123)
3.执行Type的 __call__方法
执行Foo类的 __new__方法
执行Foo类的 __init__ 方法
new 和 __init()和__metaclass__:
-
__new__函数是实例一个类所要调用的函数,每当我们调用obj = Foo()来实例一个类时,都是先调用__new__()
-
然后再调用__init__()函数初始化实例. __init__()在__new__()执行后执行,
-
类中还有一个属性 __metaclass__,其用来表示该类由 谁 来实例化创建,所以,我们可以为 __metaclass__ 设置一个type类的派生类,从而查看 类 创建的过程。
那么依赖注入的实现方法,自定义一个type方法,实例化类的时候指定由自定义的type方法创建,
具体实现方法如下:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# 依赖注入应用
#DI
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
return False
@staticmethod
def value(cls):
return Mapper.__mapper_relation[cls]
class MyType(type):
def __call__(self, *args, **kwargs):
obj = self.__new__(self, *args, **kwargs)
arg_list = list(args)
if Mapper.exist(self):
value=Mapper.value(self)
arg_list.append(value)
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,"test1")
Mapper.register(Bar,"test12")
f=Foo()
print(f.name)
5.程序的设计原则
1. 单一责任原则(SRP)
一个对象只对一个元素负责 优点; 消除耦合,减小因需求变化引起代码僵化
2.开放封闭原则(OCP)
例如装饰器,可以对独立的功能实现扩展,但对源码不能进行修改 对扩展开放,对修改封闭 优点: 按照OCP原则设计出来的系统,降低了程序各部分之间的耦合性,其适应性、灵活性、稳定性都比较好。当已有软件系统需要增加新的功能时, 不需要对作为系统基础的抽象层进行修改,只需要在原有基础上附加新的模块就能实现所需要添加的功能。增加的新模块对原有的模块完全没有影响或影响很小, 这样就无须为原有模块进行重新测试 如何实现 ? 在面向对象设计中,不允许更必的是系统的抽象层,面允许扩展的是系统的实现层,所以解决问题的关键是在于抽象化。 在面向对象编程中,通过抽象类及接口,规定具体类的特征作为抽象层,相对稳定,不需要做更改的从面可以满足“对修改关闭”的原则;而从抽象类导出的具体 类可以 改变系统 的行为,从而满足“对扩展开放的原则"
3.里氏替换原则(LSP)
子类可以替换父类,父类出现的地方都可以用子类替换 可以使用任何派生类(子类)替换基类 优点: 可以很容易的实现同一父类下各个子类的互换,而客户端可以毫不察觉
4.接口分享原则(ISP)
对于接口进行分类避免一个接口的方法过多,避免”胖接口" 优点: 会使一个软件系统功能扩展时,修改的压力不会传到别的对象那里 如何实现 ? 得用委托分离接口 利用多继承分离接口
5.依赖倒置原则(DIP)
高层模块不应该依赖低层模块,二者都应该依赖其抽象(理解为接口);抽象不应该依赖细节;细节应该依赖抽象 隔离关系,使用接口或抽象类代指 高层次的模块不应该依赖于低层次的模块,而是,都应该依赖于抽象 优点: 使用传统过程化程序设计所创建的依赖关系,策略依赖于细节,这是糟糕的,因为策略受到细节改变的影响。 依赖倒置原则使细节和策略都依赖于抽象,抽象的稳定性决定了系统的稳定性
6.依赖注入(DI)和控制反转原则(ICO)
使用钩子再原来执行流程中注入其他对象
tornado项目设计实例
实例只包含登录,写此实例目的在于更好的理解及应用以上的内容
1、目录规划
说明:
Infrastructure 目录:公共组件目录 Model:业务逻辑处理目录 Repository: 数据仓库及数据处理目录 Statics:静态文件目录如(css,js,images等) UIAdmin: UI层 Views:模板文件目录 Application.py : 服务启动文件
2.业务访问流程
介绍完目录规划,那就来讲讲业务访问流程及数据走向
启动服务后,客户端访问URL,根据tornado路由找到相对的handler进行处理
找到handler后其相对方法(get/post/delete/put)中调用Model逻辑处理层方法进行处理并接收处理结果
Model逻辑处理层需
①创建接口
②建模
③创建协调层
创建完之后 ,由协调层(这里通用Services)调用数据层方法并接收处理结果返回给handler
4.数据处理层接收到Model调用后,处理数据并将数据返回给Model业务逻辑处理层
5.最终handler接收到最终结果,进行判断处理,并将处理结果返回给用户
3、落实实施
1.启动文件,路由关系配置
#!/usr/bin/env python # -*- coding:utf-8 -*- import tornado.ioloop import tornado.web from UIAdmin.Controllers import Account from UIAdmin.Controllers import Region from UIAdmin.Controllers import Customer from UIAdmin.Controllers import Merchant from UIAdmin import mapper settings = { 'template_path': 'Views', 'static_path': 'Statics', 'static_url_prefix': '/statics/', } application = tornado.web.Application([ (r"/login", Account.LoginHandler), (r"/check", Account.CheckCodeHandler), ],**settings) if __name__ == "__main__": application.listen(8000) tornado.ioloop.IOLoop.instance().start()