python设计模式(四)

MVC 模式

模式不是独立工作的,模式通常需要同时使用并加以组合,以实现特定的设计解决方法。

MVC不仅是一种实现用户界面的软件模式,也是一种易于修改维护的架构。通常分为3个基本部分:Model,View,Control。

工作机制:模型提供数据和业务逻辑(如存储查询信息),视图负责数据的展示,控制器是两者的粘合剂,根据用户需求来协调模型和视图。模型可以独立工作,视图和控制器依赖于模型。

MVC涉及的主要类:

  • 模型类定义针对数据的所有操作,并提供与数据使用方法有关的方法
  • 视图类代表用户界面。他提供相应的方法,帮助我们根据上下文和应用程序的需要来构建Web或GUI界面。不应该包含自己的任何逻辑,只应该显示收到的数据
  • 控制器类从请求接收数据,并将其发送到系统的其他部分,它需要提供用于路由请求的方法。

MVC模式常用场景:

  • 当需要更改展示方式而不更改业务逻辑
  • 多个控制器可用于使用多个视图来更改用户界面上的展示
  • 再次重申,当模型改变时,视图无需改动,相互独立

主要意图:

  • 将数据和数据的展示隔离开
  • 使类的维护和实现更加简单
  • 灵活地改变数据的存储和显示方式

模型

模型是应用程序的基石,它独立于试图和控制器。

提供客户端请求的数据,模型由存储和返回信息的数据库表来表示。模型会提供状态以及改变状态的方法。但它不知道数据是如何展示的

模型必须在多个操作中保持一致。

视图

视图将数据展示在接口上,不应包含任何复杂逻辑,避免与数据库直接交互,依靠模型来获取这些数据

控制器

控制用户在界面上的交互,调用相应的模型,也能将数据传递给视图。不应该进行数据库调用和数据的展示。

一个简答实现:

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
class Model(object):

services = {
'email' : {'number': 1000, 'price': 2,},
'sms': {'number': 1000, 'price': 10,},
'voice': {'number': 1000, 'price': 15,},
}

class View(object):

def listServices(self, services):
for src in services:
print(src, ' ')

def listPricing(self, services):
for src in services:
print("For", Model.services[src]['number'], src, "message you pay $", Model.services[src]['price'])

class Controller(object):

def __init__(self):
self.model = Model()
self.view = View()

def getServices(self):
services = self.model.services.keys()
return (self.view.listServices(services))

def getPricing(self):
services = self.model.services.keys()
return (self.view.listPricing(services))

class Client(object):

def __init__(self):
self.controller = Controller()
print("Services Provided:")
self.controller.getServices()
print("Pricing Provided:")
self.controller.getPricing()

client = Client()

这个只是展示数据

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
class Model(object):

def logic(self):
data = "Got it!"
print("Model: Crunching data as per business logic")
return data

class View(object):

def update(self, data):
print("View: Updating the view with results: ", data)

class Controller(object):

def __init__(self):
self.model = Model()
self.view = View()

def interface(self):
print("Controller: Relayed the Client asks")
data = self.model.logic()
self.view.update(data)

class Client(object):

def __init__(self):
print("Client: asks for certain information")
self.controller = Controller()
self.controller.interface()
client = Client()

Controller是沟通的桥梁

实现:

所需模块:Torando 4.3, SQLite3 :2.6.0

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
55
56
57
58
59
60
61
62
63
64
65
66
import tornado
import tornado.web
import tornado.ioloop
import tornado.httpserver
import sqlite3

def _execute(str):
with sqlite3.connect("huchi.db") as conn:
cur = conn.cursor()
return cur.execute(str)

class IndexHandler(tornado.web.RequestHandler):

def get(self):
query = "select * from task"
todos = _execute(query)
print(todos)
self.render("index.html", todos = todos)

class NewHandler(tornado.web.RequestHandler):

def post(self):
name = self.get_argument("name", None)
query = "create table if not exists task (id INTEGER PRIMARY KEY, name TEXT, status NUMERIC) "
_execute(query)
query = "insert into task (name, status) values ('%s', %d) " % (name, 1)
_execute(query)
self.redirect('/')

def get(self):
self.render('new.html')

class UpdateHandler(tornado.web.RequestHandler):

def get(self, id, status):
query = "update task set status=%d where id=%s" % (int(status), id)
_execute(query)
self.redirect('/')

class DeleteHandler(tornado.web.RequestHandler):

def get(self, id):
query = "delete from task where id=%s" % id
_execute(query)
self.redirect('/')

class RunApp(tornado.web.Application):

def __init__(self):
Handlers = [
(r'/', IndexHandler),
(r'/todo/new', NewHandler),
(r'/todo/update/(\w+)/(\d+)', UpdateHandler),
(r'/todo/delete/(\d+)', DeleteHandler),
]
settings = dict(
debug=True,
template_path='templates',
static_path='static'
)
tornado.web.Application.__init__(self, Handlers, **settings)

if __name__ == '__main__':
httpserver = tornado.httpserver.HTTPServer(RunApp())
httpserver.listen(5000)
tornado.ioloop.IOLoop.instance().start()

我们调用了tornado和sqlite3这两个库。

我们首先写了4个操作函数,增删查改四个类,每个类中都执行一两条语句。通过_execute()来执行里面的sql语句。最后用处理好的数据渲染我们的模板。有三个模板存放在templates文件夹中,这相当于是View

分别是:

base.html

1
2
3
4
5
6
7
8
9
<!DOCTYPE html>
<html>
<head>
{% block header %}{% end %}
</head>
<body>
{%block body %}{% end %}
</body>
</html>>

以及index.html

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
{% extends 'base.html' %}
<title>ToDo</title>
{% block body %}
<h3>Your Tasks</h3>
<table border="1">
<tr align="center">
<td>Id</td>
<td>Name</td>
<td>Status</td>
<td>Update</td>
<td>Delete</td>
</tr>
{% for todo in todos %}
<tr align="cneter">
<td>{{todo[0]}}</td>
<td>{{todo[1]}}</td>
{% if todo[2] %}
<td>Open</td>
{% else %}
<td>Closed</td>
{% end %}
{% if todo[2] %}
<td><a href="/todo/update/{{todo[0]}}/0">Close Task</a></td>
{% else %}
<td><a href="/todo/update/{{todo[0]}}/1">Open Task</a></td>
{% end %}
<td><a href="/todo/delete/{{todo[0]}}">X</a></td>
</tr>
{% end %}
</table>
<div>
<h3><a href="/todo/new">Add Task</a></h3>
</div>>
{% end %}

以及new.html

1
2
3
4
5
6
7
8
9
10
11
12
13
{% extends 'base.html' %}
<title>ToDo</title>
{% block body %}
<div>
<h3>Add Task to your list</h3>
<form action="/todo/new" method="post" id="new">
<p>
<input type="text" name="name" placeholder="Enter task" />
<input type="submit" class="submit" value="add" />
</p>
</form>
</div>
{% end %}

对于增删两个类,我们的操作中最后是增加了重定向的。

可以看到我们还有一个runAPP的类,相当于控制器,存放着4个路由。

最后通过启动HTTPWeb服务器来运行。

修改一下界面就可以做一个简易的ToDo list的页面了。

MVC的优点:

  • 使用MVC,开发人员可以将软件应用程序分为3个主要部分,可以提高维护性,强制松耦合,降低复杂度
  • MVC允许对前端进行独立更改,而对后端逻辑无需任何修改或只需很少的修改。相互独立

状态设计模式

行为模式关注的是对象的响应性,通过对象之间的交互以实现更强大的功能。状态设计模式是一种行为设计模式。一个对象可以基于其内部状态封装多个行为。状态模式也可以看做是运行时改变对象行为的一种方式。

状态设计模式在3个主要参与者协助下工作:

  • State: 封装对象行为的接口
  • ConcreteState: 实现State接口的子类,它的实现与对象的特定状态相关联。
  • Context: 定义了客户感兴趣的接口。还维护一个ConcreteState的子类的实例,该子类在内部定义了对象的特定状态的实现
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
from abc import ABCMeta, abstractmethod

class State(metaclass=ABCMeta):

@abstractmethod
def Handle(self):
pass

class ConcreteStateA(State):

def Handle(self):
print("ConcreteStateA")

class ConcreteStateB(State):

def Handle(self):
print("ConcreteStateB")

class Context(State):

def __init__(self):
self.state = None

def getState(self):
return self.state

def setState(self, state):
self.state = state

def Handle(self):
self.state.Handle()

if __name__ == '__main__':
context = Context()
stateA = ConcreteStateA()
stateB = ConcreteStateB()

context.setState(stateA)
context.Handle()

一个简单而不失美貌的例子

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
from abc import ABCMeta, abstractmethod

class State(metaclass=ABCMeta):

@abstractmethod
def doThis(self):
pass

class StartState(State):

def doThis(self):
print("TV switching ON..")

class StopState(State):

def doThis(self):
print("TV switching OFF..")

class TVContext(State):

def __init__(self):
self.state = None

def getState(self):
return self.state

def setState(self, state):
self.state = state

def doThis(self):
self.state.doThis()

if __name__ == '__main__':
context = TVContext()
start = StartState()
stop = StopState()

context.setState(stop)
context.doThis()

实现:

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
class ComputerState(object):

name = "state"
allowed = []

def switch(self, state):
if state.name in self.allowed:
print("Current:", self, " => switched to new state", state.name)
self.__class__ = state
else:
print("Current:", self, " => switching to", state.name, "not possible.")

def __str__(self):
return self.name

class Off(ComputerState):
name = "off"
allowed = ["on"]

class On(ComputerState):
name = "on"
allowed = ["off"]

class Suspend(ComputerState):
name = "suspend"
allowed = ["on"]

class Hibernate(ComputerState):
name = "hibernate"
allowed = ["on"]

class Computer(object):

def __init__(self, model="HP"):
self.model = model
self.state = Off()

def change(self, state):
self.state.switch(state)

if __name__ == '__main__':
comp = Computer()
comp.change(On)
comp.change(On)
comp.change(Suspend)
comp.change(Hibernate)
comp.change(Off)

状态模式的优缺点

优点:

  • 状态模式中,对象的行为是其状态的函数结果,并且行为在运行时根据状态而改变,消除了以来if/else调教逻辑的依赖。
  • 使用状态模式,适合实现多态行为,易于添加状态来支持额外的行为
  • 提高了聚合性,特定于状态的行为被聚合到ConcreteState类中,并且放置在代码中的同一个地方。
  • 通过只添加一个ConcreteState类来添加行为是非常容易的,改善了扩展应用程序行为时的灵活性,提高了代码的可维护性。

缺点:

  • 类爆炸,每一个状态都需要在ConcreteState的帮助下定义,可能创建了太多的单一的类,状态机的结果难以审查
  • 随着新行为的引入,Context类都需要进行相应的更新以处理每一个行为,使得上下文行为更容易受到每个新的行为的影响

反模式

软件设计不良设计的4个方面:

  • 不动性:以这种方式开发的应用程序难以重用
  • 刚性:以这种方式开发的应用程序,任何小修改都会导致软件的太多部分必须进行相应的改动
  • 脆弱性:当前应用程序的任何更改都会导致现有系统变得非常容易崩溃
  • 粘滞性:由于架构层面的修改非常困难,修改必须有开发人员在代码或环境本身中进行

反模式是处理重复出现问题的解决方案的后果,这些方案是无效。

原因:

  • 开发人员不了解软件开发实践
  • 开发人员没有将设计模式应用到正确的上下文中

反模式的好处:

  • 识别软件行业中出现的问题
  • 开发响应的工具来识别这些问题,确定根本原因
  • 描述可用于应用程序和架构层次上的改进措施

反模式:

  • 软件开发反模式
  • 软件架构反模式

软件开发反模式

普适原因:

  • 开发人员的想法会随着开发过程的推进而发生变化
  • 用例通常会随着客户的反馈而进行更改
  • 最初设计的数据结构会随着功能或可伸缩性等方面的考虑而发生变化

常见的几种情况:

意大利面条式代码

典型成因:

  • 对面向对象编程和分析的无知
  • 没有考虑产品的架构或设计
  • 快餐式思维

将面临的问题:

  • 结构的重用性降到了最低
  • 维护工作量过高
  • 进行修改时,扩展性和灵活性降低

金锤

由于某个解决方案在多个项目效果中不错,所以就瞎几把推广。一头扎进一个成熟的解决方案,不管其是否满足适用性。

原因:

  • 来自不了解具体问题的高层的建议
  • 虽然某解决方案在过去多次验证有效,但当前项目却具有不同的背景和要求
  • 被这种技术“绑架”了,即沉没成本

影响:

  • 痴迷于一个解决方案
  • 不是通过功能,而是通过开发中使用的技术来描述产品
  • 没有满足需求,造成与用户的预期不符

熔岩流

与软件应用程序中一段用不到的代码有关,人们害怕对其进行修改,怕破坏其他东西,这段代码在软件中固化了其位置。

成因:

  • 在产品中有大量试错代码
  • 由一个人单独编写的代码,未经审查,在没有任何培训的情况下移交给了其他开发团队
  • 软件架构设计的初始思想是通过代码库,但没有人理解

症状:

  • 开发的测试工作具有很低的代码覆盖率
  • 代码中含有莫名其妙的注释
  • 过时的接口,或开发人员需要围绕既有代码展开工作

复制粘贴剪切粘贴式编程

原因:

  • 新手开发者不习惯编写代码或者不知道如何开发代码
  • 快速修复bug或“急救章”式的开发
  • 代码重复,无法满足跨模块标准化以及代码结构化的要求
  • 缺乏长远打算

后果:

  • 多个应用程序存在相同的问题
  • 维护成本高,bug的生命周期变长
  • 缺少模块化代码库,相同的代码回散落在多处
  • 继承问题

软件架构饭模式

重复发明轮子

重新审视相同的问题并为他重新设计解决方案并没有意义,这基本就是重复发明轮子。

原因:

  • 缺乏中央文档或存储库来讲解架构级问题和村阿枫已实现的解决方案
  • 社区或公司的技术领袖缺乏沟通
  • 组织中遵循的流程是从头开始构建,不严谨,难以坚持

后果:

  • 解决一个标准问题的解决方案太多,许多解决方案考虑不周全
  • 耗费工程团队的时间资源
  • 封闭的系统架构/重复劳动/糟糕的风险管理

供应商套牢

产品公司依赖供应商提供的某些技术,以至于系统很难摆脱这些技术

原因:

  • 熟悉供应商公司的权威人士以及技术采购的可能折扣
  • 基于营销和销售业务而不是技术评估选择的技术
  • 在当前项目中使用经过验证的技术
  • 技术人员/开发人员已经接受过相关技术的培训

后果:

  • 公司产品的发布周期和维护周期取决于供应商的发布时间
  • 产品围绕该技术而不是根据客户的要求开发
  • 产品上市时间不可靠,不能满足客户需求

委员会设计

设计思想可能是由一些没有相应的节能或相应产品设计经验的技术专家提出的。

原因:

  • 根据组织的流程,产品的架构或设计是由众多的利益相关者批准的
  • 没有指定单独的联系人或负责设计的架构师
  • 由营销或技术专家确定设计优先级,而不是客户反馈来确定的

症状:

  • 开发人员和架构师之间的观点冲突,即使在设计完成后依旧如此
  • 过于复杂的设计,很难记录
  • 规格或设计的任何改动都需要经过多次审查,导致实现延迟

(完)

文章目录
  1. 1. MVC 模式
    1. 1.1. 模型
    2. 1.2. 视图
    3. 1.3. 控制器
    4. 1.4. 实现:
    5. 1.5. MVC的优点:
  2. 2. 状态设计模式
    1. 2.1. 实现:
    2. 2.2. 状态模式的优缺点
  3. 3. 反模式
    1. 3.1. 软件开发反模式
      1. 3.1.1. 意大利面条式代码
      2. 3.1.2. 金锤
      3. 3.1.3. 熔岩流
      4. 3.1.4. 复制粘贴剪切粘贴式编程
    2. 3.2. 软件架构饭模式
      1. 3.2.1. 重复发明轮子
      2. 3.2.2. 供应商套牢
      3. 3.2.3. 委员会设计
|