learn.lianglianglee.com/专栏/白话设计模式 28 讲(完)/05 职责模式:我的假条去哪了.md.html
2022-05-11 19:04:14 +08:00

929 lines
28 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<!-- saved from url=(0046)https://kaiiiz.github.io/hexo-theme-book-demo/ -->
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1.0, user-scalable=no">
<link rel="icon" href="/static/favicon.png">
<title>05 职责模式:我的假条去哪了.md.html</title>
<!-- Spectre.css framework -->
<link rel="stylesheet" href="/static/index.css">
<!-- theme css & js -->
<meta name="generator" content="Hexo 4.2.0">
</head>
<body>
<div class="book-container">
<div class="book-sidebar">
<div class="book-brand">
<a href="/">
<img src="/static/favicon.png">
<span>技术文章摘抄</span>
</a>
</div>
<div class="book-menu uncollapsible">
<ul class="uncollapsible">
<li><a href="/" class="current-tab">首页</a></li>
</ul>
<ul class="uncollapsible">
<li><a href="../">上一级</a></li>
</ul>
<ul class="uncollapsible">
<li>
<a href="/专栏/白话设计模式 28 讲(完)/00 生活中的设计模式:启程之前,请不要错过我.md.html">00 生活中的设计模式:启程之前,请不要错过我.md.html</a>
</li>
<li>
<a href="/专栏/白话设计模式 28 讲(完)/01 监听模式:坑爹的热水器.md.html">01 监听模式:坑爹的热水器.md.html</a>
</li>
<li>
<a href="/专栏/白话设计模式 28 讲(完)/02 适配模式:身高不够鞋来凑.md.html">02 适配模式:身高不够鞋来凑.md.html</a>
</li>
<li>
<a href="/专栏/白话设计模式 28 讲(完)/03 状态模式:人与水的三态.md.html">03 状态模式:人与水的三态.md.html</a>
</li>
<li>
<a href="/专栏/白话设计模式 28 讲(完)/04 单例模式:你是我生命的唯一.md.html">04 单例模式:你是我生命的唯一.md.html</a>
</li>
<li>
<a class="current-tab" href="/专栏/白话设计模式 28 讲(完)/05 职责模式:我的假条去哪了.md.html">05 职责模式:我的假条去哪了.md.html</a>
</li>
<li>
<a href="/专栏/白话设计模式 28 讲(完)/06 中介模式:找房子问中介.md.html">06 中介模式:找房子问中介.md.html</a>
</li>
<li>
<a href="/专栏/白话设计模式 28 讲(完)/07 代理模式:帮我拿一下快递.md.html">07 代理模式:帮我拿一下快递.md.html</a>
</li>
<li>
<a href="/专栏/白话设计模式 28 讲(完)/08 装饰模式:你想怎么穿就怎么穿.md.html">08 装饰模式:你想怎么穿就怎么穿.md.html</a>
</li>
<li>
<a href="/专栏/白话设计模式 28 讲(完)/09 工厂模式:你要拿铁还是摩卡.md.html">09 工厂模式:你要拿铁还是摩卡.md.html</a>
</li>
<li>
<a href="/专栏/白话设计模式 28 讲(完)/10 迭代模式:下一个就是你了.md.html">10 迭代模式:下一个就是你了.md.html</a>
</li>
<li>
<a href="/专栏/白话设计模式 28 讲(完)/11 组合模式:自己组装电脑.md.html">11 组合模式:自己组装电脑.md.html</a>
</li>
<li>
<a href="/专栏/白话设计模式 28 讲(完)/12 构建模式:想要车还是庄园.md.html">12 构建模式:想要车还是庄园.md.html</a>
</li>
<li>
<a href="/专栏/白话设计模式 28 讲(完)/13 克隆模式:给你一个分身术.md.html">13 克隆模式:给你一个分身术.md.html</a>
</li>
<li>
<a href="/专栏/白话设计模式 28 讲(完)/14 策略模式:怎么来不重要,人到就行.md.html">14 策略模式:怎么来不重要,人到就行.md.html</a>
</li>
<li>
<a href="/专栏/白话设计模式 28 讲(完)/15 命令模式:大闸蟹,走起!.md.html">15 命令模式:大闸蟹,走起!.md.html</a>
</li>
<li>
<a href="/专栏/白话设计模式 28 讲(完)/16 备忘模式:好记性不如烂笔头.md.html">16 备忘模式:好记性不如烂笔头.md.html</a>
</li>
<li>
<a href="/专栏/白话设计模式 28 讲(完)/17 享元模式:颜料很贵必须充分利用.md.html">17 享元模式:颜料很贵必须充分利用.md.html</a>
</li>
<li>
<a href="/专栏/白话设计模式 28 讲(完)/18 外观模式:学妹别慌,学长帮你.md.html">18 外观模式:学妹别慌,学长帮你.md.html</a>
</li>
<li>
<a href="/专栏/白话设计模式 28 讲(完)/19 访问模式:一千个读者一千个哈姆雷特.md.html">19 访问模式:一千个读者一千个哈姆雷特.md.html</a>
</li>
<li>
<a href="/专栏/白话设计模式 28 讲(完)/20 生活中的设计模式:与经典设计模式的不解渊源.md.html">20 生活中的设计模式:与经典设计模式的不解渊源.md.html</a>
</li>
<li>
<a href="/专栏/白话设计模式 28 讲(完)/21 生活中的设计模式:那些未完待续的设计模式.md.html">21 生活中的设计模式:那些未完待续的设计模式.md.html</a>
</li>
<li>
<a href="/专栏/白话设计模式 28 讲(完)/22 深入解读过滤器模式:制作一杯鲜纯细腻的豆浆.md.html">22 深入解读过滤器模式:制作一杯鲜纯细腻的豆浆.md.html</a>
</li>
<li>
<a href="/专栏/白话设计模式 28 讲(完)/23 深入解读对象池技术:共享让生活更便捷.md.html">23 深入解读对象池技术:共享让生活更便捷.md.html</a>
</li>
<li>
<a href="/专栏/白话设计模式 28 讲(完)/24 深入解读回调机制:把你技能亮出来.md.html">24 深入解读回调机制:把你技能亮出来.md.html</a>
</li>
<li>
<a href="/专栏/白话设计模式 28 讲(完)/25 谈谈我对设计模式的理解.md.html">25 谈谈我对设计模式的理解.md.html</a>
</li>
<li>
<a href="/专栏/白话设计模式 28 讲(完)/26 谈谈我对设计原则的思考.md.html">26 谈谈我对设计原则的思考.md.html</a>
</li>
<li>
<a href="/专栏/白话设计模式 28 讲(完)/27 谈谈我对项目重构的看法.md.html">27 谈谈我对项目重构的看法.md.html</a>
</li>
</ul>
</div>
</div>
<div class="sidebar-toggle" onclick="sidebar_toggle()" onmouseover="add_inner()" onmouseleave="remove_inner()">
<div class="sidebar-toggle-inner"></div>
</div>
<script>
function add_inner() {
let inner = document.querySelector('.sidebar-toggle-inner')
inner.classList.add('show')
}
function remove_inner() {
let inner = document.querySelector('.sidebar-toggle-inner')
inner.classList.remove('show')
}
function sidebar_toggle() {
let sidebar_toggle = document.querySelector('.sidebar-toggle')
let sidebar = document.querySelector('.book-sidebar')
let content = document.querySelector('.off-canvas-content')
if (sidebar_toggle.classList.contains('extend')) { // show
sidebar_toggle.classList.remove('extend')
sidebar.classList.remove('hide')
content.classList.remove('extend')
} else { // hide
sidebar_toggle.classList.add('extend')
sidebar.classList.add('hide')
content.classList.add('extend')
}
}
function open_sidebar() {
let sidebar = document.querySelector('.book-sidebar')
let overlay = document.querySelector('.off-canvas-overlay')
sidebar.classList.add('show')
overlay.classList.add('show')
}
function hide_canvas() {
let sidebar = document.querySelector('.book-sidebar')
let overlay = document.querySelector('.off-canvas-overlay')
sidebar.classList.remove('show')
overlay.classList.remove('show')
}
</script>
<div class="off-canvas-content">
<div class="columns">
<div class="column col-12 col-lg-12">
<div class="book-navbar">
<!-- For Responsive Layout -->
<header class="navbar">
<section class="navbar-section">
<a onclick="open_sidebar()">
<i class="icon icon-menu"></i>
</a>
</section>
</header>
</div>
<div class="book-content" style="max-width: 960px; margin: 0 auto;
overflow-x: auto;
overflow-y: hidden;">
<div class="book-post">
<p id="tip" align="center"></p>
<div><h1>05 职责模式:我的假条去哪了</h1>
<p>【故事剧情】</p>
<blockquote>
<p>周五了Tony 因为家里有一些重要的事需要回家一趟,于是向他的领导 Eren 请假,填写完假条便交给了 Eren。得到的回答却是“这个假条我签不了你得等部门总监同意” Tony 一脸疑惑:“上次去参加 SDCC 开发者大会请了一天假不就是您签的吗?” Eren“上次你只请了一天我可以直接签。现在你是请五天我要提交给部门总监等他同意才可以。”</p>
<p>Tony“您怎么不早说啊” Eren“你也没问啊下次请假要提前一点……”</p>
<p>Tony 哪管这些啊!对他来说,每次请假只要把假条交给 Eren其他的事情都交给领导去处理吧</p>
<p>事实却是,整个请假的过程要走一套复杂的流程:</p>
<ol>
<li>小于等于2天直属领导签字提交行政部门</li>
<li>大于2天小于等于5天直属领导签字部门总监签字提交行政部门</li>
<li>大于5天小于等于1月直属领导签字部门总监签字CEO 签字,提交行政部门。</li>
</ol>
</blockquote>
<p><img src="assets/0d80f9d0-d5b6-11e7-ba8d-675556ef95d9.jpg" alt="enter image description here" /></p>
<h3>用程序来模拟生活</h3>
<p>对于 Tony 来说,他只需要每次把假条交给直属领导,其他的繁琐流程他都可以不用管,所以他并不知道请假流程的具体细节。但请假会影响项目的进展和产品的交互,所以请假其实是一种责任担当的过程:你请假了,必然会给团队或部门增加工作压力,所以领导肯定会控制风险。请假的时间越长,风险越大,领导的压力和责任也越大,责任人也就越多,责任人的链条也就越长。</p>
<p>程序来源于生活,我们可以用程序来模拟这一个有趣的场景。</p>
<p>源码示例:</p>
<pre><code class="language-python">class Person:
&quot;请假申请人&quot;
def __init__(self, name, dayoff, reason):
self.__name = name
self.__dayoff = dayoff
self.__reason = reason
self.__leader = None
def getName(self):
return self.__name
def getDayOff(self):
return self.__dayoff
def getReason(self):
return self.__reason
def setLeader(self, leader):
self.__leader = leader
def reuqest(self):
print(self.__name, &quot;申请请假&quot;, self.__dayoff, &quot;天。请假事由:&quot;, self.__reason)
if( self.__leader is not None):
self.__leader.handleRequest(self)
class Manager:
&quot;公司管理人员&quot;
def __init__(self, name, title):
self.__name = name
self.__title = title
self.__nextHandler = None
def getName(self):
return self.__name
def getTitle(self):
return self.__title
def setNextHandler(self, nextHandler):
self.__nextHandler = nextHandler
def getNextHandler(self):
return self.__nextHandler
def handleRequest(self, person):
pass
class Supervisor(Manager):
&quot;主管&quot;
def __init__(self, name, title):
super().__init__(name, title)
def handleRequest(self, person):
if(person.getDayOff() &lt;= 2):
print(&quot;同意&quot;, person.getName(), &quot;请假,签字人:&quot;, self.getName(), &quot;(&quot;, self.getTitle(), &quot;)&quot;)
nextHander = self.getNextHandler()
if(nextHander is not None):
nextHander.handleRequest(person)
class DepartmentManager(Manager):
&quot;部门总监&quot;
def __init__(self, name, title):
super().__init__(name, title)
def handleRequest(self, person):
if(person.getDayOff() &gt;2 and person.getDayOff() &lt;= 5):
print(&quot;同意&quot;, person.getName(), &quot;请假,签字人:&quot;, self.getName(), &quot;(&quot;, self.getTitle(), &quot;)&quot;)
nextHander = self.getNextHandler()
if(nextHander is not None):
nextHander.handleRequest(person)
class CEO(Manager):
&quot;CEO&quot;
def __init__(self, name, title):
super().__init__(name, title)
def handleRequest(self, person):
if (person.getDayOff() &gt; 5 and person.getDayOff() &lt;= 22):
print(&quot;同意&quot;, person.getName(), &quot;请假,签字人:&quot;, self.getName(), &quot;(&quot;, self.getTitle(), &quot;)&quot;)
nextHander = self.getNextHandler()
if (nextHander is not None):
nextHander.handleRequest(person)
class Administrator(Manager):
&quot;行政人员&quot;
def __init__(self, name, title):
super().__init__(name, title)
def handleRequest(self, person):
print(person.getName(), &quot;的请假申请已审核,情况属实!已备案处理。处理人:&quot;, self.getName(), &quot;(&quot;, self.getTitle(), &quot;)\n&quot;)
nextHander = self.getNextHandler()
</code></pre>
<p>测试代码:</p>
<pre><code class="language-python">def testChainOfResponsibility():
directLeader = Supervisor(&quot;Eren&quot;, &quot;客户端研发部经理&quot;)
departmentLeader = DepartmentManager(&quot;Eric&quot;, &quot;技术研发中心总监&quot;)
ceo = CEO(&quot;Helen&quot;, &quot;创新文化公司CEO&quot;)
administrator = Administrator(&quot;Nina&quot;, &quot;行政中心总监&quot;)
directLeader.setNextHandler(departmentLeader)
departmentLeader.setNextHandler(ceo)
ceo.setNextHandler(administrator)
sunny = Person(&quot;Sunny&quot;, 1, &quot;参加MDCC大会。&quot;)
sunny.setLeader(directLeader)
sunny.reuqest()
tony = Person(&quot;Tony&quot;, 5, &quot;家里有紧急事情!&quot;)
tony.setLeader(directLeader)
tony.reuqest()
pony = Person(&quot;Pony&quot;, 15, &quot;出国深造。&quot;)
pony.setLeader(directLeader)
pony.reuqest()
</code></pre>
<p>输出结果:</p>
<pre><code>Sunny 申请请假 1 天。请假事由: 参加MDCC大会。
同意 Sunny 请假,签字人: Eren ( 客户端研发部经理 )
Sunny 的请假申请已审核,情况属实!已备案处理。处理人: Nina ( 行政中心总监 )
Tony 申请请假 5 天。请假事由: 家里有紧急事情!
同意 Tony 请假,签字人: Eric ( 技术研发中心总监 )
Tony 的请假申请已审核,情况属实!已备案处理。处理人: Nina ( 行政中心总监 )
Pony 申请请假 15 天。请假事由: 出国深造。
同意 Pony 请假,签字人: Helen ( 创新文化公司CEO )
Pony 的请假申请已审核,情况属实!已备案处理。处理人: Nina ( 行政中心总监 )
</code></pre>
<h3>从剧情中思考职责模式</h3>
<p>从请假这个示例中我们发现,对于 Tony 来说,他并不需要知道假条处理的具体细节,甚至不需要知道假条去哪儿了,他只需要知道假条有人会处理。而假条的处理流程是一手接一手的责任传递,处理假条的所有人构成了一条<strong>责任的链条</strong>。链条上的每一个人只处理自己职责范围内的请求,对于自己处理不了请求,直接交给下一个责任人。这就是程序设计中职责模式的核心思想。</p>
<p><img src="assets/43e959e0-d5b6-11e7-ba8d-675556ef95d9.jpg" alt="enter image description here" /></p>
<p><strong>职责模式</strong> 避免请求发送者与接收者耦合在一起,让多个对象都有可能接收请求,将这些对象连接成一条链,并且沿着这条链传递请求,直到有对象处理它为止。职责模式也称为责任链模式,它是一种对象行为型模式。</p>
<p>职责链模式将请求的发送者和接受者解耦了。客户端不需要知道请求处理者的明确信息和处理的具体逻辑,甚至不需要知道链的结构,它只需要将请求进行发送即可。</p>
<p>在职责链模式中我们可以随时随地的增加或者更改一个责任人,甚至可以更改责任人的顺序,增加了系统的灵活性。但是有时候可能会导致一个请求无论如何也得不到处理,它会被放置在链末端。</p>
<h3>职责模式的模型抽象</h3>
<h4>代码框架</h4>
<p>上面的示例代码还是相对比较粗糙,我们可以对它进行进一步的重构和优化,抽象出职责模式的框架模型。</p>
<pre><code class="language-python">class Request:
&quot;请求(内容)&quot;
def __init__(self, name, dayoff, reason):
self.__name = name
self.__dayoff = dayoff
self.__reason = reason
self.__leader = None
def getName(self):
return self.__name
def getDayOff(self):
return self.__dayoff
def getReason(self):
return self.__reason
class Responsible:
&quot;责任人的抽象类&quot;
def __init__(self, name, title):
self.__name = name
self.__title = title
self.__nextHandler = None
def getName(self):
return self.__name
def getTitle(self):
return self.__title
def setNextHandler(self, nextHandler):
self.__nextHandler = nextHandler
def getNextHandler(self):
return self.__nextHandler
def handleRequest(self, request):
pass
</code></pre>
<h4>类图</h4>
<p>上面的代码框架可用类图表示如下:</p>
<p><img src="assets/7c356e10-d5b6-11e7-8257-812093f8cef1.jpg" alt="enter image description here" /></p>
<h4>基于框架的实现</h4>
<p>有了上面的代码框架之后,我们要实现示例代码的功能就会更简单了,代码也会更加优雅。最开始的示例代码我们假设它为 version 1.0,那么再看看基于框架的 version 2.0 吧。</p>
<pre><code class="language-python">class Person:
&quot;请求者&quot;
def __init__(self, name):
self.__name = name
self.__leader = None
def setName(self, name):
self.__name = name
def getName(self):
return self.__name
def setLeader(self, leader):
self.__leader = leader
def getLeader(self):
return self.__leader
def sendReuqest(self, request):
print(self.__name, &quot;申请请假&quot;, request.getDayOff(), &quot;天。请假事由:&quot;, request.getReason())
if (self.__leader is not None):
self.__leader.handleRequest(request)
class Supervisor(Responsible):
&quot;主管&quot;
def __init__(self, name, title):
super().__init__(name, title)
def handleRequest(self, request):
if (request.getDayOff() &lt;= 2):
print(&quot;同意&quot;, request.getName(), &quot;请假,签字人:&quot;, self.getName(), &quot;(&quot;, self.getTitle(), &quot;)&quot;)
nextHander = self.getNextHandler()
if (nextHander is not None):
nextHander.handleRequest(request)
class DepartmentManager(Responsible):
&quot;部门总监&quot;
def __init__(self, name, title):
super().__init__(name, title)
def handleRequest(self, request):
if (request.getDayOff() &gt; 2 and request.getDayOff() &lt;= 5):
print(&quot;同意&quot;, request.getName(), &quot;请假,签字人:&quot;, self.getName(), &quot;(&quot;, self.getTitle(), &quot;)&quot;)
nextHander = self.getNextHandler()
if (nextHander is not None):
nextHander.handleRequest(request)
class CEO(Responsible):
&quot;CEO&quot;
def __init__(self, name, title):
super().__init__(name, title)
def handleRequest(self, request):
if (request.getDayOff() &gt; 5 and request.getDayOff() &lt;= 22):
print(&quot;同意&quot;, request.getName(), &quot;请假,签字人:&quot;, self.getName(), &quot;(&quot;, self.getTitle(), &quot;)&quot;)
nextHander = self.getNextHandler()
if (nextHander is not None):
nextHander.handleRequest(request)
class Administrator(Responsible):
&quot;行政人员&quot;
def __init__(self, name, title):
super().__init__(name, title)
def handleRequest(self, request):
print(request.getName(), &quot;的请假申请已审核,情况属实!已备案处理。处理人:&quot;, self.getName(), &quot;(&quot;, self.getTitle(), &quot;)\n&quot;)
nextHander = self.getNextHandler()
</code></pre>
<p>测试代码需要稍微修改一下:</p>
<pre><code class="language-python">def testChainOfResponsibility1():
directLeader = Supervisor(&quot;Eren&quot;, &quot;客户端研发部经理&quot;)
departmentLeader = DepartmentManager(&quot;Eric&quot;, &quot;技术研发中心总监&quot;)
ceo = CEO(&quot;Helen&quot;, &quot;创新文化公司CEO&quot;)
administrator = Administrator(&quot;Nina&quot;, &quot;行政中心总监&quot;)
directLeader.setNextHandler(departmentLeader)
departmentLeader.setNextHandler(ceo)
ceo.setNextHandler(administrator)
sunny = Person(&quot;Sunny&quot;)
sunny.setLeader(directLeader)
sunny.sendReuqest(Request(sunny.getName(), 1, &quot;参加MDCC大会。&quot;))
tony = Person(&quot;Tony&quot;)
tony.setLeader(directLeader)
tony.sendReuqest(Request(tony.getName(), 5, &quot;家里有紧急事情!&quot;))
pony = Person(&quot;Pony&quot;)
pony.setLeader(directLeader)
pony.sendReuqest(Request(pony.getName(), 15, &quot;出国深造。&quot;))
</code></pre>
<p>自己跑一下,会发现输出结果和之前的是一样的。</p>
<h4>模型说明</h4>
<ul>
<li>设计要点</li>
</ul>
<p>在设计职责模式的程序时要注意以下几点:</p>
<ol>
<li><strong>请求者与请求内容</strong>:谁要发送请求?发送请求的对象称为请求者。请求的内容通过发送请求时的参数进行传递。</li>
<li><strong>有哪些责任人</strong>:责任人是构成责任链的关键要素。请求的流动方向是链条中的线,而责任人则是链条上的结点,线和结点才构成了一条链条。</li>
<li><strong>对责任人进行抽象</strong>:真实世界中的责任人会多种多样,纷繁复杂,会有不同的职责和功能;但他们也有一个共同的特征——都可以处理请求。所以需要对责任人进行抽象,使他们具有责任的可传递性。</li>
<li><strong>责任人可自由组合</strong>:责任链上的责任人可以根据业务的具体逻辑进行自由的组合和排序。</li>
</ol>
<h4>优缺点</h4>
<ul>
<li>优点:</li>
</ul>
<ol>
<li>降低耦合度。它将请求的发送者和接受者解耦。</li>
<li>简化了对象。使得对象不需要知道链的结构。</li>
<li>增强给对象指派职责的灵活性。通过改变链内的成员或者调动它们的次序,允许动态地新增或者删除责任人。</li>
<li>增加新的处理类很方便。</li>
</ol>
<ul>
<li>职责模式的缺点:</li>
</ul>
<ol>
<li>不能保证请求一定被接收。</li>
<li>系统性能将受到一定影响,而且在进行代码调试时不太方便,可能会造成循环调用。</li>
</ol>
<h3>应用场景</h3>
<ol>
<li>有多个对象可以处理同一个请求,具体哪个对象处理该请求由运行时刻自动确定。</li>
<li>请求的处理具有明显的一层层传递关系。</li>
<li>请求的处理流程和顺序需要程序运行时动态确定。</li>
</ol>
</div>
</div>
<div>
<div style="float: left">
<a href="/专栏/白话设计模式 28 讲(完)/04 单例模式:你是我生命的唯一.md.html">上一页</a>
</div>
<div style="float: right">
<a href="/专栏/白话设计模式 28 讲(完)/06 中介模式:找房子问中介.md.html">下一页</a>
</div>
</div>
</div>
</div>
</div>
</div>
<a class="off-canvas-overlay" onclick="hide_canvas()"></a>
</div>
<script defer src="https://static.cloudflareinsights.com/beacon.min.js/v652eace1692a40cfa3763df669d7439c1639079717194" integrity="sha512-Gi7xpJR8tSkrpF7aordPZQlW2DLtzUlZcumS8dMQjwDHEnw9I7ZLyiOj/6tZStRBGtGgN6ceN6cMH8z7etPGlw==" data-cf-beacon='{"rayId":"70997b84aeaa3cfa","version":"2021.12.0","r":1,"token":"1f5d475227ce4f0089a7cff1ab17c0f5","si":100}' crossorigin="anonymous"></script>
</body>
<!-- Global site tag (gtag.js) - Google Analytics -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-NPSEEVD756"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag() {
dataLayer.push(arguments);
}
gtag('js', new Date());
gtag('config', 'G-NPSEEVD756');
var path = window.location.pathname
var cookie = getCookie("lastPath");
console.log(path)
if (path.replace("/", "") === "") {
if (cookie.replace("/", "") !== "") {
console.log(cookie)
document.getElementById("tip").innerHTML = "<a href='" + cookie + "'>跳转到上次进度</a>"
}
} else {
setCookie("lastPath", path)
}
function setCookie(cname, cvalue) {
var d = new Date();
d.setTime(d.getTime() + (180 * 24 * 60 * 60 * 1000));
var expires = "expires=" + d.toGMTString();
document.cookie = cname + "=" + cvalue + "; " + expires + ";path = /";
}
function getCookie(cname) {
var name = cname + "=";
var ca = document.cookie.split(';');
for (var i = 0; i < ca.length; i++) {
var c = ca[i].trim();
if (c.indexOf(name) === 0) return c.substring(name.length, c.length);
}
return "";
}
</script>
</html>