1.什么叫做設(shè)計(jì)模式(基本概念)
- 在面向?qū)ο筌浖O(shè)計(jì)過程中瑞躺,針對問題進(jìn)行簡潔而優(yōu)雅的一種解決方案
- 設(shè)計(jì)模式是在某種場合下對某個(gè)問題的一種解決方案
- 設(shè)計(jì)模式是在1995年初步形成训桶,共有23種模式
①:創(chuàng)建型模式:單例模式头朱,抽象工廠模式,建造者模式怀读,工廠者模式涕侈,原型模式
②:結(jié)構(gòu)型模式:適配器模式鲫尊,橋接模式,裝飾模式竞滓,組合模式咐吼,外觀模式,亨元模式商佑,代理模式锯茄,
③:行為型模式:模板方法模式,命令模式莉御,迭代器模式撇吞,觀察者模式俗冻,中介者模式,備忘錄模式牍颈,解釋器模式迄薄,狀態(tài)模式,策略模式煮岁,職責(zé)鏈模式讥蔽,訪問者模式。
2.設(shè)計(jì)模式能干什么
- 設(shè)計(jì)模式最大的功績就是:把已經(jīng)存在好的設(shè)計(jì)提煉出來画机,并取了一個(gè)號(hào)的名字(GoF)
設(shè)計(jì)模式-創(chuàng)建型模式(之工廠者模式)
案列-四則運(yùn)算
- 基本版本↓
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<style>
input[type="text"]{
width: 100px;
}
</style>
</head>
<body>
<div style="width:500px;height:80px;margin:50px auto;border:2px solid #ddd;">
<input type="text" id="num1">
<select name="" id="operator">
<option value="+">+</option>
<option value="-">-</option>
<option value="*">*</option>
<option value="/">/</option>
</select>
<input type="text" id="num2">
<input type="button" value='計(jì)算' id='btn'>
<input type="text" id="result">
</div>
<script>
function $(id){
return document.getElementById(id);
}
$('btn').onclick=function(){
//收集信息
var num1 = $("num1").value;
var num2 = $("num2").value;
//正則驗(yàn)證
var reg = /^[1-9]\d*$/;//正整數(shù)
if(reg.test(num1) && reg.test(num2)){
num1 = parseFloat(num1);
num2 = parseFloat(num2);
var op = $("operator").value;
var result;
//計(jì)算
if("+" == op){
result = num1 + num2;
}
else if("-" == op){
result = num1 - num2;
}
else if("*" == op){
result = num1 * num2;
}
else if("/" == op){
result = num1 / num2;
}
//顯示結(jié)果
$("result").value = result;
console.info(num1,num2,op);
}
else{
alert("請輸入正整數(shù)")
}
}
</script>
</body>
</html>
分析:在給一個(gè)運(yùn)算符添加新功能時(shí)冶伞,會(huì)有一個(gè)安全隱患:全部的代碼暴露在當(dāng)前的程序員面前,同時(shí)步氏,相當(dāng)與是在修改源代碼的方法去拓展一個(gè)新功能响禽,但是這不符合“對修改關(guān)閉原則”
- 使用面向?qū)ο蟮陌姹尽?/li>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<style>
input[type="text"]{
width: 100px;
}
</style>
</head>
<body>
<div style="width:500px;height:80px;margin:50px auto;border:2px solid #ddd;">
<input type="text" id="num1">
<select name="" id="operator">
<option value="+">+</option>
<option value="-">-</option>
<option value="*">*</option>
<option value="/">/</option>
<option value="%">%</option>
</select>
<input type="text" id="num2">
<input type="button" value='計(jì)算' id='btn'>
<input type="text" id="result">
</div>
<script>
function Add(num1,num2){
this.num1 = num1;
this.num2 = num2;
}
Add.prototype.getResult = function(){
return this.num1 + this.num2;
}
function Mul(num1,num2){
this.num1 = num1;
this.num2 = num2;
}
Mul.prototype.getResult = function(){
return this.num1 * this.num2;
}
function Div(num1,num2){
this.num1 = num1;
this.num2 = num2;
}
Div.prototype.getResult = function(){
if(this.num2 == 1){
return this.num1;
}
return this.num1 / this.num2;
}
function Sub(num1,num2){
this.num1 = num1;
this.num2 = num2;
}
Sub.prototype.getResult = function(){
return this.num1 - this.num2;
}
function Mod(num1,num2){
this.num1 = num1;
this.num2 = num2;
}
Mod.prototype.getResult = function(){
return this.num1 % this.num2;
}
function $(id){
return document.getElementById(id);
}
/**
* 功能:計(jì)算器
* @param {[type]} op [操作符]
* @param {[type]} num1 [第一個(gè)操作數(shù)]
* @param {[type]} num2 [第二個(gè)操作數(shù)]
* @return {[type]} [返回結(jié)果]
*/
function computer(op,num1,num2){
var c;
if("+" == op){
c = new Add(num1,num2);
}
else if("-" == op){
c = new Sub(num1,num2);
}
else if("*" == op){
c = new Mul(num1,num2);
}
else if("/" == op){
c = new Div(num1,num2);
}
else if("%" == op)
c = new Mod(num1,num2);
return c.getResult();
}
/**
* 正則驗(yàn)證
* @param {[type]} type [類型]
* @param {[type]} str [要驗(yàn)證的字符串]
* @return {[type]} [驗(yàn)證結(jié)果]
*/
function reg(type,str){
if( "number" == type){
var reg = /^[1-9]\d*$/;//正整數(shù)
return reg.test(str);
}
else if("email" == type ){
var reg = /^\w+@\w+(\.[a-z]{2,3})+$/i;
return reg.test(str);
}
}
$('btn').onclick=function(){
//收集信息
var num1 = $("num1").value;
var num2 = $("num2").value;
//正則驗(yàn)證
if(reg("number",num1) && reg("number",num2)){
num1 = parseFloat(num1);
num2 = parseFloat(num2);
var op = $("operator").value;
//計(jì)算
var result = computer(op,num1,num2);
//顯示結(jié)果
$("result").value = result;
console.info(num1,num2,op);
}
else{
alert("請輸入正整數(shù)")
}
}
</script>
</body>
</html>
分析:
- ①四個(gè)單獨(dú)的運(yùn)算器對象都可以在其他地方重復(fù)使用。
②在對新功能進(jìn)行拓展的時(shí)候荚醒,不需要去暴露 其他功能的具體實(shí)現(xiàn)細(xì)節(jié)
③一個(gè)構(gòu)造器就是一個(gè)單獨(dú)的文件芋类,它不會(huì)影響其他功能的構(gòu)造器
總結(jié):編寫一個(gè)工廠函數(shù),根據(jù)輸入界阁,返回特定的對象
設(shè)計(jì)模式-創(chuàng)建型模式(之單例模式)
- js字面量對象就是單例模式
需求:讓一個(gè)構(gòu)造器只產(chǎn)生一個(gè)對象
案列-按鈕的單次點(diǎn)擊(只能進(jìn)行一次點(diǎn)擊)
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<style>
</style>
</head>
<body>
<input type="button" value="anniu" id="btn">
<script>
function $(id){
return document.getElementById(id);
}
//arguments
function f(){
console.info("我是事件響應(yīng)函數(shù)");
}
function one(func){
var isclicked;
function f1(){
if(!isclicked){
func();
isclicked = true;
}
}
return f1;
}
$('btn').onclick=one(f);
</script>
</body>
</html>
總結(jié):構(gòu)造器的返回值侯繁,以及閉包的使用是對單例模型實(shí)現(xiàn)的一個(gè)完美體現(xiàn)
設(shè)計(jì)模式-行為模式(策略模式)
定義:把一系列的算法,封裝起來泡躯,并且可以使它們可以相互替換
需求:給定一個(gè)值贮竟,進(jìn)行求值
案列:計(jì)算公司員工的年終獎(jiǎng)
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<script>
// 公司的年終獎(jiǎng)是根據(jù)工資基數(shù)和績效來發(fā)放的。例如績效為S的員工有4倍工資较剃。A的員工有3倍工資咕别。B的員工有2倍工資。
// 請你給出代碼
var salary = {
S:function(basic){
return 4 * basic;
},
A:function(basic){
return 3 * basic;
},
B:function(basic){
return 2 * basic;
},
C:function(basic){
return 1.5 * basic;
}
};
function getSalary(level,basic){
//var f1 = salary[level];
//return f1(basic);
return salary[level](basic);
}
console.info ( getSalary("S",10000) );
console.info ( getSalary("B",8000) );
console.info ( getSalary("C",6000) );
// console.info( salary("S",10000) );
// console.info( salary("C",6000) );
//問題:加一個(gè)等級(jí)C 1.5系數(shù)
</script>
</body>
</html>
總結(jié):①保證了對修改的關(guān)閉重付,同時(shí)對拓展開放
②保證了代碼之間的相互不影響
設(shè)計(jì)模式-行為模式(策略模式)
定義:觀察者模式又叫發(fā)布訂閱模式(Publish/Subscribe)顷级,它定義了一種一對多的關(guān)系,讓多個(gè)觀察者對象同時(shí)監(jiān)聽某一個(gè)主題對象确垫,這個(gè)主題對象的狀態(tài)發(fā)生變化時(shí)就會(huì)通知所有的觀察者對象弓颈,使得它們能夠自動(dòng)更新自己。
- 實(shí)現(xiàn)原理
觀察者1删掀,觀察者2......
中心點(diǎn)
添加對象
刪除觀察者
發(fā)布更新指令(通知所有的觀察者)
容器:保存觀察者的數(shù)據(jù) - 理論事例
案列
<html lang="en">
<head>
<meta charset="UTF-8">
<title>觀察者模式</title>
</head>
<body>
<h1 id='header'>消息列表1條</h1>
<ul id="list">
<li>
消息內(nèi)容<br><br>
<button value='刪除' id='del'>刪除</button>
</li>
</ul>
<input type="text" id='news'>
<button value='點(diǎn)擊添加消息' id='btn'>點(diǎn)擊添加消息</button>
</body>
<script>
function $(id){
return document.getElementById(id);
};
var counts=1;
$('del').onclick=function(){
counts--;
this.parentNode.parentNode.removeChild(this.parentNode);
$('header').innerHTML="消息列表"+counts+"條"
}
$('btn').onclick=function(){
var news=document.createElement('li'),
del=document.createElement('button');
news.innerHTML=$('news').value+'<br><br>';
del.innerHTML="刪除";
del.onclick=function(){
counts--;
this.parentNode.parentNode.removeChild(this.parentNode);
$('header').innerHTML="消息列表"+counts+"條"
}
news.appendChild(del);
$('list').appendChild(news);
counts++;
$('header').innerHTML="消息列表"+counts+"條";
}
</script>
</html>
- 實(shí)踐操作
<html>
<head>
<meta charset="utf-8" />
<title></title>
<style>
.container{
border:3px solid #ccc;
padding:0 20px;
margin:30px auto;
width: 600px;
}
a{
text-decoration: none;
}
ul{list-style: none;}
</style>
</head>
<body>
<div class="container">
<h1>消息列表<span id="num">0</span>條</h1>
<ul id="ulmsg">
<!-- <li><p>消息內(nèi)容</p><button>刪除</button></li> -->
</ul>
<form action="">
<input type="text" id="words">
<input type="button" value="添加" id="btnsubmit">
</form>
</div>
<script type="text/javascript">
function $(id){
return document.getElementById(id);
}
function create(flag){
return document.createElement(flag)
}
//實(shí)現(xiàn)觀察者
function NumberManager(data){
$("num").innerHTML = $("num").innerHTML*1 + data.num;
}
function MessageManager(data){
//<li><p>消息內(nèi)容</p><button>刪除</button></li>
var li = create("li");
var p = create("p");
var button = create("button");
button.innerHTML="刪除";
button.onclick = function(){
$("ulmsg").removeChild(li);
center.fire("delMsg",{num:-1});
}
p.innerHTML = data.txt;
li.appendChild(p);
li.appendChild(button);
$("ulmsg").appendChild(li);
}
function Somthingelse(data){
console.info(data);
}
var center = {
viewers:{},
addViewer:function(eventtype,callback){
if(this.viewers.hasOwnProperty(eventtype)){
this.viewers[eventtype].push(callback);
}
else{
this.viewers[eventtype] = [callback];
}
},
delViewer:function(){
},
fire:function(eventtype,data){ //有變化 通知所有的觀察者
if(this.viewers.hasOwnProperty(eventtype)){
for (var i = 0; i < this.viewers[eventtype].length; i++) {
this.viewers[eventtype][i].call(this,data);
}
}
}
}
center.addViewer("addMsg",Somthingelse); //添加訂閱者
center.addViewer("addMsg",NumberManager);
center.addViewer("addMsg",MessageManager);
center.addViewer("delMsg",NumberManager);
$("btnsubmit").onclick = function(){
var data={
txt:$("words").value,
num:1
};
center.fire("addMsg",data);
}
</script>
</body>
</html>
設(shè)計(jì)模式-結(jié)構(gòu)型模式(裝飾者模式)
- 常見的需求
給對象動(dòng)態(tài)地添加職責(zé)翔冀。在不改變對象自身的基礎(chǔ)上,在程序運(yùn)行期間給對象添加功能披泪。比繼承更靈活纤子。 - 闡述思路
在不影響之前的代碼下,給其添加新功能 - 問題的解決
<html>
<head>
<meta charset="utf-8" />
<title></title>
<script src="jquery-2.1.4.js"></script>
</head>
<body>
<script type="text/javascript">
function ready(f){
debugger;
//判斷一下window.onload是否存在 。如果存在 控硼,則先調(diào)用它泽论,再調(diào)用f函數(shù)
var t = window.onload;
if(typeof t =="function"){
window.onload = function(){
t();
f();
}
}
else{
window.onload = function(){
f();
}
}
}
ready(function(){console.info(2)});
ready(function(){console.info(3)});
ready(function(){console.info(1)});
</script>
</body>
</html>
3.設(shè)計(jì)模式的利與弊
4.關(guān)于自學(xué)設(shè)計(jì)模式的淺略總結(jié)(坑)
設(shè)計(jì)模式“可復(fù)用面向?qū)ο筌浖幕A(chǔ)”。
設(shè)計(jì)模式完全是從面向?qū)ο笤O(shè)計(jì)的角度出發(fā)的卡乾,通過對封裝翼悴、繼承、多態(tài)幔妨、組合等技術(shù)的反復(fù)使用鹦赎,提煉出一些可重復(fù)使用的面向?qū)ο笤O(shè)計(jì)技巧。所以有一種說法是設(shè)計(jì)模式僅僅是就面向?qū)ο蟮恼Z言而言的误堡。