淺談tornado項目應用設計

来源:http://www.cnblogs.com/jl-bai/archive/2016/09/16/5865481.html
-Advertisement-
Play Games

一.預備知識 最近開始嘗試做一些tornado商城項目,在開始之前需要引入一些項目設計知識,如介面,抽象方法抽象類,組合,程式設計原則等,個人理解項目的合理設計可增加其靈活性,降低數據之間的耦合性,提高穩定性,下麵介紹一些預備知識 1.介面 其實py中沒有介面這個概念。要想實現介面的功能,可以通過主 ...


一.預備知識  

 最近開始嘗試做一些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對象

  那麼,在引入依賴註入時先瞭解一下python類實例化過程中背後做了什麼事情

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. 單一責任原則       

一個對象只對一個元素負責

優點;

  消除耦合,減小因需求變化引起代碼僵化

  2.開放封閉原則

    對擴展開放,對修改關閉

    優點:

      按照OCP原則設計出來的系統,降低了程式各部分之間的耦合性,其適應性、靈活性、穩定性都比較好。當已有軟體系統需要增加新的功能時,

      不需要對作為系統基礎的抽象層進行修改,只需要在原有基礎上附加新的模塊就能實現所需要添加的功能。增加的新模塊對原有的模塊完全沒有影響或影響很小,

      這樣就無須為原有模塊進行重新測試

    如何實現 ? 

      在面向對象設計中,不允許更必的是系統的抽象層,面允許擴展的是系統的實現層,所以解決問題的關鍵是在於抽象化。

      在面向對象編程中,通過抽象類及介面,規定具體類的特征作為抽象層,相對穩定,不需要做更改的從面可以滿足“對修改關閉”的原則;而從抽象類導出的具體 類可以

      改變系統 的行為,從而滿足“對擴展開放的原則"

  3.里氏替換原則  

    可以使用任何派生類替換基類     優點:       可以很容易的實現同一父類下各個子類的互換,而客戶端可以毫不察覺   4.介面分享原則     對於介面進行分類避免一個介面的方法過多,避免”胖介面"     優點:       會使一個軟體系統功能擴展時,修改的壓力不會傳到別的對象那裡     如何實現 ?       得用委托分離介面       利用多繼承分離介面

  5.依賴倒置原則    

隔離關係,使用介面或抽象類代指 高層次的模塊不應該依賴於低層次的模塊,而是,都應該依賴於抽象     優點:       使用傳統過程化程式設計所創建的依賴關係,策略依賴於細節,這是糟糕的,因為策略受到細節改變的影響。       依賴倒置原則使細節和策略都依賴於抽象,抽象的穩定性決定了系統的穩定性   6.依賴註入和控制反轉原則          使用鉤子再原來執行流程中註入其他對象

二.tornado項目設計實例

  此實例只包含登錄,寫此實例目的在於更好的理解及應用以上的內容

1.目錄規劃

  

註:

  Infrastructure 目錄:公共組件目錄

  Model:業務邏輯處理目錄

  Repository: 數據倉庫及數據處理目錄

  Statics:靜態文件目錄如(css,js,images等)

  UIAdmin: UI層

  Views:模板文件目錄

  Application.py : 服務啟動文件

2.業務訪問流程

   介紹完目錄規劃,那就來講講業務訪問流程及數據走向

  1. 啟動服務後,客戶端訪問URL,根據tornado路由找到相對的handler進行處理
  2. 找到handler後其相對方法(get/post/delete/put)中調用Model邏輯處理層方法進行處理並接收處理結果
  3. 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()
Application.py

註:

settings 中指定配置,如模板文件路徑,靜態文件路徑等

application :路由配置,那個路徑由那個handler進行處理

2.handler配置 

#!/usr/bin/env python
# -*- coding: utf-8 -*-
import io
from Infrastructure.Core.HttpRequest import BaseRequestHandler
from Infrastructure.utils import check_code
from Model.User import UserService


class LoginHandler(BaseRequestHandler):
    def get(self, *args, **kwargs):
        self.render("Admin/Account/login.html")

    def post(self, *args, **kwargs):
        username = self.get_argument("username",None)
        email = self.get_argument("email",None)
        pwd = self.get_argument("pwd",None)
        code = self.get_argument("checkcode",None)
        service = UserService()
        result = service.check_login(user=username,email=email,pwd=pwd)
        #obj封裝了所有的用戶信息,UserModel對象
        if result and code.upper() == self.session["CheckCode"].upper():
            self.session['username'] = result.username
            self.redirect("/ProvinceManager.html")
        else:
            self.write("alert('error')")
hanler.py

handler中主要是針對數據訪問方式的不同,給出不同的處理方法,並將結果返回給客戶端

3.Model 邏輯處理層

  邏輯處理層中,著重看的有三點
  1. 建模
  2. 介面
  3. 協調

 建模  

#!/usr/bin/env python
# -*- coding: utf-8 -*-
#建模
from Infrastructure.DI.Meta import DIMetaClass

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)


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)


class UserModel:
    def __init__(self, nid, username,password, email, last_login, user_type_obj, vip_type_obj):
        self.nid = nid
        self.username = username
        self.email = email
        self.password = password
        self.last_login = last_login
        self.user_type_obj = user_type_obj
        self.vip_type_obj = vip_type_obj
建模

 介面

IUseRepository類:介面類,用於約束資料庫訪問類的方法

class IUserRepository:

    def fetch_one_by_user(self,user,pwd):
        """
        根據用戶名和密碼獲取對象
        :param user:
        :param pwd:
        :return:
        """

    def fetch_one_by_email(self, user, pwd):
        """
        根據郵箱和密碼獲取對象
        :param user:
        :param pwd:
        :return:
        """
介面

協調

協調作用主要是調用數據處理層的方法,並將數據處理層處理後的結果返回給它的上一層的調度者

class UserService(metaclass=DIMetaClass):
    def __init__(self, user_repository):
        """
        :param user_repository: 數據倉庫對象
        """
        self.userRepository = user_repository

    def check_login(self,user,email,pwd):
        if user:
            #數據倉庫執行SQL後返回的字典
            #{"nid":1,username:xxx,vip:2,usertype:1}
            ret = self.userRepository.fetch_one_by_user(user,pwd)
        else:
            ret = self.userRepository.fetch_one_by_email(email,pwd)
        return ret
協調層UserService

4.Repository數據處理層

  將處理後結果(usermodel對象)返回給上一層調度者(UserService)

#!/usr/bin/env python
# -*- coding:utf-8 -*-
#數據表創建 
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column
from sqlalchemy import Integer, Integer, CHAR, VARCHAR, ForeignKey, Index, DateTime, DECIMAL, TEXT
from sqlalchemy.orm import sessionmaker, relationship
from sqlalchemy import create_engine

engine = create_engine("mysql+pymysql://root:123@127.0.0.1:3306/ShoppingDb?charset=utf8", max_overflow=5)

Base = declarative_base()


class Province(Base):
    """
    省
    """
    __tablename__ = 'province'
    nid = Column(Integer, primary_key=True)
    caption = Column(VARCHAR(16), index=True)


class City(Base):
    """
    市
    """
    __tablename__ = 'city'
    nid = Column(Integer, primary_key=True)
    caption = Column(VARCHAR(16), index=True)
    province_id = Column(Integer, ForeignKey('province.nid'))


class County(Base):
    """
    縣(區)
    """
    __tablename__ = 'county'
    nid = Column(Integer, primary_key=True)
    caption = Column(VARCHAR(16), index=True)
    city_id = Column(Integer, ForeignKey('city.nid'))


class UserInfo(Base):
    """
    用戶信息
    """

    __tablename__ = 'userinfo'

    nid = Column(Integer, primary_key=True)

    USER_TYPE = (
        {'nid': 1, 'caption': '用戶'},
        {'nid': 2, 'caption': '商戶'},
        {'nid': 3, 'caption': '管理員'},
    )
    user_type = Column(Integer)

    VIP_TYPE = (
        {'nid': 1, 'caption': '銅牌'},
        {'nid': 2, 'caption': '銀牌'},
        {'nid': 3, 'caption': '金牌'},
        {'nid': 4, 'caption': '鉑金'},
    )
    vip = Column(Integer)

    username = Column(VARCHAR(32))
    password = Column(VARCHAR(64))
    email = Column(VARCHAR(64))

    last_login = Column(DateTime)
    ctime = Column(DateTime)

    __table_args__ = (
        Index('ix_user_pwd', 'username', 'password'),
        Index('ix_email_pwd', 'email', 'password'),
    )
SqlAchemyOrm.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from Model.User import IUserRepository
from Model.User import UserModel
from Model.User import UserType
from Model.User import VipType
from Repository.Admin.DbConnection import DbConnection


class UserRepository(IUserRepository):

    def __init__(self):
        self.db_conn = DbConnection()

    def fetch_one_by_email(self, email, password):
        ret = None

        cursor = self.db_conn.connect()
        sql = """select nid,username,password,email,last_login,vip,user_type from userinfo where email=%s and password=%s"""
        cursor.execute(sql, (email, password))
        db_result = cursor.fetchone()
        self.db_conn.close()
        print(type(db_result), db_result)
        if db_result:
            ret = UserModel(nid=db_result['nid'],
                            username=db_result['username'],
                            password=db_result['password'],
                            email=db_result['email'],
                            last_login=db_result['last_login'],
                            user_type_obj=UserType(nid=db_result['user_type']),
                            vip_type_obj=VipType(nid=db_result['vip']),)
            return ret
        return db_result

    def fetch_one_by_user(self, username, password):
        ret = None
        cursor = self.db_conn.connect()
        sql = """select nid,username,password,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:
    #建模,將usermodel對象返回給上一層調用者,因為要向用戶展示的user_type不可能為1,2這些數據而應該是相對的caption
            ret = UserModel(nid=db_result['nid'],
                            username=db_result['username'],
                            password=db_result['password'],
                            email=db_result['email'],
                            last_login=db_result['last_login'],
                            user_type_obj=UserType(nid=db_result['user_type']),
                            vip_type_obj=VipType(nid=db_result['vip']),)
            return ret
        return db_result
數據處理層

5.Handler最終處理

  接收到最終處理結果後判斷,並返回數據給用戶

註:

  有沒有註意到UserService是怎麼和數據處理層建立聯繫的?

  這裡我們用到了依賴註入,具體配置如下:  

#!/usr/bin/env python
# -*- coding:utf-8 -*-
#依賴註入

class DIMapper:

    __mapper_dict = {}

    @staticmethod
    def inject(cls, arg):
        if cls not in DIMapper.__mapper_dict:
            DIMapper.__mapper_dict[cls] = arg

    @staticmethod
    def get_mappers():
        return DIMapper.__mapper_dict


	   

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 一、創建django程式 終端命令:django-admin startproject sitename IDE創建Django程式時,本質上都是自動執行上述命令 其他常用命令: python manage.py runserver 0.0.0.0 #創建APP python manage.py s ...
  • 近些天一直在學習javaweb 但總是時斷時續,今天終於有時間,來學習一下MVC下的查詢模式。 一、首先先瞭解一下 什麼是mvc? MVC是Model-View-Controller的簡稱,即模型-視圖-控制器。它是一種設計模式,它吧應用程式分成三個核心模塊,模型,視圖,控制器。他們各自處理自己的任 ...
  • 一. 針對很多java web初學者入門困難的問題,筆者利用一個小型web項目,一步一步的展示java web開發方法,每一個章節引入一些java web開發的重點知識,讓同學們可以將java web開發的知識點串聯起來,學會綜合運用。 對於初學者而言,代碼示例是最好的教材,可能一遍看不懂,就多看幾 ...
  • 最近比較關註學習Python方面的資料和課程,由於Python本身基本都是在Linux下開發,本人windows用習慣了初用Linux各種彆扭啊。 下麵將我在配置Windows環境下的禁言寫出來,與大家分享。 1.下載與安裝Python 1.Python官方網址:https://www.python ...
  • 1.列表解析。 (1)這是一個,讓人聽起來十分欣喜的術語,代表著你可以通過一個迴圈將所有值放到一個列表中。python列表解析屬於python的迭代中的一種,相比python for迴圈速度會快很多。下麵看下python列表解析是怎麼工作的: 註:python會在解釋器里對range(10)進行迭代 ...
  • 題目: 在一個2^k x 2^k 個方格組成的棋盤中,若恰有一個方格與其他方格不同,則稱該方格為一特殊方格,且稱該棋盤為一特殊棋盤。現在要用4種不同形態的L型骨牌覆蓋一個給定的特殊棋盤上除特殊方格以外的所有方格,且任意2個L型骨牌不得重疊覆蓋。 解釋一下什麼是L型骨牌:就是由三個方格組成的一個角,可 ...
  • Reverse a linked list from position m to n. Do it in-place and in one-pass. For example:Given 1->2->3->4->5->NULL, m = 2 and n = 4, return 1->4->3->2- ...
  • 1.字典。 字典是python中的映射的數據類型,由鍵值對構成,幾乎所有類型的python對象都可以用作鍵,不過一般還是以數字或是字元串最為常見。值可以是任意類型的python對象,字典元素用{}來包裹。 (1)要註意“鍵值對”的相關概念。 假設字典為dics = {0:'a', 1:'b', 'c ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...